Detecting and Recognizing Leaf Diseases Using OpenCV

You can help me with this.

I was tasked with building an application using OpenCV and C ++ that will accept plant leaf input. This application would detect possible disease symptoms such as black / gray / brown leaf spots, or spots, damage, etc. Each characteristic of a disease, such as the color of the spots, represents a different disease. After detecting possible symptoms, the application will match this set of pattern images from the application database and display the best possible match.

What methods should I use for this? I've researched histogram mapping and cue point and handle mapping, but I'm not sure which one would work best.

I found a sample code using SURF and FLANN, but I don't know if this is enough:

#include <stdio.h>
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/nonfree/features2d.hpp"

using namespace cv;

void readme();

/**
* @function main
* @brief Main function
*/
int main( int argc, char** argv )
{
if( argc != 3 )
{  readme(); return -1; }

Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE );
Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE );

if( !img_1.data || !img_2.data )
{ std::cout<< " --(!) Error reading images " << std::endl; return -1; }

//-- Step 1: Detect the keypoints using SURF Detector
int minHessian = 400;

SurfFeatureDetector detector( minHessian );

std::vector<KeyPoint> keypoints_1, keypoints_2;

detector.detect( img_1, keypoints_1 );
detector.detect( img_2, keypoints_2 );

//-- Step 2: Calculate descriptors (feature vectors)
SurfDescriptorExtractor extractor;

Mat descriptors_1, descriptors_2;

extractor.compute( img_1, keypoints_1, descriptors_1 );
extractor.compute( img_2, keypoints_2, descriptors_2 );

//-- Step 3: Matching descriptor vectors using FLANN matcher
 FlannBasedMatcher matcher;
 std::vector< DMatch > matches;
 matcher.match( descriptors_1, descriptors_2, matches );

 double max_dist = 0; double min_dist = 100;

 //-- Quick calculation of max and min distances between keypoints
for( int i = 0; i < descriptors_1.rows; i++ )
{ double dist = matches[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
 }

printf("-- Max dist : %f \n", max_dist );
printf("-- Min dist : %f \n", min_dist );

//-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist,
//-- or a small arbitary value ( 0.02 ) in the event that min_dist is very
//-- small)
//-- PS.- radiusMatch can also be used here.
std::vector< DMatch > good_matches;

for( int i = 0; i < descriptors_1.rows; i++ )
{ if( matches[i].distance <= max(2*min_dist, 0.02) )
{ good_matches.push_back( matches[i]); }
}

//-- Draw only "good" matches
Mat img_matches;
drawMatches( img_1, keypoints_1, img_2, keypoints_2,
           good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
           vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );

//-- Show detected matches
imshow( "Good Matches", img_matches );

for( int i = 0; i < (int)good_matches.size(); i++ )
{ printf( "-- Good Match [%d] Keypoint 1: %d  -- Keypoint 2: %d  \n", i,           good_matches[i].queryIdx, good_matches[i].trainIdx ); }

 waitKey(0);

return 0;
}

  /**
  * @function readme
 */
    void readme()
 { std::cout << " Usage: ./SURF_FlannMatcher <img1> <img2>" << std::endl; }

      

Here are my questions:

  • Which method should I use? Histogram match, cue point / handle match, or?

  • If I use Keypoint / Descriptor matching, which algorithm is the best alternative to SURF and FLANN, since I will be using it on Android platform as well? Should I do thresholds or segmentation? Could you remove important details like color, shape, etc.? Please guys suggest some steps for this.

+3


source to share


1 answer


I think this path should give you good results:

Studying proccess.

  • Extract LBP descriptors for the outer pixel of the image (can be computed for color images too).
  • Compute LBP descriptor histograms for each training sample.
  • Classify trains using histograms as inputs and labels as outputs.

Forecasting process:

  • Retrieve the LBP descriptors for the outer pixel of the new image.
  • Compute the histogram of LBP descriptors for this image.
  • Feed historgam for classifier -> get results.


I have successfully used a Flash Forward Neural Network as a classifier to solve a similar problem.

You may find this book helpful: ISBN 978-0-85729-747-1 "Computer Vision Using Local Binary Patterns"

Try this (computes LBP descriptors, there is also a function to compute histogram):

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include "opencv2/nonfree/nonfree.hpp"
#include <limits>
using namespace cv;

class myLBP
{
public:
    uchar lut[256];
    uchar null;
    int radius;
    int maxTransitions;
    bool rotationInvariant;

    myLBP(int _radius=1,int _maxTransitions=8,bool _rotationInvariant=false)
    {
        radius=_radius;
        maxTransitions=_maxTransitions;
        rotationInvariant=_rotationInvariant;

        bool set[256];
        uchar uid = 0;
        for (int i=0; i<256; i++)
        {
            if (numTransitions(i) <= maxTransitions)
            {
                int id;
                if (rotationInvariant)
                {
                    int rie = rotationInvariantEquivalent(i);
                    if (i == rie)
                    {
                        id = uid++;
                    }
                    else
                    {
                        id = lut[rie];
                    }
                }
                else
                {
                    id = uid++;
                }
                lut[i] = id;
                set[i] = true;
            }
            else
            {
                set[i] = false;
            }
        }
        null = uid;
        for (int i=0; i<256; i++)
            if (!set[i])
            {
                lut[i] = null;    // Set to null id
            }
    }


