Unable to get the boundary of an object using opencv minAreaRect - python

I am trying to draw a minimum bounding rectangle around an object in an image using openCV,
the cv2.minAreaRect is returning the rectangle of correct size but its orientation is off
Following is my codeSnippet
The following screentshot shows the image i am working
In the next screenshot it shows the image with the detected border
According to the opencv documentation linked here: https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html
this should work

np.where() returns things in the normal array index (row, column) order, but OpenCV expects points in (x, y) order, which is opposite to (row, column). This has the effect that you are flipping the points about the diagonal of the image.
Simply reverse the points by swapping the two columns. Better yet would just be to be more explicit with variables and not do everything on one line:
y, x = np.where(binary == 0)
coords = np.column_stack((x, y))

Related

How to draw a rectangle around keypoints of a pose?

I have an image of a hand and I passed it through a pre trained hand pose estimation model and got this output. Output
Task
Now I want to draw a rectangle around the hand to carry out some tasks. How do I draw a rectangle around a hand with just using those keypoints(not using another model).
In case if you're interested to know why I need that rectangle.
I want to normalize all the points inside the rectangle to range of (0,1) by dividing each point with width and height of rectangle and top left points to 0s and bottom points to 1s.
I haven't used OpenCV in quite a while, hence the simplest approach I can think of without relying on it's methods would be to use the list of keypoint locations and find the min/max x and y values.
That is, loop through the list of points (which I assume each have a given x and y) and store the minimum x and y, as well as maximum x and y. To do this you will want to initialise the variables according to your image's size, or alternatively store all x and y in their own separate lists and perform min and max functions accordingly.
The rectangle therefore is defined by the 2 corner points of (x_min, y_min) and (x_max, y_max), which you can also extract width and height from using subtraction. Make sure your drawing reference matches up with the xy-reference of the points. To actually draw the rectangle you can refer to the code here: https://docs.opencv.org/master/dc/da5/tutorial_py_drawing_functions.html

openCV perspective transformation doesn't work as expected

I have an image with a text.
This is the image:
What I'm trying to do is to straighten the text, using perspective transformation.
The red dots in the corners are the detected boundaries.
This is more or less my code (hard-coded, for simplicity):
old_pts=np.float32([[2,41],[37,965],[1389,1121],[1389,0]])
bor=cv2.boundingRect(old_pts) #bounding_rect
ul=[bor[0], bor[1]] #upper left
ur=[bor[0], bor[1]+bor[3]] #upper right
br=[bor[0]+bor[2],bor[1]+bor[3]] #bottom right
bl=[bor[0]+bor[2],bor[1]] #bottom left
new_pts=np.float32([ul,ur,br,bl]) #new pts=[[2,0],[2,1122],[1390,1122],[1390,0]]
M = cv2.getPerspectiveTransform(old_pts,new_pts)
transformed_img = cv2.warpPerspective(new_img,M,(bor[3],bor[2])) #bor[3] and bor[4] are the bounding rect height&width.
transforemed_img=transformed_img.astype(int)
cv2.imwrite('transformed.png',transformed_img)
Now, the result I'm getting is this:
Why am I not getting a nice, straightened rectangle??
Any help will be appreciated!
Take a look at your points:
You have :
old_pts=np.float32([[2,41],[37,965],[1389,1121],[1389,0]])
and:
new_pts=np.float32([ul,ur,br,bl]) #new pts=[[2,0],[2,1122],[1390,1122],[1390,0]]
But, OpenCV manages POINTS as (x,y) values, yours are in (y,x)... I know this is confusing, since the matrix manipulation is done with (y,x) notation... The thing is that OpenCV sees the matrix manipulation as the rows and columns like a matrix, but points are seen as Cartesian coordinates...
In conclusion, try flipping the axes for the points and check the results.

OpenCV Python creating bounding box or enclosing circle/polygon around scattered points

