I already tried to search about openCV ROI function, but All of them used rectangle roi function.
I want to get roi using by inclined line that get from hough transform function.
My situation is next :
I have multiple vertical lines(little inclined) that output from hough transform function.
i want to get image(Matrix) between vertical lines.
enter image description here
i want to get divided matrix in my image (For example, A image, B image, C image etc.. )
Is there ROI function that used line in openCV?
or
any another method?
I think you need to use contours to define your roi. If it is not a perfect square you can not use the ROI function, because this is always a perfect square (not even a rotated square)
int main()
{
enum hierIdx { H_NEXT = 0, H_PREVIOUS, H_FIRST_CHILD, H_PARENT };
cv::Mat img = cv::imread("example_image.jpg", cv::IMREAD_UNCHANGED);
// convert RGB to gray scale image
cv::Mat imgGrs;
cv::cvtColor(img, imgGrs, cv::COLOR_RGB2GRAY);
// because it was a .jpg the grey values are messed up
// we fix it by thresholding at 128
cv::threshold(imgGrs, imgGrs, 128, 255, cv::THRESH_BINARY);
imgGrs = ~imgGrs;
// now create contours (we need the hierarchy to find the inner shapes)
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(imgGrs.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
//cv::drawContours(img, contours, -1, cv::Scalar(255, 0, 0), 1);
int iLen = (int)hierarchy.size();
int idxChild = -1;
// find first child of master
for (int i = 0; i < iLen; i++){
if (hierarchy[i][H_PARENT] < 0) {
idxChild = hierarchy[i][H_FIRST_CHILD];
break;
}
}
// used for erosion of mask
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
while (idxChild >= 0)
{
// create image to use as mask for section
cv::Mat mask = cv::Mat::zeros(imgGrs.size(), CV_8U);
cv::drawContours(mask, contours, idxChild, cv::Scalar(255), CV_FILLED);
// make masker 1 pixel smaller so we wont see the outer contours
cv::erode(mask, mask, element);
// ok nu we create a singled out part we want
cv::Mat part = imgGrs & mask;
// Crop it to the AOI rectangle
cv::Rect aoi = cv::boundingRect(contours[idxChild]);
part = part(aoi);
// part is now the aoi image you asked for
// proceed to next AOI
idxChild = hierarchy[idxChild][H_NEXT];
}
return 0;
}
Related
I have found out the centroid of multiple objects in my image using the code provided here OpenCV examples
Here is the code which found the centroid and stored them in a vector.
cv::Mat InputImage;
cv::Mat CannyOutput;
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
RNG rng(12345);
InputImage = cv::imread("Untitled.jpg");
//Edge detection
Canny(InputImage, CannyOutput, 100, 150);
//Contour detection
cv::findContours(CannyOutput, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
//Finding Moments
vector<Moments> mu(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mu[i] = moments(contours[i], false);
}
//Calculating Centroid
vector<Point2f> mc(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
}
// Drawing
Mat drawing = Mat::zeros(CannyOutput.size(), CV_8UC3);
for (int i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
//Drawing contour
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point());
//Drawing circles with centroid as centre
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
What I want to do is draw a polygon with the centroid as the vertices. I used drawcontours, polyline and line functions but not getting the desired result. Is there a way to achieve this? . I need it to be achieved in C++
Output image
Desired image
Also, on another note, the code doesnt seem to be displaying the centroid if am replacing the 'color' variable with BGR value. Seems like both contour and centroid has to be of same color if i want to see the centroid. When i gave (0,255,255) for contour and (255,255,0) for centroid, the centroid was not displaying .
For the given sample image, you can use convexhull to obtain the order of centers, and then draw them with polylines.
A doubt came to my mind this morning: does the findChessboardCorners OpenCV function work with a chessboard of different colours, for example blue?
If it's not the case, do you think that a quite straightforward thresholding would do the trick?
You can't pass coloured images to the findChessboardCorners because it only takes a greyscale image as #api55 pointed out in his comment.
You might be worth taking a look at the checkchessboard code provided here
// does a fast check if a chessboard is in the input image. This is a workaround to
// a problem of cvFindChessboardCorners being slow on images with no chessboard
// - src: input binary image
// - size: chessboard size
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
// 0 if there is no chessboard, -1 in case of error
int checkChessboardBinary(const cv::Mat & img, const cv::Size & size)
{
CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
Mat white = img.clone();
Mat black = img.clone();
int result = 0;
for ( int erosion_count = 0; erosion_count <= 3; erosion_count++ )
{
if ( 1 == result )
break;
if ( 0 != erosion_count ) // first iteration keeps original images
{
erode(white, white, Mat(), Point(-1, -1), 1);
dilate(black, black, Mat(), Point(-1, -1), 1);
}
vector<pair<float, int> > quads;
fillQuads(white, black, 128, 128, quads);
if (checkQuads(quads, size))
result = 1;
}
return result;
}
With the main loop being:
CV_IMPL
int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
CvPoint2D32f* out_corners, int* out_corner_count,
int flags )
is the main implementation of this method. In here they
Use cvCheckChessboard to determine if a chessboard is in the image
Convert to binary (B&W) and dilate to split the corners apart Use
icvGenerateQuads to find the squares.
So in answer to your question, as long as there is sufficient contrast in your image after you convert it to greyscale it will likely work, I would imagine a greyscaled blue and white image would be good enough, if it was a light aqua or yellow or something you might struggle without more processing
I am struggling with finding the appropriate contour algorithm for a low quality image. The example image shows a rock scene:
What I am trying to achieve is to find contours arround features such as:
light areas
dark areas
grey1 areas
grey2 areas
etc. until grey-n areas
(The number of areas shall be a parameter of choice)
I do not want to take a simple binary-threshold but rather use some sort of contour-finding (for example watershed or other). The major feature-lines shall be kept, noise within a feature-are can be flattened.
The result of my code can be seen on the images to the right.
Unfortunately, as you can easily tell, the colors do not really represent the original large-scale image features! For example: check out the two areas that I circled with red - these features are almost completely flooded with another color. What I imagine is that at least the very light and the very dark areas are covered by its own color.
cv::Mat cv_src = cv::imread(argv[1]);
cv::Mat output;
cv::Mat cv_src_gray;
cv::cvtColor(cv_src, cv_src_gray, cv::COLOR_RGB2GRAY);
double clipLimit = 0.1;
cv::Size titleGridSize = cv::Size(8,8);
cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(clipLimit, titleGridSize);
clahe->apply(cv_src_gray, output);
cv::equalizeHist(output, output);
cv::cvtColor(output, cv_src, cv::COLOR_GRAY2RGB);
// Create binary image from source image
cv::Mat bw;
cv::cvtColor(cv_src, bw, cv::COLOR_BGR2GRAY);
cv::threshold(bw, bw, 180, 255, cv::THRESH_BINARY);
// Perform the distance transform algorithm
cv::Mat dist;
cv::distanceTransform(bw, dist, cv::DIST_L2, CV_32F);
// Normalize the distance image for range = {0.0, 1.0}
cv::normalize(dist, dist, 0, 1., cv::NORM_MINMAX);
// Threshold to obtain the peaks
cv::threshold(dist, dist, .2, 1., cv::THRESH_BINARY);
// Create the CV_8U version of the distance image
cv::Mat dist_8u;
dist.convertTo(dist_8u, CV_8U);
// Find total markers
std::vector<std::vector<cv::Point> > contours;
cv::findContours(dist_8u, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
int ncomp = contours.size();
// Create the marker image for the watershed algorithm
cv::Mat markers = cv::Mat::zeros(dist.size(), CV_32S);
// Draw the foreground markers
for (int i = 0; i < ncomp; i++)
cv::drawContours(markers, contours, i, cv::Scalar::all(i+1), -1);
// Draw the background marker
cv::circle(markers, cv::Point(5,5), 3, CV_RGB(255,255,255), -1);
// Perform the watershed algorithm
cv::watershed(cv_src, markers);
// Generate random colors
std::vector<cv::Vec3b> colors;
for (int i = 0; i < ncomp; i++)
{
int b = cv::theRNG().uniform(0, 255);
int g = cv::theRNG().uniform(0, 255);
int r = cv::theRNG().uniform(0, 255);
colors.push_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));
}
// Create the result image
cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);
// Fill labeled objects with random colors
for (int i = 0; i < markers.rows; i++)
{
for (int j = 0; j < markers.cols; j++)
{
int index = markers.at<int>(i,j);
if (index > 0 && index <= ncomp)
dst.at<cv::Vec3b>(i,j) = colors[index-1];
else
dst.at<cv::Vec3b>(i,j) = cv::Vec3b(0,0,0);
}
}
// Show me what you got
imshow("final_result", dst);
I think you can use a simple clustering such as k-means for this, then examine the cluster centers (or the mean and standard deviations of each cluster). I quickly tried it in matlab.
im = imread('tvBqt.jpg');
gr = rgb2gray(im);
x = double(gr(:));
idx = kmeans(x, 4);
cl = reshape(idx, 600, 472);
figure,
subplot(1, 2, 1), imshow(gr, []), title('original')
subplot(1, 2, 2), imshow(label2rgb(cl), []), title('clustered')
The result:
You could try using SLIC Superpixels. I tried it and showed some good results. You could vary the parameters to get better clustering.
SLIC Superpixels
SLIC Superpixels with OpenCV C++
SLIC Superpixels with OpenCV Python
What is the most efficient way to find the bounding box of the largest blob in a binary image using OpenCV? Unfortunately, OpenCV does not have specific functionality for blob detection. Should I just use findContours() and search for the largest in the list?
Here. It. Is.
(FYI: try not to be lazy and figure out what happens in my function below.
cv::Mat findBiggestBlob(cv::Mat & matImage){
int largest_area=0;
int largest_contour_index=0;
vector< vector<Point> > contours; // Vector for storing contour
vector<Vec4i> hierarchy;
findContours( matImage, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image
for( int i = 0; i< contours.size(); i++ ) {// iterate through each contour.
double a=contourArea( contours[i],false); // Find the area of contour
if(a>largest_area){
largest_area=a;
largest_contour_index=i; //Store the index of largest contour
//bounding_rect=boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
}
drawContours( matImage, contours, largest_contour_index, Scalar(255), CV_FILLED, 8, hierarchy ); // Draw the largest contour using previously stored index.
return matImage;
}
If you want to use OpenCV libs, check out OpenCVs SimpleBlobDetector. Here's another stack overflow showing a small tutorial of it: How to use OpenCV SimpleBlobDetector
This only gives you key points though. You could use this as an initial search to find the blob you want, and then possibly use the findContours algorithm around the most likely blobs.
Also the more information you know about your blob, you can provide parameters to filter out the blobs you don't want. You might want to test out the area parameters of the SimpleBlobDetector. Possibly could could compute the area based on the size of the area of the image and then iteratively allow for a smaller blob if the algorithm does not detect any blobs.
Here is the link to the main OpenCV documentation: http://docs.opencv.org/modules/features2d/doc/common_interfaces_of_feature_detectors.html#simpleblobdetector
To find the bounding box of the largest blob, I used findContours, followed by the following code:
double maxArea = 0;
for (MatOfPoint contour : contours) {
double area = Imgproc.contourArea(contour);
if (area > maxArea) {
maxArea = area;
largestContour = contour;
}
}
Rect boundingRect = Imgproc.boundingRect(largestContour);
Since no one has posted a complete OpenCV solution, here's a simple approach using thresholding + contour area filtering
Input image
Largest blob/contour highlighted in green
import cv2
# Load image, grayscale, Gaussian blur, and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Find contours and sort using contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
# Highlight largest contour
cv2.drawContours(image, [c], -1, (36,255,12), 3)
break
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()
TimZaman, your code has a bug but I can't comment so I start a new and correct answer.
Here is my solution based on 1"'s and TimZaman's ideas:
Mat measure::findBiggestBlob(cv::Mat &src){
int largest_area=0;
int largest_contour_index=0;
Mat temp(src.rows,src.cols,CV_8UC1);
Mat dst(src.rows,src.cols,CV_8UC1,Scalar::all(0));
src.copyTo(temp);
vector<vector<Point>> contours; // storing contour
vector<Vec4i> hierarchy;
findContours( temp, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
for( int i = 0; i< contours.size(); i++ ) // iterate
{
double a=contourArea( contours[i],false); //Find the largest area of contour
if(a>largest_area)
{
largest_area=a;
largest_contour_index=i;
}
}
drawContours( dst, contours,largest_contour_index, Scalar(255), CV_FILLED, 8, hierarchy );
// Draw the largest contour
return dst;
}
I'm trying to draw contours around blobs in a binary image, however, sometimes, openCV draws a single contour around two distinct blobs. below is an example. How can i solve this issue?
Here it should draw two bounding boxes for the blob on the right and separately for the one of the left. I agree they are close but enough distance in between them. I'm only drawing External contours instead of the tree or list. I'm also using cvFindNextContour(contourscanner) as this is a easier implementation for my case.
Thanks
EDIT:
Image displayed in the "output" window is from a different function which does just image subtraction. Image displayed in the "contours" window is in the function pplfind(). "output" image is passed to img_con().
IplImage* img_con(IplImage* image){
int ppl;
CvMemStorage* memstr = cvCreateMemStorage();
IplImage* edges = cvCreateImage(cvGetSize(image),8,1);
cvCanny(image,edges,130,255);
CvContourScanner cscan = cvStartFindContours(image,memstr,sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE,cvPoint(0,0));
ppl = pplfind(cscan,cvGetSize(image));
if (ppl !=0 )
printf("Estimated number of people: %d\n",ppl);
cvEndFindContours(&cscan);
cvClearMemStorage(memstr);
return edges;
}
int pplfind(CvContourScanner cscan, CvSize frSize){
ofstream file; char buff[50];
file.open("box.txt",ofstream::app);
int ppl =0;
CvSeq* c;
IplImage *out = cvCreateImage(frSize,8,3);
while (c = cvFindNextContour(cscan)){
CvRect box = cvBoundingRect(c,1);
if ((box.height > int(box.width*1.2))&&(box.height>20)){//&&(box.width<20)){//
ppl++;
cvRectangle(out,cvPoint(box.x,box.y),cvPoint(box.x+box.width,box.y+box.height),CV_RGB(255,0,50),1);
cvShowImage("contours",out);
//cvWaitKey();
}
//printf("Box Height: %d , Box Width: %d ,People: %d\n",box.height,box.width,ppl);
//cvWaitKey(0);
int coord = sprintf_s(buff,"%d,%d,%d\n",box.width,box.height,ppl);
file.write(buff,coord);
}
file.close();
cvReleaseImage(&out);
return ppl;
}
I've never used cvFindNextContour, but running cvFindContours with CV_RETR_EXTERNAL on your image seems to work fine:
I use OpenCV + Python, so this code might not be useful for you, but for the sake of completeness here it goes:
contours = cv.findContours(img, cv.CreateMemStorage(0), mode=cv.CV_RETR_EXTERNAL)
while contours:
(x,y,w,h) = cv.BoundingRect(contours)
cv.Rectangle(colorImg, (x,y), (x+w,y+h), cv.Scalar(0,255,255,255))
contours = contours.h_next()
Edit: you asked how to draw only those contours with certain properties; it would be something like this:
contours = cv.findContours(img, cv.CreateMemStorage(0), mode=cv.CV_RETR_EXTERNAL)
while contours:
(x,y,w,h) = cv.BoundingRect(contours)
if h > w*1.2 and h > 20:
cv.Rectangle(colorImg, (x,y), (x+w,y+h), cv.Scalar(0,255,255,255))
contours = contours.h_next()