How can i draw boundary across a particular colour in opencv? - opencv

Suppose I have an image. I basically want to make boundary across a particular colour that I want. I know the hsv minimum and maximum scalar values of that colour. But I don't know how to proceed further.
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include<stdio.h>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
VideoCapture cap(0);
while(true)
{
Mat img;
cap.read(img);
Mat dst;
Mat imghsv;
cvtColor(img, imghsv, COLOR_BGR2HSV);
inRange(imghsv,
Scalar(0, 30, 0),
Scalar(20, 150, 255),
dst
);
imshow("name",dst);
if (waitKey(30) == 27) //wait for 'esc' key press for 30ms
{
cout << "esc key is pressed by user" << endl;
break;
}
}
}
The inrange function works well but I am not able to draw a boundary across whatever is white (I mean whichever pixel is in the range specified)

You need to first segment the color, and then find the contours of the segmented image.
SEGMENT THE COLOR
Working in HSV is in general a good idea to segment colors. Once you have the correct lower and upper boundary, you can easily segment the color.
A simple approach is to use inRange.
You can find how to use it here for example.
FIND BOUNDARIES
Once you have the binary mask (obtained through segmentation), you can find its boundaries using findContours. You can refer to this or this to know how to use findContours to detect the boundary, and drawContours to draw it.
UPDATE
Here a working example on how to draw a contour on segmented objects.
I used some morphology to clean the mask, and changed to tracked color to be blue, but you can put your favorite color.
#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
VideoCapture cap(0);
while (true)
{
Mat img;
cap.read(img);
Mat dst;
Mat imghsv;
cvtColor(img, imghsv, COLOR_BGR2HSV);
inRange(imghsv, Scalar(110, 100, 100), Scalar(130, 255, 255), dst); // Detect blue objects
// Remove some noise using morphological operators
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(7,7));
morphologyEx(dst, dst, MORPH_OPEN, kernel);
// Find contours
vector<vector<Point>> contours;
findContours(dst.clone(), contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Draw all contours (green)
// This
drawContours(img, contours, -1, Scalar(0,255,0));
// If you want to draw a contour for a particular one, say the biggest...
// Find the biggest object
if (!contours.empty())
{
int idx_biggest = 0;
int val_biggest = contours[0].size();
for (int i = 0; i < contours.size(); ++i)
{
if (val_biggest < contours[i].size())
{
val_biggest = contours[i].size();
idx_biggest = i;
}
}
// Draw a single contour (blue)
drawContours(img, contours, idx_biggest, Scalar(255,0,0));
// You want also the rotated rectangle (blue) ?
RotatedRect r = minAreaRect(contours[idx_biggest]);
Point2f pts[4];
r.points(pts);
for (int j = 0; j < 4; ++j)
{
line(img, pts[j], pts[(j + 1) % 4], Scalar(0, 0, 255), 2);
}
}
imshow("name", dst);
imshow("image", img);
if (waitKey(30) == 27) //wait for 'esc' key press for 30ms
{
cout << "esc key is pressed by user" << endl;
break;
}
}
}

If you want a particular hue to be detected then you can create a mask to select only the particular color from your original image.
on the hue channel (img):
cv::Mat mask = cv::Mat::zeros(img.size(),CV_8UC1);
for(int i=0;i<img.rows;i++){
for(int j=0;j<img.cols;i++){
if(img.at<uchar>(i,j)==(uchar)specific_hue){
mask.at<uchar>(i,j)=(uchar)255;
}
}
}
color_img.copyTo(masked_image, mask);
If you want something less rigorous, you can define a range around the color to allow more image to pass through the mask.
cv::Mat mask = cv::Mat::zeros(img.size(),CV_8UC1);
int threshold = 5;
for(int i=0;i<img.rows;i++){
for(int j=0;j<img.cols;i++){
if((img.at<uchar>(i,j)>(uchar)(specific_hue - threshold)) && (img.at<uchar>(i,j)<(uchar)(specific_hue + threshold))){
mask.at<uchar>(i,j)=(uchar)255;
}
}
}
color_img.copyTo(masked_image, mask);

