1

I have this image with 3 peppers:

I need to convert this picture to HSV form and then separate each pepper into its own image.

So far, I seem to be able to separate the red pepper nicely from the others. However, I cannot seem to be able to figure out how to separate the other peppers.

Here is my code:

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cmath>
#include <string>
#include <vector>

int main(int argc, char *argv[]){
    cv::Mat im_in;
    cv::Mat hsv_in;
    cv::Mat bgr_in;
    cv::Mat orig_in;
    cv::Mat im_o1;
    cv::Mat im_o2;
    cv::Mat im_o3;
    // Read image
    if (argc<2){
    im_in = cv::imread("colorpeppers.jpg");
    }
    if (argc==2){
        im_in=cv::imread((argv[1]));
    }
    if (argc>2){
        std::cout<<"Error! Too many arguments!"<<std::endl;
    }
    if (im_in.empty()){
        std::cout << "error detected. something went wrong with opening the image. is it empty? exiting"<<std::endl;
        return -1;
    }
    cv::Mat orig_image = im_in.clone();
    cv::medianBlur(im_in, im_in, 3);

    cv::cvtColor(im_in, hsv_in, cv::COLOR_BGR2HSV);
    cv::namedWindow("Original Image", cv::WINDOW_AUTOSIZE);
    cv::imshow("Orginal Image", im_in);

    cv::namedWindow("Orginal Image converted to HSV", cv::WINDOW_AUTOSIZE);
    cv::imshow("Original Image converted to HSV", hsv_in);
    cv::Mat lower_red_hue_range;
    cv::Mat upper_red_hue_range;
    cv::Mat lower_green_hue_range;
    cv::Mat upper_green_hue_range;
    cv::Mat lower_yellow_hue_range;
    cv::Mat upper_yellow_hue_range;


    cv::inRange(hsv_in, cv::Scalar(0,100,100), cv::Scalar(10,255,255), lower_red_hue_range);
    cv::inRange(hsv_in, cv::Scalar(160,100,100),cv::Scalar(179, 255, 255), upper_red_hue_range);
    cv::inRange(hsv_in, cv::Scalar(0,100,100), cv::Scalar(10,255,255), lower_green_hue_range);
    cv::inRange(hsv_in, cv::Scalar(50,100,100),cv::Scalar(70,255,255), upper_green_hue_range);
    cv::inRange(hsv_in, cv::Scalar(0,100,100),cv::Scalar(20,0,0),lower_yellow_hue_range);
    cv::inRange(hsv_in,cv::Scalar(10,100,100),cv::Scalar(50,110,110),upper_yellow_hue_range);

    //combining the above
    cv::Mat red_hue_image=im_in.clone();
    cv::Mat green_hue_image=im_in.clone();
    cv::Mat yellow_hue_image=im_in.clone();
    cv::addWeighted(lower_red_hue_range, 1.0, upper_red_hue_range, 1.0, 0.0, red_hue_image);
    cv::addWeighted(lower_green_hue_range, 1.0, upper_green_hue_range, 1.0, 0.0, green_hue_image);
    cv::addWeighted(lower_yellow_hue_range, 1.0, upper_yellow_hue_range, 1.0, 0.0, yellow_hue_image);
    cv::GaussianBlur(red_hue_image, red_hue_image, cv::Size(9,9), 2, 2);
    cv::GaussianBlur(green_hue_image, green_hue_image, cv::Size(9,9),2,2);
    cv::GaussianBlur(yellow_hue_image, yellow_hue_image, cv::Size(9,9),2,2);

    cv::namedWindow("Threshold lower red image", cv::WINDOW_AUTOSIZE);
    cv::imshow("Threshold lower red image", lower_red_hue_range);
    cv::namedWindow("Threshold upper red image", cv::WINDOW_AUTOSIZE);
    cv::imshow("Threshold upper red image", upper_red_hue_range);
    cv::namedWindow("Combined Threshold red Images", cv::WINDOW_AUTOSIZE);
    cv::imshow("Combined Threshold red Images", red_hue_image);

    cv::namedWindow("Threshold lower green image", cv::WINDOW_AUTOSIZE);
    cv::imshow("Threshold lower green image", lower_green_hue_range);
    cv::namedWindow("Threshold upper green image", cv::WINDOW_AUTOSIZE);
    cv::imshow("Threshold upper green image", upper_green_hue_range);
    cv::namedWindow("Combined Threshold green Images", cv::WINDOW_AUTOSIZE);
    cv::imshow("Combined Threshold green Images", green_hue_image);

    cv::namedWindow("Threshold lower yellow image", cv::WINDOW_AUTOSIZE);
    cv::imshow("Threshold lower yellow image", lower_yellow_hue_range);
    cv::namedWindow("Threshold upper yellow image", cv::WINDOW_AUTOSIZE);
    cv::imshow("Threshold upper yellow image", upper_yellow_hue_range);
    cv::namedWindow("Combined Threshold yellow Images", cv::WINDOW_AUTOSIZE);
    cv::imshow("Combined Threshold yellow Images", yellow_hue_image);



    //cv::Mat redpepper;//=red_hue_image.clone();
    //cv::Mat redpepper_hsv;
    //cvtColor(redpepper, redpepper_hsv,CV_BGR2HSV);
//    for(int i = 0; i < redpepper.rows;i++){
  //      for(int j = 0; j <redpepper.cols;j++){




    char k;
    for (int x=1;x<15;x++){
        k=cvWaitKey(0);
    }

}
Dan Mašek
  • 17,852
  • 6
  • 57
  • 85
