Skip to content

Custom Scripts

Jeffrey Berry edited this page Nov 5, 2019 · 24 revisions

Custom Scripts

It is possible to create your own workflow using the functions that come with PhenotyperCV if you are familiar with C++. The functions that make up PhenotyperCV exist in header files so all you have to do is add a single include #include <phenotypercv.h> to your source file and link the files when compiling. For instance if you'd like to combine parts of the ChArUco workflow with parts of the machine learning workflow, you can do that in a custom workflow. Full disclosure, I do not have documentation for the individual functions (yet). You will need to look at the header files and determine the proper parameters. An example of a custom source file, cmake file, and commands to link all the headers is here.

main.cpp

#include <phenotypercv.h>

namespace {
const char* keys =
    "{i||Input image}"
    "{s||Shapes file}"
    "{l||Leaves file}"
    "{debug||debug}";
}

int main(int argc, char *argv[]){
    CommandLineParser parser(argc, argv, keys);

    //---------- EXAMPLE OF CUSTOM WORKFLOW ------------
    Mat inputImage = imread(parser.get<string>("i"));
    
    //-- Convert to Lab colorspace
    Mat lab;
    cvtColor(inputImage, lab, cv::COLOR_BGR2Lab);
    vector<Mat> split_lab;
    split(lab, split_lab);
    
    //-- Threshold b-channel    
    Mat b_thresh1;
    inRange(split_lab[2],133,255,b_thresh1);
    
    //-- Threshold l-channel
    Mat l_thresh1;
    inRange(split_lab[0],52,128,l_thresh1);

    //-- Logical AND
    Mat mask1 = b_thresh1 & l_thresh1;

    //-- Erode then dilate by 1
    Mat mask1_er;
    erode(mask1,mask1_er, Mat(), Point(-1, -1), 1, 1, 1);
    Mat mask1_dil;
    dilate(mask1_er,mask1_dil, Mat(), Point(-1, -1), 1, 1, 1);
    
    //-- ROI inclusion
    Mat mask1_roi;
    vector<Point> cc_mask1 = keep_roi(mask1_dil,Point(1908,1712),Point(3228,2600),mask1_roi);
        
    //-- Getting and writing shapes data
    vector<double> shapes_data = get_shapes(cc_mask1,mask1_roi);
    write_shapes(shapes_data,parser.get<string>("i"),parser.get<string>("s"));

    //-- If desired, write out mask
    if(parser.has("debug")){
    	vector<string> sub_str;
	const string full_str = string(parser.get<string>("i"));
	char del = '.';
	split(full_str,del,sub_str);
	string new_name = sub_str[0]+"_mask.png";
	imwrite(new_name,mask1_roi);
    }
	
    //-- If desired, segmenting leaves from stem
    if(parser.has("l")){
        //-- Dilate mask
        Mat dil;
	dilate(mask1_roi, dil, Mat(), Point(-1, -1), 1, 1, 1);

        //-- Thinning
	Mat skel;
	ximgproc::thinning(dil,skel,THINNING_ZHANGSUEN);

        //-- Length filter #1 
	Mat skel_filt0 = length_filter(skel,50);

        //-- Prune skeleton 
        Mat pruned = prune(skel_filt0,5);

        //-- Break skeleton into pieces
	Mat seg_skel = segment_skeleton(pruned);

        //-- Find leaf tips
	Mat tips = find_endpoints(pruned);

        //-- Remove bad tips
	Mat no_tips = Mat::zeros(inputImage.size(),pruned.type());
	rectangle(no_tips,Point(2478,2568),Point(2591,2654),255,cv::FILLED);
	tips = tips -(tips & no_tips);

        //-- Length filter #2 
	Mat skel_filt1 = length_filter(seg_skel,12);

        //-- Identify leaf blades
	Mat leaves = find_leaves(skel_filt1,tips);

        //-- Anything not-leaf becomes stem
	Mat classified = add_stem(leaves,pruned);

        //-- Fill original mask with classification
	Mat filled_mask = fill_mask(dil,classified);
    
        //-- Getting and writing shapes data
	vector<vector<double> > leaf_data = get_leaf_info(classified,filled_mask);
	write_leaves(leaf_data,parser.get<string>("i"),parser.get<string>("l"));
    
        //-- If desired, write out filled mask
	if(parser.has("debug")){
            vector<string> sub_str;
    	    const string full_str = string(parser.get<string>("i"));
    	    char del = '.';
    	    split(full_str,del,sub_str);
    	    string new_name = sub_str[0]+"_filled.png";
    	    imwrite(new_name,filled_mask);
	}
    }

    //----------------- END EXAMPLE --------------------

    return(0);
}

CMakeLists.txt

# Should only have to modify these three lines
#----------------------------------------------------------------------------
set(Project_name your_project)
set(PhenotyperCV_dir "/path/to/ddpsc_phenotypercv")
set(your_file "main.cpp")
#----------------------------------------------------------------------------

project (${Project_name})
cmake_minimum_required (VERSION 2.6)
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_FLAGS -Wno-narrowing)
find_package(OpenCV REQUIRED)
find_package (Eigen3 REQUIRED NO_MODULE)
find_library(ZBAR_LIBRARIES NAMES zbar)
find_path(ZBAR_INCLUDE_DIR Decoder.h PATH_SUFFIXES zbar)
include_directories(/usr/local/include)
include_directories(${OpenCV_INCLUDE_DIRS})
include_directories(${PhenotyperCV_dir}/include)
add_executable(${Project_name} ${your_file})
target_link_libraries(${Project_name} ${OpenCV_LIBS} ${ZBAR_LIBRARIES})

Then in the directory of your source and cmake files

mkdir build && cd build
cmake ..
make

To see all the different functions that are already made, please see the header files for a full list of what each header file contains. Again full documentation is coming but for now you just have to look.