I am working on object detection for collision avoidance using OpenCV Python on a small quad. First I need to detect objects using Optical Flow Pyramid (LK)(OpenCV) approach. I was able to track points on the image ROI as shown in the image points tracked using opticalflow lk pyr
I need to create a bounding box or enclosing convexHull or some polygonal shape as shown below to show that these are the detected objects red lines are which I drew . Ignoring isolated points, only points at certain distance to each other must be taken.
If any one could help me or provide me with your ideas it will be useful.
If my question is not precise or to vast please let me know
you can get minimum and maximum x,y values by looping through the cluster labels. and then can draw rectangles using those 4 points for each cluster.
following code will help you.
ret, label, center = cv2.kmeans(Z, 10, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
for i in label.ravel():
x_values = []
y_values = []
count = Z[label.ravel()==i]
for x,y in count:
x_values.append(x)
y_values.append(y)
min_x = min(x_values)
min_y = min(y_values)
max_x = max(x_values)
max_y=max(y_values)
cv2.rectangle(frame, (max_x, max_y), (min_x, min_y), (0, 255, 0), 3)
To get the bounding box draw a rectangle around p1(x_min, y_min) and p2(x_max, y_max), where x_min/max and y_min/max denote the minimum and maximum x and y coordinates of a point cluster.
So as you already have your points the first step is to form clusters of close points and get rid of outliers.
Please research cluster analysis to find out how to do this. I'm not willing to write a book here. https://en.wikipedia.org/wiki/Cluster_analysis might give you a first idea.
The problem involves two steps:
You need to perform clustering over the key-points. I suggest taking a look at scikit-learn clustering.
Build a bounding rectangle or any other type convex envelope over each of the clusters. For this open CV provides the function BoundingRect(link to the documentation) which provides the desired functionality: The function calculates and returns the minimal up-right bounding rectangle for the specified point set.

Python connected components with pixel list

In matlab you can use
cc = bwconncomp(bimg);
pixels = cc.PixelIdxList{i}
To get pixel list of each connected components. What's the python equivalent?
I tried
from skimage import measure
label = measure.label(bimg)
To get the labels, however, this does not come with pixel list.
Any suggestions?
The regionprops function in scikit-image returns the property "coords", an (N, 2) ndarray coordinate list (row, col) of the region. I ran into the same problem and am using this to get the pixel list from the label array.
To get pixel list of a connected component with some_label (e.g. some_label=1), if you have a labeled image:
pixels = numpy.argwhere(labeled_image == some_label)
See numpy.argwhere.
And see also this similar question.

OpenCV Python HoughCircles: Circles detected outside of image boundary

I am using the OpenCV HoughCircles method in Python as follows:
circles = cv2.HoughCircles(img,cv.CV_HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
This seems to work quite well. However, one thing I noticed is that it detects circles which can extend outside of the image boundaries. Does anyone know how I can filter these results out?
Think of each circle as being bounded inside a square of dimensions 2r x 2r where r is the radius of the circle. Also, the centre of this box is located at (x,y) which also corresponds to where the centre of the circle is located in the image. To see if the circle is within the image boundaries, you simply need to make sure that the box that contains the circle does not go outside of the image. Mathematically speaking, you would need to ensure that:
r <= x <= cols-1-r
r <= y <= rows-1-r # Assuming 0-indexing
rows and cols are the rows and columns of your image. All you really have to do now is cycle through every circle in the detected result and filter out those circles that go outside of the image boundaries by checking if the centre of each circle is within the two inequalities specified above. If the circle is within the two inequalities, you would save this circle. Any circles that don't satisfy the inequalities, you don't include this in the final result.
To put this logic to code, do something like this:
import cv # Load in relevant packages
import cv2
import numpy as np
img = cv2.imread(...,0) # Load in image here - Ensure 8-bit grayscale
final_circles = [] # Stores the final circles that don't go out of bounds
circles = cv2.HoughCircles(img,cv.CV_HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0) # Your code
rows = img.shape[0] # Obtain rows and columns
cols = img.shape[1]
circles = np.round(circles[0, :]).astype("int") # Convert to integer
for (x, y, r) in circles: # For each circle we have detected...
if (r <= x <= cols-1-r) and (r <= y <= rows-1-r): # Check if circle is within boundary
final_circles.append([x, y, r]) # If it is, add this to our final list
final_circles = np.asarray(final_circles).astype("int") # Convert to numpy array for compatability
The peculiar thing about cv2.HoughCircles is that it returns a 3D matrix where the first dimension is a singleton dimension. To eliminate this singleton dimension, I did circles[0, :] which will result in a 2D matrix. Each row of this new 2D matrix contains a tuple of (x, y, r) and characterizes where a circle is located in your image as well as its radius. I also converted the centres and radii to integers so that if you decide to draw them later on, you will be able to do it with cv2.circle.
you could, add a function which will take the center and the radius of the circle add them up/and subtract and check if this will result outside the boundaries of your image.

Categories