    /* Returns the number of 0->1 or 1->0 transitions in i */
    static int numTransitions(int i)
    {
        int transitions = 0;
        int curParity = i%2;
        for (int j=1; j<=8; j++)
        {
            int parity = (i>>(j%8)) % 2;
            if (parity != curParity)
            {
                transitions++;
            }
            curParity = parity;
        }
        return transitions;
    }

    static int rotationInvariantEquivalent(int i)
    {
        int min = std::numeric_limits<int>::max();
        for (int j=0; j<8; j++)
        {
            bool parity = i % 2;
            i = i >> 1;
            if (parity)
            {
                i+=128;
            }
            min = std::min(min, i);
        }
        return min;
    }

    void process(const Mat &src, Mat &dst) const
    {
        Mat m;
        src.convertTo(m, CV_32F);
        assert(m.isContinuous() && (m.channels() == 1));
        Mat n(m.rows, m.cols, CV_8UC1);
        n = null; // Initialize to NULL LBP pattern
        const float *p = (const float*)m.ptr();
        for (int r=radius; r<m.rows-radius; r++)
        {
            for (int c=radius; c<m.cols-radius; c++)
            {
                const float cval  =     (p[(r+0*radius)*m.cols+c+0*radius]);
                n.at<uchar>(r, c) = lut[(p[(r-1*radius)*m.cols+c-1*radius] >= cval ? 128 : 0) |
                    (p[(r-1*radius)*m.cols+c+0*radius] >= cval ? 64  : 0) |
                    (p[(r-1*radius)*m.cols+c+1*radius] >= cval ? 32  : 0) |
                    (p[(r+0*radius)*m.cols+c+1*radius] >= cval ? 16  : 0) |
                    (p[(r+1*radius)*m.cols+c+1*radius] >= cval ? 8   : 0) |
                    (p[(r+1*radius)*m.cols+c+0*radius] >= cval ? 4   : 0) |
                    (p[(r+1*radius)*m.cols+c-1*radius] >= cval ? 2   : 0) |
                    (p[(r+0*radius)*m.cols+c-1*radius] >= cval ? 1   : 0)];
            }
        }
        dst=n.clone();
    }

    /* Returns the number of 1 bits in i */
    static int bitCount(int i)
    {
        int count = 0;
        for (int j=0; j<8; j++)
        {
            count += (i>>j)%2;
        }
        return count;
    } 

    void draw(const Mat &src, Mat &dst) const
    {
        static Mat hueLUT, saturationLUT, valueLUT;
        if (!hueLUT.data)
        {
            const int NUM_COLORS = 10;
            hueLUT.create(1, 256, CV_8UC1);
            hueLUT.setTo(0);
            uchar uid = 0;
            for (int i=0; i<256; i++)
            {
                const int transitions = numTransitions(i);
                int u2;
                if   (transitions <= 2)
                {
                    u2 = uid++;
                }
                else
                {
                    u2 = 58;
                }
                // Assign hue based on bit count
                int color = bitCount(i);
                if (transitions > 2)
                {
                    color = NUM_COLORS-1;
                }
                hueLUT.at<uchar>(0, u2) = 255.0*(float)color/(float)NUM_COLORS;
            }
            saturationLUT.create(1, 256, CV_8UC1);
            saturationLUT.setTo(255);
            valueLUT.create(1, 256, CV_8UC1);
            valueLUT.setTo(255.0*(3.0/4.0));
        }
        if (src.type() != CV_8UC1)
        {
            std::cout << "Expected 8UC1 source type.";

        }
        Mat hue, saturation, value;
        LUT(src, hueLUT, hue);
        LUT(src, saturationLUT, saturation);
        LUT(src, valueLUT, value);
        std::vector<Mat> mv;
        mv.push_back(hue);
        mv.push_back(saturation);
        mv.push_back(value);
        Mat coloredU2;
        merge(mv, coloredU2);
        cvtColor(coloredU2, dst, cv::COLOR_HSV2BGR);
    } 
};


void Hist(const Mat &src, Mat &dst,float max=256, float min=0,int dims=-1)
{
    std::vector<Mat> mv;
    split(src, mv);
    Mat m(mv.size(), dims, CV_32FC1);
    for (size_t i=0; i<mv.size(); i++)
    {
        int channels[] = {0};
        int histSize[] = {dims};
        float range[] = {min, max};
        const float* ranges[] = {range};
        Mat hist, chan = mv[i];
        // calcHist requires F or U, might as well convert just in case
        if (mv[i].depth() != CV_8U && mv[i].depth() != CV_32F)
        {
            mv[i].convertTo(chan, CV_32F);
        }
        calcHist(&chan, 1, channels, Mat(), hist, 1, histSize, ranges);
        memcpy(m.ptr(i), hist.ptr(), dims * sizeof(float));
    }
    dst=m.clone();
}



int main(int argc, char* argv[])
{
    cv::initModule_nonfree();
    cv::namedWindow("result");
    cv::Mat bgr_img = cv::imread("D:\\ImagesForTest\\lena.jpg");
    if (bgr_img.empty()) 
    {
        exit(EXIT_FAILURE);
    }
    cv::Mat gray_img;
    cv::cvtColor(bgr_img, gray_img, cv::COLOR_BGR2GRAY);
    cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);

    myLBP lbp(1,2);
    Mat lbp_img;

    lbp.process(gray_img,lbp_img);
    lbp.draw(lbp_img,bgr_img);

    //for(int i=0;i<lbp_img.rows;++i)

    imshow("result",bgr_img);
    cv::waitKey();
    return 0;
}

      

+1


source







All Articles