Count number of colored objects in image
****** EDIT-2 *******
Based on the comment from Tetragramm, I have simplified the connect_components function. However, I am still getting the same exception:
3, 0
8UC3
OpenCV Error: Assertion failed (L.channels() == 1 && I.channels() == 1) in connectedComponents_sub1, file /source/opencv-3.0.0/modules/imgproc/src/connectedcomponents.cpp, line 341
Exception caught
/source/opencv-3.0.0/modules/imgproc/src/connectedcomponents.cpp:341: error: (-215) L.channels() == 1 && I.channels() == 1 in function connectedComponents_sub1
Maybe I shouldn't use the connectedComponenets function and go at it by the old way (which I'm not sure how to do, so an example would be great!)?
****** EDIT-1 *******
Sorry for the delayed response. Being new to OpenCV I did some research and found out that a connected components function was introduced in OpenCV 3.0 and it is only available in C++ (AFAIK) as shown here. So, I decided to to write my code in C++. I have also updated the test image (I would like to make it work on this before I got to video stream). Here is my code:
#include <iostream>
#include <vector>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
cv::Mat connect_components(cv::Mat img, int threshval)
{
cv::Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);
cv::Mat labelImage(img.size(), CV_16U);
auto nLabels = cv::connectedComponents(bw, labelImage, 8);
bw = labelImage >= 1;
cv::Mat bwArray[3] = {bw, bw, bw};
cv::Mat dst(img.size(), CV_8UC3);
cv::merge(bwArray, 3, dst);
return dst;
}
int main(int argc, char **argv)
{
cv::VideoCapture webcam(0);
if (!webcam.isOpened()) {
cerr << "Cannot open webcam" << endl;
exit(-1);
}
// Define range for blue and red color in HSV
const cv::Scalar blue_range[] = { {110, 50, 50}, {130, 255, 255} };
const cv::Scalar red_range[] = { {0, 200, 50}, {10, 255, 150} };
cout << CV_MAJOR_VERSION << ", " << CV_MINOR_VERSION << endl;
cv::Mat original_frame, color_mask[2], final_mask;
while (true) {
try {
webcam.read(original_frame); // grab each frame
cv::Mat frame_hsv;
cv::cvtColor(original_frame, frame_hsv, CV_BGR2HSV); // convert BGR to HSV
// threshold HSV image to get only defined range colors
cv::inRange(frame_hsv, blue_range[0], blue_range[1], color_mask[0]);
cv::inRange(frame_hsv, red_range[0], red_range[1], color_mask[1]);
// add the masks
final_mask = color_mask[0] + color_mask[1];
// Bitwise-AND full mask and original image
cv::Mat colored_boxes;
cv::bitwise_and(original_frame, original_frame, colored_boxes, final_mask);
// Apply morphological closing
cv::Mat morphed_boxes;
cv::morphologyEx(colored_boxes, morphed_boxes, cv::MORPH_CLOSE, cv::Mat::ones(10, 10, CV_8UC1));
// Connected componenets
auto labeled_boxes = connect_components(colored_boxes, 62);
// Display the image
//cv::imshow("Webcam", original_frame);
cv::imshow("Boxes", morphed_boxes);
// quit progrem is user inputs ESC or q
auto user_input = (cv::waitKey(5) & 0xFF);
if ((user_input == 27) || (user_input == 113)) {
break;
}
}
catch (cv::Exception &e) {
cout << "Exception caught" << endl;
cout << e.what();
break;
}
}
return 0;
}
The connected_components function is adopted from here. When I run this, I get the following exception:
3, 0
8UC3
OpenCV Error: Assertion failed (L.channels() == 1 && I.channels() == 1) in connectedComponents_sub1, file /source/opencv-3.0.0/modules/imgproc/src/connectedcomponents.cpp, line 341
Exception caught
/source/opencv-3.0.0/modules/imgproc/src/connectedcomponents.cpp ...
What you can do is change the type of labelImage
to
You can also simplifiy your code a bit. I'm assuming you had more functionality there and moved it out of the function as opposed to preparing to add more. If you're adding more, with varying colors and such, just ignore this part.
@Tetragramm, thank you for your help. I have updated my OP.
Ah, I see now. You're passing in img as a CV_8UC3. Sorry, I read this just after waking up.
So at some point you need to flatten that to one channel. I suggest just passing the final_mask to the morphology operation, and the output from that into connected components.
Inside connect_components, modify it so it removes components that don't meet your criteria (too small, bad aspect ratio, whatever) before you create the three channel mask. Then do the bitwise and with the original image.