cmcninja
  • 11
  • 2

1 Answers1

5

As first step, let's reduce the number of colours in the image using k-means clustering.

Next, let's convert the image to HSV space and split it into individual Hue, Saturation and Value components.

Hue:

Hue (colourmapped):

Saturation:

Value:

The hue component is sufficient for the rest of the algorithm. We create three masks by selecting pixels in ranges appropriate for the three colours.

Red:

Yellow:

Green:

We clean up the mask images using morphological transformations (erode, dilate). Next, we detect the contour and only select those with area greater than some threshold (I selected 5000 pixels). We generate new masks by filling the contours that satisfy the criteria.

Red:

Yellow:

Green:

Now we can, for example, draw the contours...

Red:

Yellow:

Green:

Code:

#include <opencv2/opencv.hpp>

#include <cstdint>
#include <iostream>
#include <vector>

cv::Mat cluster_image(cv::Mat const& img)
{
    int K = 4;
    int n = img.rows * img.cols;
    cv::Mat data = img.reshape(1, n);
    data.convertTo(data, CV_32F);

    std::vector<int> labels;
    cv::Mat1f colors;
    cv::kmeans(data, K, labels
        , cv::TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10000, 0.0001)
        , 5, cv::KMEANS_PP_CENTERS, colors);

    for (int i = 0; i < n; ++i) {
        data.at<float>(i, 0) = colors(labels[i], 0);
        data.at<float>(i, 1) = colors(labels[i], 1);
        data.at<float>(i, 2) = colors(labels[i], 2);
    }

    cv::Mat reduced = data.reshape(3, img.rows);
    reduced.convertTo(reduced, CV_8U);

    return reduced;
}

cv::Mat filter_mask(cv::Mat& img, cv::Mat& mask)
{
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5));
    cv::Mat filtered;
    cv::erode(mask, filtered, kernel, cv::Point(-1, -1), 2);
    cv::dilate(filtered, filtered, kernel, cv::Point(-1, -1), 2);

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;

    cv::findContours(filtered, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    cv::Mat output_mask = cv::Mat::zeros(mask.size(), CV_8UC1);

    double const MIN_CONTOUR_AREA(5000.0);
    for (int i(0); i < contours.size(); ++i) {
        double area = cv::contourArea(contours[i]);

        if (area >= MIN_CONTOUR_AREA) {
            cv::drawContours(output_mask, contours, i, cv::Scalar(255), CV_FILLED);
        }
    }

    cv::dilate(output_mask, output_mask, kernel, cv::Point(-1, -1), 1);

    return output_mask;
}

void highlight_mask(std::string const& filename, cv::Mat& img, cv::Mat& mask)
{
    cv::Mat output = img.clone();

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(mask, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    cv::drawContours(output, contours, 0, cv::Scalar(255, 0, 0), 2);

    cv::imwrite(filename, output);
}


int main(int argc, char *argv[])
{
    cv::Mat orig_image(cv::imread("d:\\code\\shit\\so03\\bin\\runtime\\Debug\\peppers.png"));
    if (orig_image.empty()) {
        std::cerr << "Input image empty." << std::endl;
        return -1;
    }

    cv::Mat im_in;
    cv::medianBlur(orig_image, im_in, 3);

    cv::Mat clustered = cluster_image(im_in);
    cv::imwrite("peppers_clustered.png", clustered);

    cv::Mat hsv_in;
    cv::cvtColor(clustered, hsv_in, cv::COLOR_BGR2HSV);

    uint32_t HUE(0), SAT(1), VAL(2);
    std::vector<cv::Mat> h_s_v(3);
    cv::split(hsv_in, h_s_v);
    cv::imwrite("peppers_hue.png", h_s_v[HUE]);
    cv::imwrite("peppers_sat.png", h_s_v[SAT]);
    cv::imwrite("peppers_val.png", h_s_v[VAL]);



    cv::Mat red_mask_a;
    cv::inRange(h_s_v[HUE], cv::Scalar(2), cv::Scalar(10), red_mask_a);
    cv::Mat red_mask_b = filter_mask(im_in, red_mask_a);
    cv::imwrite("peppers_red_mask_a.png", red_mask_a);
    cv::imwrite("peppers_red_mask_b.png", red_mask_b);

    cv::Mat yellow_mask_a;
    cv::inRange(h_s_v[HUE], cv::Scalar(15), cv::Scalar(25), yellow_mask_a);
    cv::Mat yellow_mask_b = filter_mask(im_in, yellow_mask_a);
    cv::imwrite("peppers_yellow_mask_a.png", yellow_mask_a);
    cv::imwrite("peppers_yellow_mask_b.png", yellow_mask_b);

    cv::Mat green_mask_a;
    cv::inRange(h_s_v[HUE], cv::Scalar(40), cv::Scalar(50), green_mask_a);
    cv::Mat green_mask_b = filter_mask(im_in, green_mask_a);
    cv::imwrite("peppers_green_mask_a.png", green_mask_a);
    cv::imwrite("peppers_green_mask_b.png", green_mask_b);

    highlight_mask("peppers_red_out.png", orig_image, red_mask_b);
    highlight_mask("peppers_yellow_out.png", orig_image, yellow_mask_b);
    highlight_mask("peppers_green_out.png", orig_image, green_mask_b);
}
Community
  • 1
  • 1
Dan Mašek
  • 17,852
  • 6
  • 57
  • 85
  • Well done. You can simplify the kmeans code [a little bit](http://stackoverflow.com/a/34734939/5008845) ;D – Miki Apr 04 '16 at 09:13