Related

OpenCV convexityDefects drawing

Hi. I have the above image and use the "findContours" function.
And then I use the "convexity defects" functions to find the corner points.
The result is as follows.
The problem with this code is that it can not find the rounded corners.You can not find a point like the following.
This is my code
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <iostream>
#include <sstream>
#include <fstream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
cv::Mat image = cv::imread("find_Contours.png");
//Prepare the image for findContours
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::threshold(image, image, 128, 255, CV_THRESH_BINARY);
//Find the contours. Use the contourOutput Mat so the original image doesn't get overwritten
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = image.clone();
cv::findContours(contourOutput, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
////convexityDefects
vector<vector<Point> >hull(contours.size());
vector<vector<int> > hullsI(contours.size()); // Indices to contour points
vector<vector<Vec4i>> defects(contours.size());
for (int i = 0; i < contours.size(); i++)
{
convexHull(contours[i], hull[i], false);
convexHull(contours[i], hullsI[i], false);
if (hullsI[i].size() > 3) // You need more than 3 indices
{
convexityDefects(contours[i], hullsI[i], defects[i]);
}
}
///// Draw convexityDefects
for (int i = 0; i < contours.size(); ++i)
{
for (const Vec4i& v : defects[i])
{
float depth = v[3]/256;
if (depth >= 0) // filter defects by depth, e.g more than 10
{
int startidx = v[0]; Point ptStart(contours[i][startidx]);
int endidx = v[1]; Point ptEnd(contours[i][endidx]);
int faridx = v[2]; Point ptFar(contours[i][faridx]);
circle(image, ptFar, 4, Scalar(255, 255, 255), 2);
cout << ptFar << endl;
}
}
}
//
cv::imshow("Input Image", image);
cvMoveWindow("Input Image", 0, 0);
//
waitKey(0);
}
Can someone make the code and find the red dot? please help.
now i want find "convexity defects" from inside,not outside like this image:
Someone can help me??
It is very important to use
convexHull(contours[i], hullsI[i], true);
That is, with the last argument "true" for indices. I'm almost certain this is the reason it cannot find all the defects. Before fixing this, it is not much sense try to find other bugs (if any).

Bounding a foreground object

