Related
My idea is to draw all vertical lines, which are created by calculating the Canny before, from a intersection point to a diagonal lines to another intersection point (also this point comes from a intersection between a vetical and diagonal line). As a reference here an image, the red vertical (Hough)lines should be drawen:
Until yet I just detect all vertical lines with this implementation:
int main(int argc, char *argv[]) {
std::vector<cv::Point> diagonalLine = DiagonalLines::diagonalLines(src);
Mat wdst, cwdst, contRegion;
vector<Vec4i> vericalLines;
double maxLineGap = 200.0;
double threshold = 100;
std::vector<cv::Vec4i> elemLinesCur;
cv::Scalar mu, sigma;
meanStdDev(src, mu, sigma);
Canny(src, wdst, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0], 3, false);
cvtColor(wdst, cwdst, CV_GRAY2BGR);
HoughLinesP(wdst, vericalLines, 1, CV_PI / 2, threshold, 50, 200);
cv::Vec4i current, previous;
cv::Point pt1, pt2, ppt1, ppt2;
for (size_t i = 1; i < vericalLines.size(); i++) {
current = vericalLines[i];
pt1 = cv::Point(current[0], current[1]);
pt2 = cv::Point(current[2], current[3]);
previous = vericalLines[i - 1];
ppt1 = cv::Point(previous[0], previous[1]);
ppt2 = cv::Point(previous[2], previous[3]);
if (diagonalLine[i - 1].y > pt2.y && diagonalLine[i].y < pt1.y) {
std::cout << "Intersection: " << pt2.x << "\n";
}
double distanceBetweenPointsX = abs(pt1.x - ppt1.x)*sqrt(2);
if (distanceBetweenPointsX >= 12) {
elemLinesCur.push_back(current);
double angle = atan2(ppt2.y - ppt1.y, ppt2.x - ppt1.x) * 180.0 / CV_PI; ///draw only vertical lines (90 degree)
if (angle) {
line(cwdst, pt1, pt2, cv::Scalar(0, 0, 255), 2, CV_AA);
}
//do some stuff
}
...and here a method, which detect only diagonal lines (it looks similiar to the above one):
std::vector<cv::Point> diagonalLines(cv::Mat src) {
std::vector<cv::Point> hitPoint;
Scalar mu, sigma;
meanStdDev(src, mu, sigma);
Canny(src, ddst, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0], 3, false);
cvtColor(ddst, cddst, CV_GRAY2BGR);
HoughLinesP(ddst, vertlines, 1, CV_PI / 180, 100, 50, 10);
cv::Point pt1, pt2;
for (size_t i = 1; i < vertlines.size(); i++) {
cv::Vec4i current = vertlines[i];
pt1 = cv::Point(current[0], current[1]);
pt2 = cv::Point(current[2], current[3]);
double angle = atan2(pt2.y - pt1.y, pt2.x - pt1.x) * 180.0 / CV_PI;
if (angle != -90 && angle != 90) {
//line(cddst, pt1, pt2, Scalar(0, 0, 255), 2, CV_AA);
hitPoint.push_back(pt1);
hitPoint.push_back(pt2);
}
}
return hitPoint;
}
What I know:
I should calculate all those intersection points, yes, I also tried it in if (diagonalLine[i - 1].y > pt2.y && diagonalLine[i].y < pt1.y) but I don't get the further steps. Could some one help me? Thank you in advance!
The OpenCV function line() accepts endpoints as the arguments, so all you need to do is calculate the intersections and use those intersection points for the endpoints of the vertical lines. You can calculate the intersection directly from the endpoints you have as the result of HoughLinesP() using determinants.
In Python, a function to compute the intersection points might look like
def find_intersection(line1, line2):
# extract points
x1, y1 = line1[0]
x2, y2 = line1[1]
x3, y3 = line2[0]
x4, y4 = line2[1]
# compute determinant
Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4)) /
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4)) /
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
return (int(Px), int(Py))
Let's show how you might use this. Suppose your image looked like this:
# draw image and lines
img = np.ones((500, 500, 3)) * 255
diag1 = [(0, 0), (499, 100)]
diag2 = [(0, 499), (499, 399)]
vert1 = [(100, 0), (100, 499)]
vert2 = [(400, 0), (400, 499)]
cv2.line(img, diag1[0], diag1[1], color=[0, 0, 255])
cv2.line(img, diag2[0], diag2[1], color=[0, 0, 255])
cv2.line(img, vert1[0], vert1[1], color=[0, 255, 0])
cv2.line(img, vert2[0], vert2[1], color=[0, 255, 0])
To cut them off at the intersection, simply use the function to find those points and only draw the vertical lines at the intersection points with each diagonal line.
# get intersection points
vert1_intersect = [find_intersection(diag1, vert1), find_intersection(diag2, vert1)]
vert2_intersect = [find_intersection(diag1, vert2), find_intersection(diag2, vert2)]
# draw vertical lines from intersection points
img = np.ones((500, 500, 3)) * 255
diag1 = [(0, 0), (499, 100)]
diag2 = [(0, 499), (499, 399)]
vert1 = [(100, 0), (100, 499)]
vert2 = [(400, 0), (400, 499)]
cv2.line(img, diag1[0], diag1[1], color=[0, 0, 255])
cv2.line(img, diag2[0], diag2[1], color=[0, 0, 255])
cv2.line(img, vert1_intersect[0], vert1_intersect[1], color=[0, 255, 0])
cv2.line(img, vert2_intersect[0], vert2_intersect[1], color=[0, 255, 0])
I'm currently trying to correct the perspective of a random taken image showing a rectangle.
The perspective correction is working fine, but i want to crop the image to the target, too. Si I've tried to transform the given contour of my target by the perspective matrix (cv::Mat) and crop it with the results.
My method is currently crashing at the marked line with the following error.
OpenCV Error: Assertion failed (mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & fixedDepthMask) != 0)) in create, file /Volumes/build-storage/build/master_iOS-mac/opencv/modules/core/src/matrix.cpp, line 2430
libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /Volumes/build-storage/build/master_iOS-mac/opencv/modules/core/src/matrix.cpp:2430: error: (-215) mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & fixedDepthMask) != 0) in function create
Code
cv::Mat correctMat(cv::Mat mat, std::vector<cv::Point> contour) {
double minObjectSize = 100.0;
if (contour.size() == 4) {
cv::Rect rect = cv::boundingRect(contour);
if (rect.height < minObjectSize || rect.width < minObjectSize) {
NSLog(#"Objects size was too small: %d * %d", rect.width, rect.height);
}
else {
std::vector<Point2f> quad_pts;
std::vector<Point2f> squre_pts;
quad_pts.push_back(Point2f(contour[0].x, contour[0].y));
quad_pts.push_back(Point2f(contour[1].x, contour[1].y));
quad_pts.push_back(Point2f(contour[3].x, contour[3].y));
quad_pts.push_back(Point2f(contour[2].x, contour[2].y));
squre_pts.push_back(Point2f(rect.x, rect.y));
squre_pts.push_back(Point2f(rect.x, rect.y + rect.height));
squre_pts.push_back(Point2f(rect.x + rect.width, rect.y));
squre_pts.push_back(Point2f(rect.x + rect.width, rect.y + rect.height));
Mat transmtx = getPerspectiveTransform(quad_pts, squre_pts);
Mat transformed = Mat::zeros(mat.rows, mat.cols, CV_8UC3);
cv::line(mat, quad_pts[0], quad_pts[1], Scalar(0,0,255), 5, CV_AA, 0);
cv::line(mat, quad_pts[1], quad_pts[2], Scalar(0,0,255), 5, CV_AA, 0);
cv::line(mat, quad_pts[2], quad_pts[3], Scalar(0,0,255), 5, CV_AA, 0);
cv::line(mat, quad_pts[3], quad_pts[0], Scalar(0,0,255), 5, CV_AA, 0);
warpPerspective(mat, transformed, transmtx, mat.size());
std::vector<cv::Point2f> transformedPoints;
cv::Matx23f matrix = *transmtx.clone().ptr();
cv::transform(quad_pts, transformedPoints, matrix);
cv::Mat cropped = transformed(cv::boundingRect(transformedPoints));
fixColorOfMat(cropped);
return cropped;
}
}
return mat;
}
I do not really know what the error message is telling me so i hope somebody here could help me solving this crash.
Next Problem
I've changed the code a bit, the transform action on the point vector works now, but i'm unabled to crop the Mat by these transformed points.
std::vector<cv::Point2f> transformedPoints;
cv::Matx23f matrix = *transmtx.clone().ptr();
cv::transform(quad_pts, transformedPoints, matrix);
// Crash
cv::Mat cropped = transformed(cv::boundingRect(transformedPoints));
The error message says
OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows) in Mat, file /Volumes/build-storage/build/master_iOS-mac/opencv/modules/core/src/matrix.cpp, line 522
libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /Volumes/build-storage/build/master_iOS-mac/opencv/modules/core/src/matrix.cpp:522: error: (-215) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in function Mat
Hmm, I used memcpy() convert vector to Mat directly. Maybe you can try that on the transform part.
memcpy(Mat.data, vector.data(), vector.size()*sizeof(Point));
As long as you got the mask matrix of contour, I think you can use bitwise() to corp your target result.
Btw, I cannot explain opencv's error statement, as I have never understood it.
Im looking for suggestions to improve my algorithm to search for parts in the following image
so far I have the following
GaussianBlur(canny, canny, Size(5, 5), 2, 2);
Canny(canny, canny, 100, 200, 5);
HoughCircles(canny, Part_Centroids, CV_HOUGH_GRADIENT, 2, 30, 100, 50, 50, 60);
My edge detect output looks like this
and Im using a HoughCircle to try to find the parts. I havent been having great success though because the HoughCircle seems very fussy and often returns a circle that isnt really the best match for a part.
Any suggestions on improving this search algorithm
EDIT:
I have tried the suggestions in the comments below. The normalization made some improvements but removing the canny before hough circles altered the required settings but not the stability.
I think now that I need to do something like the hough circles with very open thresholds and then find a way to score the results. Are there any good methods to score the results of hough circle or correlate the results with the canny output for percentage of match
I thought I would post my solution as someone may find my lessons learned valuable.
I started by taking several frames and averaging them out. This solved some of the noise issues I was having while preserving the strong edges. Next I did a basic filter and canny edge to extract a decent edge map.
Scalar cannyThreshold = mean(filter);
// Canny Edge Detection
Canny(filter, canny, cannyThreshold[0]*(2/3), cannyThreshold[0]*(1+(1/3)), 3);
Next I use a cross correlation with increasing diametered templates and store matches that score over a threshold
// Iterate through diameter ranges
for (int r = 40; r < 70; r++)
{
Mat _mask, _template(Size((r * 2) + 4, (r * 2) + 4), CV_8U);
_template = Scalar(0, 0, 0);
_mask = _template.clone();
_mask = Scalar(0, 0, 0);
circle(_template, Point(r + 4, r + 4), r, Scalar(255, 255, 255), 2, CV_AA);
circle(_template, Point(r + 4, r + 4), r / 3.592, Scalar(255, 255, 255), 2, CV_AA);
circle(_mask, Point(r + 4, r + 4), r + 4, Scalar(255, 255, 255), -1);
Mat res_32f(canny.rows, canny.cols, CV_32FC1);
matchTemplate(canny, _template, res_32f, CV_TM_CCORR_NORMED, _mask);
Mat resize(canny.rows, canny.cols, CV_32FC1);
resize = Scalar(0, 0, 0);
res_32f.copyTo(resize(Rect((resize.cols - res_32f.cols) / 2, (resize.rows - res_32f.rows) / 2, res_32f.cols, res_32f.rows)));
// Strore Well Scoring Results
double minVal, maxVal;
double threshold = .25;
do
{
Point minLoc, maxLoc;
minMaxLoc(resize, &minVal, &maxVal, &minLoc, &maxLoc);
if (maxVal > threshold)
{
matches.push_back(CircleScore(maxLoc.x, maxLoc.y, r, maxVal,1));
circle(resize, maxLoc, 30, Scalar(0, 0, 0), -1);
}
} while (maxVal > threshold);
}
I filter out circles for the best match in each zone
// Sort Matches For Best Match
for (size_t i = 0; i < matches.size(); i++)
{
size_t j = i + 1;
while (j < matches.size())
{
if (norm(Point2f(matches[i].X, matches[i].Y) - Point2f(matches[j].X, matches[j].Y)) - abs(matches[i].Radius - matches[j].Radius) < 15)
{
if (matches[j].Score > matches[i].Score)
{
matches[i] = matches[j];
}
matches[j] = matches[matches.size() - 1];
matches.pop_back();
j = i + 1;
}
else j++;
}
}
Next was the tricky one. I wanted to see which part was likely to be on top. I did this by examining every set of parts that are closer then the sum of there radii, then seeing if the edges in the overlap zone are a stronger match for one over the other. Any covered circle should have little strong edges in the overlap zone.
// Layer Sort On Intersection
for (size_t i = 0; i < matches.size(); i++)
{
size_t j = i + 1;
while (j < matches.size())
{
double distance = norm(Point2f(matches[i].X, matches[i].Y) - Point2f(matches[j].X, matches[j].Y));
// Potential Overlapping Part
if (distance < ((matches[i].Radius+matches[j].Radius) - 10))
{
int score_i = 0, score_j = 0;
Mat intersect_a(canny.rows, canny.cols, CV_8UC1);
Mat intersect_b(canny.rows, canny.cols, CV_8UC1);
intersect_a = Scalar(0, 0, 0);
intersect_b = Scalar(0, 0, 0);
circle(intersect_a, Point(cvRound(matches[i].X), cvRound(matches[i].Y)), cvRound(matches[i].Radius) +4, Scalar(255, 255, 255), -1);
circle(intersect_a, Point(cvRound(matches[i].X), cvRound(matches[i].Y)), cvRound(matches[i].Radius / 3.592-4), Scalar(0, 0, 0), -1);
circle(intersect_b, Point(cvRound(matches[j].X), cvRound(matches[j].Y)), cvRound(matches[j].Radius) + 4, Scalar(255, 255, 255), -1);
circle(intersect_b, Point(cvRound(matches[j].X), cvRound(matches[j].Y)), cvRound(matches[j].Radius / 3.592-4), Scalar(0, 0, 0), -1);
bitwise_and(intersect_a, intersect_b, intersect_a);
double a, h;
a = (matches[i].Radius*matches[i].Radius - matches[j].Radius*matches[j].Radius + distance*distance) / (2 * distance);
h = sqrt(matches[i].Radius*matches[i].Radius - a*a);
Point2f p0((matches[j].X - matches[i].X)*(a / distance) + matches[i].X, (matches[j].Y - matches[i].Y)*(a / distance) + matches[i].Y);
circle(intersect_a, Point2f(p0.x + h*(matches[j].Y - matches[i].Y) / distance, p0.y - h*(matches[j].X - matches[i].X) / distance), 6, Scalar(0, 0, 0), -1);
circle(intersect_a, Point2f(p0.x - h*(matches[j].Y - matches[i].Y) / distance, p0.y + h*(matches[j].X - matches[i].X) / distance), 6, Scalar(0, 0, 0), -1);
bitwise_and(intersect_a, canny, intersect_a);
intersect_b = Scalar(0, 0, 0);
circle(intersect_b, Point(cvRound(matches[i].X), cvRound(matches[i].Y)), cvRound(matches[i].Radius), Scalar(255, 255, 255), 6);
bitwise_and(intersect_a, intersect_b, intersect_b);
score_i = countNonZero(intersect_b);
intersect_b = Scalar(0, 0, 0);
circle(intersect_b, Point(cvRound(matches[j].X), cvRound(matches[j].Y)), cvRound(matches[j].Radius), Scalar(255, 255, 255), 6);
bitwise_and(intersect_a, intersect_b, intersect_b);
score_j = countNonZero(intersect_b);
if (score_i < score_j)matches[i].Layer = matches[j].Layer + 1;
if (score_j < score_i)matches[j].Layer = matches[i].Layer + 1;
}
j++;
}
}
After that it was easy to extract the best part to pick(Im correlating to depth data as well
The blue circles are parts, the green circle is the tallest stack and red circles are part that are under other parts.
I hope this may help someone else working on similar problems
This question already has an answer here:
OpenCV's Canny Edge Detection in C++
(1 answer)
Closed 8 years ago.
I need to detect all rectangles in image.
Here is my code:
Mat PolygonsDetection(Mat src)
{
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
Mat bw;
Canny(src, bw, 50, 200, 3, true);
imshow("canny", bw);
morphologyEx(bw, bw, MORPH_CLOSE, cv::noArray(),cv::Point(-1,-1),1);
imshow("morph", bw);
vector<vector<Point>> countours;
findContours(bw.clone(), countours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
vector<Point> approx;
Mat dst = src.clone();
for(int i = 0; i < countours.size(); i++)
{
approxPolyDP(Mat(countours[i]), approx, arcLength(Mat(countours[i]), true) * 0.01, true);
if (approx.size() >= 4 && (approx.size() <= 6))
{
int vtc = approx.size();
vector<double> cos;
for(int j = 2; j < vtc + 1; j++)
cos.push_back(Angle(approx[j%vtc], approx[j-2], approx[j-1]));
sort(cos.begin(), cos.end());
double mincos = cos.front();
double maxcos = cos.back();
if (vtc == 4)// && mincos >= -0.5 && maxcos <= 0.5)
{
Rect r = boundingRect(countours[i]);
double ratio = abs(1 - (double)r.width / r.height);
line(dst, approx.at(0), approx.at(1), cvScalar(0,0,255),4);
line(dst, approx.at(1), approx.at(2), cvScalar(0,0,255),4);
line(dst, approx.at(2), approx.at(3), cvScalar(0,0,255),4);
line(dst, approx.at(3), approx.at(0), cvScalar(0,0,255),4);
SetLabel(dst, "RECT", countours[i]);
}
}
}
return dst;
}
Here is my output:
Instead 17 rectangles(16 little and 1 big) I got only 12 rectangles.
I'm new in opencv, maybe I pass wrong parameters to Canny function and morphologyEx...
So, my questions:
What's wrong I do?
How do I repair it?
What you can do is use the usual erode dilate trick. In Matlab, I usually do the following for creating a mask. This makes lines bigger and flow together.
%Create Structuring element
sel = strel('disk', 1);
% bw is the black and white (binary image) created using e.g. Otsu thresholding
bw2 = imcomplement(bw);
bw3 = imerode(bw2, sel);
bw4 = imdilate(bw3, sel);
tightmask = imcomplement(bw4);
I use this very often for finding independent components in an image
I am working on my project and I got a problem is that I have no idea how to only detect the key points from a square area which had been detector before. Below is my demo and as so far, my code would detect key points both outside and inside square: https://www.youtube.com/watch?feature=player_embedded&v=3U8V6PhMnZ8
This is my code to find the square:
const int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
gray = gray0 >= (l+1) * 255 / threshold_level;
// Find contours and store them in a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Test contours
vector<Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 3000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
This is my code to draw the square and the corner points:
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
Point p1 = squares[i][0];
Point p2 = squares[i][1];
Point p3 = squares[i][2];
Point p4 = squares[i][3];
cout<<"p1 is "<<p1<<" p2 is "<<p2<<" p3 is "<<p3<<" p4 is "<<p4<<endl;
circle(image, squares[i][0], 3, Scalar(0,0,255), 5, 8, 0);
circle(image, squares[i][1], 3, Scalar(0,255,255), 5, 8, 0);
circle(image, squares[i][2], 3, Scalar(255,0,255), 5, 8, 0);
circle(image, squares[i][3], 3, Scalar(255,255,0), 5, 8, 0);
polylines(image, &p, &n, 1, true, Scalar(0,255,0), 3, CV_AA);
This is my code to detect key points:
Mat gray_image;
vector<KeyPoint> keyPoints;
cvtColor(image, gray_image, CV_BGR2GRAY);
FastFeatureDetector fast(60);
fast.detect(gray_image,keyPoints);
drawKeypoints(image, keyPoints,image, Scalar::all(255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
You can crop the image using
Rect r(left,top,width,height); // Part of the image we are interested in
Mat roi(fullImage, r); // will create a reference to the rectangle r of the original image. Note that it is not a copy.
You have two possible solutions:
Detect all the keypoints and then check if they are inside the square.
Crop the square from the image to generate a new image and then detect keypoints there.
Cheers,