Max and min vertices in a rectangle - c++

I am re asking this question with a better explanation.
I have obtained the four points of a rectangle that I wish to perform a warp perspective transformation on. I have successfully achieved a transform on one of my images as I manually selected the points and assigned their location for the transform.
cv::Point2f src_vertices[4];
src_vertices[0] = corners[3];
src_vertices[1] = corners[1];
src_vertices[2] = corners[0];
src_vertices[3] = corners[2];
Point2f dst_vertices[4];
dst_vertices[0] = Point(0, 0);
dst_vertices[1] = Point(box.boundingRect().width-1, 0);
dst_vertices[2] = Point(0, box.boundingRect().height-1);
dst_vertices[3] = Point(box.boundingRect().width-1, box.boundingRect().height-1);
As you can see I assigned the corners to the transformation vertices manually.
As I wish to use this code on multiple slightly different images I would like a more accurate way of assigning the corners.
All the images will look similar to this:
http://imgur.com/t0cgTzr
The corners for this image are as follows:
Top left - (1106, 331),
Top right- (810, 747),
Bottom left- (825, 187),
Bottom right- (510, 537)
I have found the max and min x and y values as follows:
float X, Y;
float maxX= 0;
float minX = 10000;
float maxY= 0;
float minY = 10000;
for(int i=0; i< 4; i++){
if( corners[i].x > maxX){
maxX = corners[i].x;
}
if( corners[i].x < minX){
minX = corners[i].x;
}
if( corners[i].y > maxY){
maxY = corners[i].y;
}
if( corners[i].y < minY){
minY = corners[i].y;
}
}
This gives me:
maxX - 1106, minX - 510, maxY - 747, minY - 187.
I would like to know how to recombine the max and min values to their respective values so I can use the points to perform the transform. I am quite new to opencv so sorry if this is very obvious.

What you can do to get the corners without losing information on the other coordinate is to find the whole point. Instead of storing just float maxX= 0; you can store Point2f maxX(0,0); and modify you for loop to keep track of the point itself, not only the x coordinate.
Better yet, get rid of those loops and make use of STL
Point2f maxX =
*std::max_element(corners, corners+4, [](Point2f a, Point2f b){return a.x < b.x;});

Related

Compute dimensions (bounding box) of packing algorithm result

