****** EDIT-1EDIT-2 *******
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++. Based on the comment from Tetragramm, 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_8S);
auto nLabels = cv::connectedComponents(bw, labelImage, 8);
vector<cv::Vec3b> colors(nLabels);
colors[0] = cv::Vec3b(0, 0, 0); //background
for(auto label = 1; label < nLabels; ++label){
colors[label] = cv::Vec3b(255, 255, 255);
}
cv::Mat dst(img.size(), CV_8UC3);
for(auto r = 0; r < dst.rows; ++r){
for(auto c = 0; c < dst.cols; ++c){
auto label = labelImage.at<int>(r, c);
cv::Vec3b &pixel = dst.at<cv::Vec3b>(r, c);
pixel = colors[label];
}
}
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 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:341: error: (-215) L.channels() == 1 && I.channels() == 1 in function connectedComponents_sub1
I think I know why I'm getting this error. According to the connectComponents documentation, it only supports image types CV_32S and CV_16U. I confirmed this when I changed the line
auto nLabels = cv::connectedComponents(bw, labelImage, 8);
to
auto nLabels = cv::connectedComponents(bw, labelImage, 8, CV_8S);
and got the following exception:
3, 0
8UC3
OpenCV Error: Unsupported format or combination of formats (the type of labels must be 16u or 32s) in connectedComponents, file /source/opencv-3.0.0/modules/imgproc/src/connectedcomponents.cpp, line 377
Exception caught
/source/opencv-3.0.0/modules/imgproc/src/connectedcomponents.cpp:377: error: (-210) the type of labels must be 16u or 32s in function connectedComponents
The problem is I don't know how to fix this. Is there a work around for this? If you'd like to try the entire code you can download it from my github account here.
****** Original Question *******
Hi,
I am detecting multiple objects of different color from a video stream. I would like to count how many objects of each color appear in the stream. I am using OpenCV 2.4.8 on Ubuntu 14.04. Here is the code:
#!/usr/bin/env python
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while(1):
# Take each frame
_, frame = cap.read()
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])
# define range of red color in HSV
lower_red = np.array([160, 100, 100])
upper_red = np.array([179, 255, 255])
# Threshold the HSV image to get only blue colors
blue_mask = cv2.inRange(hsv, lower_blue, upper_blue)
# Threshold the HSV image to get only red colors
red_mask = cv2.inRange(hsv, lower_red, upper_red)
# add the masks
mask = red_mask + blue_mask
# Bitwise-AND mask and original image
res = cv2.bitwise_and(frame, frame, mask = mask)
# cv2.imshow('frame',frame)
# cv2.imshow('mask',mask)
cv2.imshow('res',res)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
Attached is an image from the video stream. In that image there are 4 red boxes and 4 blue boxes which I would like to count.
Any help is appreciated. Thanks.C:\fakepath\image.png