I want to bound the foreground object by a rectangle, how shall I go about doing it?
I tried bounding the rectangle by collecting the white pixels , but it bounds the whole screen...
How shall I solve this problem?
//defined later
vector<Point> points;
RNG rng(12345);
int main(int argc, char* argv[])
{
//create GUI windows
namedWindow("Frame");
namedWindow("FG Mask MOG 2");
pMOG2 = createBackgroundSubtractorMOG2();
VideoCapture capture(0);
//update the background model
pMOG2->apply(frame, fgMaskMOG2);
frame_check = fgMaskMOG2.clone();
erode(frame_check, frame_check, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
dilate(frame_check, frame_check, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// Detect edges using Threshold
//threshold(frame_check, frame_check, 100, 255, THRESH_BINARY);
//find contours
findContours(frame_check, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//points
for (size_t i = 0; i < contours.size(); i++) {
for (size_t j = 0; j < contours[i].size(); j++) {
if (contours[i].size() > 2000)
{
cv::Point p = contours[i][j];
points.push_back(p);
}
}
}
if (points.size() > 0){
Rect brect = cv::boundingRect(cv::Mat(points).reshape(2));
rectangle(frame, brect.tl(), brect.br(), CV_RGB(0, 255, 0), 2, CV_AA);
cout << points.size() << endl;
}
imshow("Frame", frame);
imshow("FG Mask MOG 2", fgMaskMOG2);
//get the input from the keyboard
keyboard = waitKey(1000);
}
}
Try this, it's from a project I am working on. To isolate objects I use color masks, which produce binary images, but in your case with a proper threshold on the foreground image (after filtering) you can achieve the same result.
// Morphological opening
erode(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
dilate(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
// Morphological closing
dilate(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
erode(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
// Find contours
vector<vector<Point>> contours;
findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
vector<Rect> rectangles;
for(auto& contour : contours)
{
// Produce a closed polygon with object's contours
vector<Point> polygon;
approxPolyDP(contour, polygon, 3, true);
// Get polygon's bounding rectangles
Rect rect = boundingRect(polygon);
rectangles.push_back(rect);
}
This is a small example on how to draw a bounding box around the foreground objects using the points of the contour. It's with OpenCV 2.9, so you probably need to change the initialization of the BackgroundSubtractorMOG2 if you're using OpenCV 3.0.
#include <opencv2\opencv.hpp>
using namespace cv;
int main(int argc, char *argv[])
{
BackgroundSubtractorMOG2 bg = BackgroundSubtractorMOG2(30, 16.0, false);
VideoCapture cap(0);
Mat3b frame;
Mat1b fmask;
for (;;)
{
cap >> frame;
bg(frame, fmask, -1);
vector<vector<Point>> contours;
findContours(fmask.clone(), contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
for(int i=0; i<contours.size(); ++i)
{
if(contours[i].size() > 200)
{
Rect roi = boundingRect(contours[i]);
drawContours(frame, contours, i, Scalar(0,0,255));
rectangle(frame, roi, Scalar(0,255,0));
}
}
imshow("frame", frame);
imshow("mask", fmask);
if (cv::waitKey(30) >= 0) break;
}
return 0;
}

accumulatedweight throws cv:Exception error

I am new to OpenCV and trying to find contours and draw rectangle on them, here's my code but its throwing cv::Exception when it comes to accumulatedweighted().
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <ctype.h>
using namespace cv;
using namespace std;
static void help()
{
cout << "\nThis is a Example to implement CAMSHIFT to detect multiple motion objects.\n";
}
Rect rect;
VideoCapture capture;
Mat currentFrame, currentFrame_grey, differenceImg, oldFrame_grey,background;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
bool first = true;
int main(int argc, char* argv[])
{
//Create a new movie capture object.
capture.open(0);
if(!capture.isOpened())
{
//error in opening the video input
cerr << "Unable to open video file: " /*<< videoFilename*/ << endl;
exit(EXIT_FAILURE);
}
//capture current frame from webcam
capture >> currentFrame;
//Size of the image.
CvSize imgSize;
imgSize.width = currentFrame.size().width; //img.size().width
imgSize.height = currentFrame.size().height; ////img.size().height
//Images to use in the program.
currentFrame_grey.create( imgSize, IPL_DEPTH_8U);//image.create().
while(1)
{
capture >> currentFrame;//VideoCapture& VideoCapture::operator>>(Mat& image)
//Convert the image to grayscale.
cvtColor(currentFrame,currentFrame_grey,CV_RGB2GRAY);//cvtColor()
// Converting Original image to make both background n original image same
currentFrame.convertTo(currentFrame,CV_32FC3);
background = Mat::zeros(currentFrame.size(), CV_32FC3);
//Here its throwing exception
accumulateWeighted(currentFrame,background,1.0,NULL);
imshow("Background",background);
if(first) //Capturing Background for the first time
{
differenceImg = currentFrame_grey.clone();//img1 = img.clone()
oldFrame_grey = currentFrame_grey.clone();//img2 = img.clone()
convertScaleAbs(currentFrame_grey, oldFrame_grey, 1.0, 0.0);//convertscaleabs()
first = false;
continue;
}
//Minus the current frame from the moving average.
absdiff(oldFrame_grey,currentFrame_grey,differenceImg);//absDiff()
//bluring the differnece image
blur(differenceImg, differenceImg, imgSize);//blur()
//apply threshold to discard small unwanted movements
threshold(differenceImg, differenceImg, 25, 255, CV_THRESH_BINARY);//threshold()
//find contours
findContours(differenceImg,contours,hierarchy,CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); //findcontours()
//draw bounding box around each contour
//for(; contours! = 0; contours = contours->h_next)
for(int i = 0; i < contours.size(); i++)
{
rect = boundingRect(contours[i]); //extract bounding box for current contour
//drawing rectangle
rectangle(currentFrame, cvPoint(rect.x, rect.y), cvPoint(rect.x+rect.width, rect.y+rect.height), cvScalar(0, 0, 255, 0), 2, 8, 0);
}
//New Background
convertScaleAbs(currentFrame_grey, oldFrame_grey, 1.0, 0.0);
//display colour image with bounding box
imshow("Output Image", currentFrame);//imshow()
//display threshold image
imshow("Difference image", differenceImg);//imshow()
//clear memory and contours
//cvClearMemStorage( storage );
//contours = 0;
contours.clear();
//background = currentFrame;
//press Esc to exit
char c = cvWaitKey(33);
if( c == 27 ) break;
}
// Destroy All Windows.
destroyAllWindows();
return 0;
}
Please Help to solve this.
First of all, I don't really get the idea of calling accumulateWeighted with alpha = 1.0. If you look at the definition of accumulateWeighted in the doc, you will see that with alpha = 1.0 it is basically equivalent to copy currentFrame into background at each iteration.
Moreover, it is an accumulation function, to accumulate image changes over time into a new image. What is the interest of it if you reset background at every loop with background = Mat::zeros(currentFrame.size(), CV_32FC3); ?
This being said, there is a little flaw in your code with the 4th argument of the function. You wrote accumulateWeighted(currentFrame,background,1.0,NULL);. If you look into the documentation you will find that the 4th argument is a Mask, and is optional. Passing a NULL pointer here might be the source of your exception. Why don't you call the function like this : accumulateWeighted(currentFrame,background,1.0); ?
Hope this helps,
Ben

Detect/Fitting Circles using Hough Transform in OpenCV 2.4.6

The objective is to detect the 5 white circles in the image.The test image in which the circles have to be detected is the one shown here 640x480
Please download the original image here,1280x1024
I am using different methods to bring out a evaluation of various circle/ellipse detection methods. But somehow I am not able to fix my simple Hough transform code. It does not detect any circles. I am not clear whether the problem is with pre-processing step, or the parameters of the HoughCircle. I have gone through all the similar questions in the forum, but still not able to fix the issue. This is my code. Please help me in this regards..
Header file
#ifndef IMGPROCESSOR_H
#define IMGPROCESSOR_H
// OpenCV Library
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
class ImgProcessor{
public:
Mat OpImg ;
ImgProcessor();
~ImgProcessor();
//aquire filter methods to image
int Do_Hough(Mat IpImg);
};
#endif /* ImgProcessor_H */
Source file
#include "ImgProcessor.h"
#include <opencv2\opencv.hpp>
#include "opencv2\imgproc\imgproc.hpp"
#include "opencv2\imgproc\imgproc_c.h"
#include <vector>
using namespace cv;
ImgProcessor::ImgProcessor(){
return;
}
ImgProcessor::~ImgProcessor(){
return;
}
//Apply filtering for the input image
int ImgProcessor::Do_Hough(Mat IpImg)
{
//Parameter Initialization________________________________________________________
double sigma_x, sigma_y, thresh=250, max_thresh = 255;
int ksize_w = 5 ;
int ksize_h = 5;
sigma_x = 0.3*((ksize_w-1)*0.5 - 1) + 0.8 ;
sigma_y = 0.3*((ksize_h-1)*0.5 - 1) + 0.8 ;
vector<Vec3f> circles;
//Read the image as a matrix
Mat TempImg;
//resize(IpImg, IpImg ,Size(), 0.5,0.5, INTER_AREA);
//Preprocessing__________________________________________________________
//Perform initial smoothing
GaussianBlur( IpImg, TempImg, Size(ksize_w, ksize_h),2,2);
//perform thresholding
threshold(TempImg,TempImg, thresh,thresh, 0);
//Remove noise by gaussian smoothing
GaussianBlur( TempImg, TempImg, Size(ksize_w, ksize_h),2,2);
/*imshow("Noisefree Image", TempImg);
waitKey(10000);*/
//Obtain edges
Canny(TempImg, TempImg, 255,240 , 3);
imshow("See Edges", TempImg);
waitKey(10000);
//Increase the line thickness
//dilate(TempImg,TempImg,0,Point(-1,-1),3);
//Hough Circle Method______________________________________________________________
// Apply the Hough Transform to find the circles
HoughCircles( TempImg, circles, 3, 1, TempImg.rows/32, 255, 240, 5, 0 );
// Draw the circles detected
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle center
circle( IpImg, center, 3, Scalar(0,255,0), -1, 8, 0 );
// circle outline
circle( IpImg, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
// Show your results
namedWindow( "Hough Circle Transform", WINDOW_AUTOSIZE );
imshow( "Hough Circle Transform", IpImg );
// waitKey(0);
return 0;
}
int main(int argc, char** argv)
{
ImgProcessor Iclass;
//char* imageName = argv[1];
string imageName = "D:/Projects/test_2707/test_2707/1.bmp";
Mat IpImg = imread( imageName );
cvtColor(IpImg, IpImg,6,CV_8UC1);
Iclass.Do_Hough(IpImg);
/*Iclass.Do_Contours(IpImg);*/
return 0;
}
The code seems fine, other than for:
HoughCircles( TempImg, circles, 3, 1, TempImg.rows/32, 255, 240, 5, 0 );
Does number 3 in the parameter list correspond to CV_HOUGH_GRADIENT ? It is always better to use definitions instead of numbers.
May be you should test it first with an image with bigger circles. Once you are sure that the rest of the code is correct, you can tune the parameters of HoughCircles.

find shape and color circle

I have detected just all the red contours and am struggling to find a way to run a shape detection algorithm on these contours to get just red circules but don't know how to extract just red circle and eliminate the undesirable rest of contours ? Source Code:
#include "stdafx.h"
#include"math.h"
#include"conio.h"
#include"cv.h"
#include"highgui.h"
#include"stdio.h"
#include <math.h>
int main()
{
int i,j,k;
int h,w,seuill,channels;
int seuilr, channelsr;
int temp=0;
uchar *data,*datar;
i=j=k=0;
IplImage *frame=cvLoadImage("Mon_image.jpg",1);
IplImage *result=cvCreateImage( cvGetSize(frame), IPL_DEPTH_8U, 1 );
IplImage *gray=cvCreateImage( cvGetSize(frame), IPL_DEPTH_8U, 1 );
cvCvtColor(frame, result, CV_BGR2GRAY );
//IplImage* gray;
cvNamedWindow("original",CV_WINDOW_AUTOSIZE);
cvNamedWindow("Result",CV_WINDOW_AUTOSIZE);
h = frame->height;
w = frame->width;
seuill =frame->widthStep;
channels = frame->nChannels;
data = (uchar *)frame->imageData;
seuilr=result->widthStep;
channelsr=result->nChannels;
datar = (uchar *)result->imageData;
for(i=0;i < (h);i++)
for(j=0;j <(w);j++)
{
if(((data[i*seuill+j*channels+2]) >(19+data[i*seuill+j*channels]))&& ((data[i*seuill+j*channels+2]) > (19+data[i*seuill+j*channels+1])))
datar[i*seuilr+j*channelsr]=255;
else
datar[i*seuilr+j*channelsr]=0;
}
cvCanny(result,result, 50, 100, 3);
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* circles = cvHoughCircles(result, storage, CV_HOUGH_GRADIENT, 1, 40.0, 100, 100,0,0);
cvShowImage("original",frame);
cvShowImage("Result",result);
cvSaveImage("result.jpg",result);
cvWaitKey(0);
cvDestroyWindow("original");
cvDestroyWindow("Result");
return 0;
}
I will rather use RANSAC algorithm to detect circle in your set of contours but, Hough transform will also do the work.
See here for an explication of the both process. Solution in matlab in given.

Resources