I'm using this binary pack code. I'm using its GrowingPacker approach. It works fine, but I have a hard time figuring out the dimensions of the final pack, I mean the bounding box of final result like (xMin, xMax, yMin, yMax). What is the best approach to find it?
Can I use the final values of x, y, w, h for this.root to compute the bounding box?
I'm not sure if it is correct, but currently I'm using a loop like that loop to compute the bounding box of final pack (I'm using C++):
#include <cfloat> // For FLT_MAX
float minX = FLT_MAX;
float minY = FLT_MAX;
float maxW = - FLT_MAX;
float maxH = - FLT_MAX;
for (const Block &block : blocks) {
if (block.fit) {
if (block.fit->x < minX)
minX = block.fit->x;
if (block.fit->y < minY)
minY = block.fit->y;
if (block.fit->w > maxW)
maxW = block.fit->w;
if (block.fit->h > maxH)
maxH = block.fit->h;
}
}
To calculate the bounding box that encompasses all the bounding boxes in your list of blocks, you need find the minimum and maximum X and Y and then use that to calculate the width and height of the overall bounding box.
Calculating the maximum width and height of the smaller boxes won't work because that will be the size of the widest and highest of the individual smaller boxes, which won't necessarily be the dimensions of the overall bounding box that encompasses all of them.
float minX = FLT_MAX;
float minY = FLT_MAX;
float maxX = - FLT_MAX;
float maxY = - FLT_MAX;
for (const Block &block : blocks) {
if (block.fit) {
if (block.fit->x < minX)
minX = block.fit->x;
if (block.fit->y < minY)
minY = block.fit->y;
if (block.fit->x + block.fit->w > maxX)
maxX = block.fit->x + block.fit->w;
if (block.fit->y + block.fit->h > maxY)
maxY = block.fit->y + block.fit->h;
}
}
// now that you have the min/max of x and y, calculate overall w and h:
float w = maxX > minX ? maxX - minX : 0.0f;
float h = maxY > minY ? maxY - minY : 0.0f;

Extract corner (extreme corner points) of quadrangle from black/white image using OpenCV C++

As part of a bigger project, I need to extract the extreme bottom corners of a quadrangle. I have an image and a corresponding binary Mat with 1s where the image is white (the image) and 0 where black (the background).
I've found ways to find the extreme left, right, bottom and top points but they may not give the points I want as the quadrangles are not perfectly rectangular.
https://www.pyimagesearch.com/2016/04/11/finding-extreme-points-in-contours-with-opencv/
Finding Top Left and Bottom Right Points (C++)
Finding extreme points in contours with OpenCV C++
The only way I can think of doing it is not very good. I'm hoping you guys can think of a better way than to just cycle though the matrix for the most bottom row then most left point and then keep points within a certain radius from that most bottom and left point.
And the same for the right, but this is not very computationally efficient.
This is an example quadruple and the corners of interest.
The ideal output is two Mats, similar to the original one, that have 1s only in the region of interest and 0s everywhere.
Any and all help will be greatly appreciated!!
There are at least two possible approaches. Both assume that you've extracted the corners:
Use approxPolyDPfunction to approximate the contour and get 4 vertices of a quadrangle.
2.Fit rectangle to the contour and then find nearest points in your contour to the bottom vertices of this rectangle.
// bin - your binarized image
std::vector<std::vector<cv::Point2i>> contours;
cv::findContours(bin, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
int biggestContourIdx = -1;
double biggestContourArea = 0;
for (int i = 0; i < contours.size(); ++i)
{
auto area = cv::contourArea(contours[i]);
if (area > biggestContourArea)
{
biggestContourArea = area;
biggestContourIdx = i;
}
}
//first solution:
std::vector<cv::Point2i> approx;
cv::approxPolyDP(contours[biggestContourIdx], approx, 30, true);
auto mean = cv::mean(approx);
std::vector<cv::Point2i> bottomCorners;
for (auto p : approx)
{
if (p.y > mean[1]) bottomCorners.push_back(p);
}
//second solution:
auto rect = cv::minAreaRect(cv::Mat(contours[biggestContourIdx]));
auto center = rect.center;
Point2f rect_points[4];
rect.points(rect_points);
std::vector<cv::Point2i> bottomRectCorners;
std::vector<double> distances(2, std::numeric_limits<double>::max());
for (int i = 0; i < 4; ++i)
{
if (rect_points[i].y > center.y)
bottomRectCorners.push_back(rect_points[i]);
}
bottomCorners.clear();
bottomCorners.resize(2);
for (auto p : contours[biggestContourIdx])
{
for (int i = 0; i < distances.size(); ++i)
{
auto dist = cv::norm(p - bottomRectCorners[i]);
if (dist < distances[i])
{
distances[i] = dist;
bottomCorners[i] = p;
}
}
}
Results of both approaches: red - first method, green second

Find intersection points for vector construct

So in my software I have two vectors. The first vector matrix stores the information of the shape of a given 3D model. So I got a vector of arrays to store the x,y,z coordinates of points.
std::vector<std::array<double, 3>> matrix;
This vector is already sorted, so that I get the contour of the model.
In the second vector boundingbox I store the information of a bounding box.
std::vector<std::array<double, 3>> boundingbox;
In this vector the first four elements describe the bounding box around the contour. To fill the outline I have placed a grid on it. The grid is in this case defined by the software based on a variable. The variable infill is set by the user at run-time. So currently my program creats the following image.
Now the next step would be to find the intersection points between the grid and the contour. My approach to this would be a typical mathematical approach.
I would use two for-loops. The first loop would be used to iterate over the grid so that each line of the grid is called once.
The second loop would be used the vector to undergo matrix. I developed a pseudo code, in which I describe my procedure.
int fillingStart; //first element of boundingbox to contain information about the grid
int n; //number of lines in the Grid.
for(size_t i=fillingStart; i<(n-1); i+2)
{
double A_x=boundingbox[j][0];
double A_y=boundingbox[j][1];
double B_x=boundingbox[j+1][0];
double B_y=boundingbox[j+1][0];
double AB_x=B_x-A_x;
double AB_y=B_y-A_y;
double intersectionpoint_y = DBL_MAX;
double intersectionpoint_x = DBL_MAX;
double intersectionpoint2_y = DBL_MAX;
double intersectionpoint2_x = DBL_MAX;
for(size_t j=0; j<(matrix.size()-1); j++)
{
double C_x = matrix[j][0];
double C_y = matrix[j][1];
double D_x = matrix[j+1][0];
double D_y = matrix[j+1][1];
double CD_x = D_x-C_x;
double CD_y = D_y-C_y;
double s = (((C_x-A_x)*(-CD_y))-((-CD_x)*(C_y-A_y)))/((AB_x*(-CD_y))-((-CD_x)*AB_y));//Cramer's rule
double t = ((AB_x*(C_y-A_y))-((C_x-A_x)*AB_y)) / ((AB_x * (-CD_y))-((-CD_x)*AB_y));//Cramer's rule
double point_x = A_x+s*AB_x;
double point_y = A_y*s*AB_y;
if(point_x < intersectionpoint_x && point_y < intersectionpoint_y)
{
intersectionpoint_x = point_x;
intersectionpoint_y = point_y;
}
else if(point_x < intersectionpoint2_x && point_y < intersectionpoint2_y)
{
intersectionpoint2_x = point_x;
intersectionpoint2_y = point_y;
}
}
intersects.push_back(std::array<double, 3>());
double q = boundingbox.size()-1;
intersects[q][0] = intersectionpoint_x;
intersects[q][1] = intersectionpoint_y;
intersects.push_back(std::array<double, 3>());
double q = boundingbox.size()-1;
intersects[q][0] = intersectionpoint2_x;
intersects[q][1] = intersectionpoint2_y;
}
With this two loops I would find the intersection points for each line of the grid and each vector (between two points) of the contour. Then I would have to find the two intersection points, closest to the grid line and store these points. The special case would be, if there is something in the contoure, like a hole. In this case I would find four points.
EDIT: Why I want to use intersection points is shown in the following figures
Here we have the contour of a rectangle. As you can see there are just a few points to describe the figure.
The next image shows the filling of the model
Because of the few points of the contour I have to calculate the intersection points of the contour and the grid.
EDIT2: I now got the code working and updated the code here, but the problem is that it saves always the same point in intersectionpoint. Thats because of the if-statement, but I cant figure out how get it working.
You could iterate over the contour, and for each two consecutive points, check if there is a line between, and if there is one, compute the intersection point.
std::vector<std::array<double, 3>> intersects;
auto it = matrix.begin();
while (it != matrix.end() - 1) {
auto &p1 = *it;
auto &p2 = *(++it);
double x;
// Check if there is a vertical line between p1 and p2
if (has_vertical_line(p1, p2, &x)) {
// The equation of the line joining p1 and p2 is:
// (p2[1] - p1[1]) / (p2[0] - p1[0]) * x + p1[0]
double y = (p2[1] - p1[1]) / (p2[0] - p1[0]) * x + p1[0];
intersects.push_back({x, y, 0.0});
}
}
Where has_vertical_line is something like:
bool has_vertical_line (std::array<double, 3> const& p1,
std::array<double, 3> const& p2,
double *px) {
double x1 = p1[0], x2 = p2[0];
if (x2 <= x1) {
std::swap(x1, x2);
}
size_t lx2 = closest_from_below(x2),
lx1 = closest_from_above(x1);
if (lx1 == lx2) {
*px = lines[lx1]; // Assuming abscissa
return true;
}
return false;
}
Where closest_from_below and closest_from_above are simple function that find the line just below / above the current abscissa (trivial since your lines are vertical).

OpenCV mass center point

I found the mass center of an irregular shape, but now I need to compute the distance to any given point.
I understand that the mc is a vector of points, but how can I find the coordinates of mc so I can calculate the distance between the mass center and some other point.Thanks
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 );
}
First you should get the point by index. Let :
int size = contours.size();
The indices are: i = 0 ... size . The point at index i is
mc[i];
The coordinates of that point can be reached by:
float xCoor = mc[i].x;
float yCoor = mc[i].y;
Of course you can read those values in a loop from i = 0 to size if you want to read all the coordinates of all the mc points.
Edit:
I assumed that you knew how to find the mass center, and was just asking how to get the coordinates. But if you want to get mass center and the distance from mass center to some other point then you could do the following:
float distance;
float totalX=0.0, totalY=0.0;
for(int i=0; i<size; i++) {
totalX+=mc[i].x;
totalY+=mc[i].y;
}
Point2f massCenter(totalX/size, totalY/size); // condition: size != 0
Point2F someOtherPoint(someXVal, someYVal);
distance = massCenter.distance(someOtherPoint);
is the distance from mass center to some other point.
Hope that helps!
mc[i].x and mc[i].y are the x and y coordinates of the point of index i.
To compute the center of mass:
cv::Point2f baricenter(0,0);
for( int i = 0; i < mc.size(); i++ )
barycenter += mc[i];
barycenter.x /= mc.size();
barycenter.y /= mc.size();
Check that you have at least one point in your vector.

