There is an image of apparently rags containing a set of stripes in shades of green:

Source image

The color values ​​of the pixels are closely located to each other, plus there is a color heterogeneity in each of the strips, which greatly hinders if you try to use the usual binarization.

How to determine the area of ​​each of the strips in the image?

  • one
    I didn’t work with OpenCV, but I think it’s necessary to blur, jot down a bunch of dots and find color clusters from them. - AivanF.
  • 2
    Need an algorithm - filter type smar tblur. This blur with a threshold. Thresholds are different in size of the filter on the strength of the color difference in brightness. It is necessary to kill the thin white stripes retaining the dark vertical, and then to cluster ... - Andrey Golikov

1 answer 1

The time spent on finding a solution showed that it is difficult to disagree with the authors of comments on the use of blurring and clustering. Found that to implement the second item (actually clustering), you can use the K-means method. Of course, provided that the number of color bars in the image is known in advance:

const int cluster_count = 10; 

Load an image from a file and divide its matrix into separate channels:

 cv::Mat src_mat = cv::imread("image.jpg"); if(src_mat.empty()) return; std::vector<cv::Mat> channels; cv::split(src_mat, channels); 

By default, OpenCV will load the image in BGR format. You can convert it to other color spaces (for example, HSV or YCbCr ), but this did not bring any further benefit. I even tried to combine different channels of different color spaces with each other, but this improvement also did not work.

Next, you need to create a matrix whose height will be equal to the number of points of the original image, and the width to the number of channels obtained earlier:

 const int rows = src_mat.rows * src_mat.cols; const int cols = channels.size(); cv::Mat mat = cv::Mat::zeros(rows, cols, CV_8U); for(int col = 0; col < cols; ++col) { // Применяем размытие с ядром 4х4. // Как оказалось, это даёт самый лучший результат. // Больший размер ядра фильтра размытия ведёт // к большей деформации результата, так что 4х4 - это компромисс. cv::Mat blr_mat; cv::blur(channels[col], blr_mat, cv::Size(4,4)); // Размытое изображение переформатируем в матрицу с одной колонкой. blr_mat.reshape(1,rows).copyTo(mat.col(col)); } 

Convert the matrix data into a floating point type - float:

 mat.convertTo(mat, CV_32F); 

The K-means clustering itself remains:

 cv::Mat labels_mat; cv::kmeans(mat, cluster_count, labels_mat, cv::TermCriteria(), 1 , cv::KMEANS_PP_CENTERS); 

Now labels_mat will contain the property values ​​of each pixel of the image to a particular cluster. For ease of drawing, you can normalize these values:

 cv::convertScaleAbs(labels_mat.reshape(0,src_mat.rows) , labels_mat, 255/cluster_count, 1); 

The result:

Result

It is noticeable that everything is far from ideal, however, in general, the color strips are highlighted correctly and you just need to work on eliminating artifacts. This can be done with the help of morphological operations or even through the processing of contours, eliminating small ones by their area.