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
Related
I tried everything available here related to this problem, but none gave me the exact same results, so I'd like to know if there exist one? and if so, how can I achieve it?
Let's talk about a simple example and hopefully find a way to get the same result in Matlab as well as in OpenCV:
Matlab:
test = [ 10 20 10 ; 20 10 10 ; 30 30 30]
im_rot = imrotate(double(test), -45, 'bilinear', 'crop');
Result:
im_rot =
11.7157 14.1421 11.7157
26.2132 10.0000 12.0711
17.5736 24.1421 5.8579
OpenCV:
double data[9]{ 10, 20, 10, 20, 10, 10, 30, 30, 30 };
Mat test = Mat(Size(3, 3), CV_64F, data);
What lines of code can get the exact same result as the one above?
Edit:
Tried the following:
void matlabImrotate(Mat& src, double angle, int interpolationMethod, Mat& dst)
{
// src: https://stackoverflow.com/questions/38715363/how-to-implement-imrotate-of-matlab-in-opencv
try
{
// Special Cases
if (fmod(angle, 360.0) == 0.0)
dst = src;
else {
Point2f center(src.cols / 2.0F, src.rows / 2.0F);
Mat rot = cv::getRotationMatrix2D(center, angle, 1.0);
cout << "center: " << center << endl;
// determine bounding rectangle
Rect bbox = RotatedRect(center, src.size(), angle).boundingRect();
// adjust transformation matrix
// cout << "bbox.size: " << bbox.size() << endl;
rot.at<double>(0, 2) += ((bbox.width ) / 2.0 - center.x );
rot.at<double>(1, 2) += ((bbox.height ) / 2.0 - center.y );
warpAffine(src, dst, rot, bbox.size(), interpolationMethod);
}
}
}
matlabImrotate(test, -45, INTER_LINEAR, res);
Result:
[0, 0, 0, 1.40625, 0, 0;
0, 0, 6.6796875, 11.69921875, 6.6796875, 0;
0, 8.7890625, 24.53125, 13.41796875, 14.53125, 2.9296875;
0, 2.8125, 23.4375, 20, 7.8125, 0.9375;
0, 0, 2.8125, 18.310546875, 1.875, 0;
0, 0, 0, 0.263671875, 0, 0]
Also tried:
void matlabImrotate2(Mat& src, double angle, int interpolationMethod, Mat& dst, int border=0)
{
// src: https://stackoverflow.com/questions/14870089/how-to-imrotate-with-opencv-2-4-3
Mat bordered_source;
int top, bottom, left, right;
top = bottom = left = right = border;
copyMakeBorder(src, bordered_source, top, bottom, left, right, BORDER_CONSTANT, cv::Scalar());
Point2f src_center(bordered_source.cols / 2.0F, bordered_source.rows / 2.0F);
Mat rot_mat = getRotationMatrix2D(src_center , angle, 1.0);
warpAffine(bordered_source, dst, rot_mat, bordered_source.size());
return true;
}
matlabImrotate2(test, -45, INTER_LINEAR, res);
Result:
[9.375, 17.28515625, 17.28515625;
23.4375, 21.09375, 11.09375;
2.8125, 23.4375, 15.625]
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 11 months ago.
Locked. There are disputes about this question’s content being resolved at this time. It is not currently accepting new answers or interactions.
I am new to c++, I started with it today and I got an error that I can't solve myself.
When I run my code it sometimes works fine, and I sometimes get an error after a few seconds.
The errors that I have gotten are:
"corrupted size vs. prev_size Aborted (core dumped)"
"segmentation fault (core dumped)"
This is my code:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <random>
using namespace cv;
using namespace std;
using chrono::duration;
using chrono::duration_cast;
using chrono::high_resolution_clock;
using chrono::milliseconds;
class Product
{
public:
Point location = Point(-80, 0);
Scalar color;
Product(int given_y, Scalar given_color)
{
location.y = given_y;
color = given_color;
cout << "Product created" << endl;
}
Mat update(double speed, Mat img, double dt)
{
location.x += speed* dt;
circle(img, location, 25, Scalar(255, 255, 255), FILLED, LINE_8);
circle(img, location, 20, color, FILLED, LINE_8);
putText(img, "12", location + Point(-17, +7), FONT_HERSHEY_DUPLEX, .8, Scalar(255, 255, 255), 1, LINE_AA);
return (img);
}
};
int main(int argc, char **argv)
{
// Constant variables
const int kScreenWidth = 1920;
const int kScreenHeight = 1080;
const int kUpdateFpsMilliseconds = 600;
const String kWindowName = "Moving dots";
const Mat kBlackImg(kScreenHeight, kScreenWidth, CV_8UC3, Scalar(0, 0, 0));
// Variables
Scalar red = Scalar(255, 0, 0);
Scalar green = Scalar(0, 255, 0);
Scalar blue = Scalar(0, 0, 255);
Scalar cyan = Scalar(0, 255, 255);
Scalar magenta = Scalar(255, 0, 255);
Scalar yellow = Scalar(255, 255, 0);
Scalar black = Scalar(0, 0, 0);
Scalar white = Scalar(255, 255, 255);
vector<Product> products;
Mat img;
double speed = .2;
double fps;
int frame_counter = 0;
int loop_dt;
int time_since_fps_update = 0;
int counter = 0;
bool remove_product = false;
bool full_screen = false;
// Random number generators
random_device rd_seed;
mt19937 rng(rd_seed());
uniform_int_distribution<mt19937::result_type> rd_screen_height(50, kScreenHeight - 50);
uniform_int_distribution<mt19937::result_type> rd_new_product(0, 100);
namedWindow(kWindowName, WINDOW_NORMAL);
setWindowProperty(kWindowName, WND_PROP_FULLSCREEN, WINDOW_FULLSCREEN);
auto loop_start = high_resolution_clock::now();
while (true)
{
// Black image
Mat img = kBlackImg.clone();
// Add new products
if (rd_new_product(rng) < .1 * loop_dt)
{
products.push_back(Product(rd_screen_height(rng), red));
}
// Update products
if (products.size() > 0)
{
for (int i = 0; i <= products.size(); i++)
{
img = products[i].update(speed, img, loop_dt);
}
}
// Show text and border
stringstream temp;
temp << "FPS: " << fps << " Products: " << products.size();
putText(img, temp.str(), Point(18, 38), FONT_HERSHEY_DUPLEX, .8, white, 1, LINE_AA);
rectangle(img, Point(6, 6), Point(kScreenWidth - 6, kScreenHeight - 6), white, 12);
rectangle(img, Point(6, 6), Point(kScreenWidth - 6, 60 - 6), white, 12);
rectangle(img, Point(6, 6), Point(kScreenWidth - 6, kScreenHeight - 6), cyan, 3);
rectangle(img, Point(6, 6), Point(kScreenWidth - 6, 60 - 6), cyan, 3);
// Update screen
cvtColor(img, img, COLOR_RGB2BGR);
imshow(kWindowName, img);
// Exit script
if (waitKey(1) >= 0)
{
break;
};
// Handle FPS counter
frame_counter++;
loop_dt = duration_cast<milliseconds>(high_resolution_clock::now() - loop_start).count();
loop_start = high_resolution_clock::now();
time_since_fps_update += loop_dt;
if (time_since_fps_update > kUpdateFpsMilliseconds)
{
fps = (double)frame_counter / (double)time_since_fps_update * 1000;
time_since_fps_update -= kUpdateFpsMilliseconds;
frame_counter = 0;
}
}
destroyWindow(kWindowName);
return 0;
}
I think that it might have something to do with the color scalars or with the products vector.
Any help is appreciated!
The main culprit is likely:
for (int i = 0; i <= products.size(); i++)
{
img = products[i].update(speed, img, loop_dt);
}
That should be < products.size(). Vectors use indices from 0 to size()-1, like pretty much all other std containers.
I have problems regarding vector subscript out of range.Eyes[0] refers to left eye while eyes[1] refer to right eye.The function of the code below allows an OpenCV function called eyecascade to track eyes and I have edited it to output the coordinates of the eyes,however for the right eye of eyes[1],I believe it causes the vector to be out of range.
vector<Rect> eyes;
eyeCascade.detectMultiScale(faceROI, eyes);
unsigned int x = eyes.size();
{
Point eye_center(eyes[0].x + eyes[0].width / 2, eyes[0].y + eyes[0].height / 2);
int radius = cvRound((eyes[0].width + eyes[0].height)*0.25);
circle(frame, eye_center, radius, Scalar(255, 0, 0), 5);
printf("eyes0_x;%d", eyes[0].x);
printf(" eyes0_y;%d\n",eyes[0].y );
circle(frame, eye_center, radius, Scalar(255, 0, 0), 5);
printf("eyes1_x;%d", eyes[(1)].x);
printf(" eyes1_y;%d\n", eyes[(1)].y);
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 done with detecting vehicles in OpenCV C++ but i need to count them, not counting cars in "frame". i need to count all the cars passed. This is my detecting code, what am i suppose to do?
arac_cascade.detectMultiScale(
frame_gray, arac, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(20, 20));
for (int i = 0; i < arac.size(); i++)
{
Point pt1(arac[i].x + arac[i].width, arac[i].y + arac[i].height);
Point pt2(arac[i].x, arac[i].y);
rectangle(frame, pt1, pt2, cvScalar(0, 255, 0, 0), 1, 8, 0);
Mat aracROI = frame_gray(arac[i]);
}
After detecting, arac.size() is the total number of the detected vehicles in current frame. If you need to count the number of all the frames, you can simply sum them together like:
int num_total = 0;
for (-every-frame-){
// detection code...
num_total += arac.size();
}