How to determine Scale of Line Graph based on Pixels/Height?

I have a problem due to my terrible math abilities, that I cannot figure out how to scale a graph based on the maximum and minimum values so that the whole graph will fit onto the graph-area (400x420) without parts of it being off the screen (based on a given equation by user).
Let's say I have this code, and it automatically draws squares and then the line graph based on these values. What is the formula (what do I multiply) to scale it so that it fits into the small graphing area?
vector<int> m_x;
vector<int> m_y; // gets automatically filled by user equation or values
int HeightInPixels = 420;// Graphing area size!!
int WidthInPixels = 400;
int best_max_y = GetMaxOfVector(m_y);
int best_min_y = GetMinOfVector(m_y);
m_row = 0;
m_col = 0;
y_magnitude = (HeightInPixels/(best_max_y+best_min_y)); // probably won't work
x_magnitude = (WidthInPixels/(int)m_x.size());
m_col = m_row = best_max_y; // number of vertical/horizontal lines to draw
////x_magnitude = (WidthInPixels/(int)m_x.size())/2; Doesn't work well
////y_magnitude = (HeightInPixels/(int)m_y.size())/2; Doesn't work well
ready = true; // we have values, graph it
Invalidate(); // uses WM_PAINT
////////////////////////////////////////////
/// Construction of Graph layout on WM_PAINT, before painting line graph
///////////////////////////////////////////
CPen pSilver(PS_SOLID, 1, RGB(150, 150, 150) ); // silver
CPen pDarkSilver(PS_SOLID, 2, RGB(120, 120, 120) ); // dark silver
dc.SelectObject( pSilver ); // silver color
CPoint pt( 620, 620 ); // origin
int left_side = 310;
int top_side = 30;
int bottom_side = 450;
int right_side = 710; // create a rectangle border
dc.Rectangle(left_side,top_side,right_side,bottom_side);
int origin = 310;
int xshift = 30;
int yshift = 30;
// draw scaled rows and columns
for(int r = 1; r <= colrow; r++){ // draw rows
pt.x = left_side;
pt.y = (ymagnitude)*r+top_side;
dc.MoveTo( pt );
pt.x = right_side;
dc.LineTo( pt );
for(int c = 1; c <= colrow; c++){
pt.x = left_side+c*(magnitude);
pt.y = top_side;
dc.MoveTo(pt);
pt.y = bottom_side;
dc.LineTo(pt);
} // draw columns
}
// grab the center of the graph on x and y dimension
int top_center = ((right_side-left_side)/2)+left_side;
int bottom_center = ((bottom_side-top_side)/2)+top_side;
You are using ax^2 + bx + c (quadratic equation). You will get list of (X,Y) values inserted by user.
Let us say 5 points you get are
(1,1)
(2,4)
(4,1)
(5,6)
(6,7)
So, here your best_max_y will be 7 and best_min_y will be 1.
Now you have total graph area is
Dx = right_side - left_side //here, 400 (710 - 310)
Dy = bottom_side - top_side //here, 420 (450 - 30)
So, you can calculate x_magnitude and y_magnitude using following equation :
x_magnitude = WidthInPixels / Dx;
y_magnitude = HeightInPixels / Dy;
What I did was to determine how many points I had going in the x and y directions, and then divide that by the x and y dimensions, then divide that by 3, as I wanted each minimum point to be three pixels, so it could be seen.
The trick then is that you have to aggregate the data so that you are showing several points with one point, so it may be the average of them, but that depends on what you are displaying.
Without knowing more about what you are doing it is hard to make a suggestion.
For this part, subtract, don't add:
best_max_y+best_min_y as you want the difference.
The only other thing would be to divide y_magnitude and x_magnitude by 3. That was an arbitrary number I came up with, just so the users could see the points, you may find some other number to work better.