I am using a script to measure the size of objects using OpenCV. For my pixel_to_mm_ratio I use an Aruco Marker. This is done with exact values.
To draw boxes around the other objects I first find contours and then draw rectangles around them with cv.minAreaRect(). My problem is that width (w) and height (h) are not given as exact numbers (float) but are already rounded (integer):
rect = cv.minAreaRect(cnt)
(x, y), (w, h), angle = rect
Through the rounding of these numbers (w and h) from rectangles around contours i later get an inaccuracy when calculating the width and height in mm.
object_width = w / pixel_mm_ratio
object_height = h / pixel_mm_ratio
Is there a way to get the exact values from cv2.minAreaRect()? Or an other way to grab these values?
Thanks in advance!
Related
I would like to split an image into triangle shaped tiles (equilateral) . I have tried to generate the coordinates of a triangle using the function from https://alexwlchan.net/2016/10/tiling-the-plane-with-pillow/.
My code:
#import opencv
import math
image_path="/content/newspaper-icon-in-transparent-style-news-on-vector-25591681.jpg"
#Create Triangles
# https://alexwlchan.net/2016/10/tiling-the-plane-with-pillow/
#A horrizontal offset is added to ensure that images line up
#https://stackoverflow.com/questions/22588074/polygon-crop-clip-using-python-pil
def generate_coordinates_for_unit_triangles(image_width,image_height):
image_width=50;
image_height=50;
h=math.sin(math.pi/3)
for x in range(image_width):
for y in range(int(image_height / h)):
first_c,second_c,third_c=(x, y * h), (x+1, y * h), ((x+0.5, (y+1) * h))
first_sc, second_sc,third_sc=(x+1, y * h), (x+1.5, (y+1) * h), (x+0.5, (y+1) * h)
return first_c, second_c,third_c, first_sc, second_sc,third_sc
#return [(x, y * h), (x+1, y * h), (x+0.5, (y+1) * h)] ,[(x+1, y * h), (x+1.5, (y+1) * h), (x+0.5, (y+1) * h)]
##Generates the two triangles coordinates
first_c, second_c,third_c, first_sc, second_sc,third_sc=generate_coordinates_for_unit_triangles(50,50)
#convert image into numpy array
image_read=Image.open(image_path)
image_to_numpy=np.asarray(image_read)
shape_of_array=image_to_numpy.shape
print(shape_of_array)
mask_image=[first_c, second_c,third_c, first_sc, second_sc,third_sc]
I realized that this may not given my desired output.
The expected input and output is included below:
[Expected input and output][1]
Any guidance on how to approach the problem would be appreciated.
[1]: https://i.stack.imgur.com/vr7rV.jpg
I'm posting this as an answer because it's long, but it's not literally an answer. I'm hoping this will lead you to the next step in your design process.
Here are the design decisions you face, It's clear from your code that you can generate a list of triangle coordinates. Good, what next? You probably know the bounding box of your triangles (largest w and h) advance, so you can certainly create a set of images that contain your triangles, masked off with a black background or alpha=0 background. You could just copy the bounding rectangle to an image, then create a mask using the triangle as a path, and set the alpha to 0 outside of the mask. opencv should be able to do that.
But after you have those, what then? You talked about matching the edges. That's complicated. I suppose you could extract a vector of pixels from the three edges of each triangle, and then do some kind of approximate comparison.
If you do find matches that allow you to stitch together a composite, it is possible (assuming you have alpha=0 in the backgrounds) to blit all of these triangles back into some larger image, kind of like quilting. openvc can do block copy with alpha blending.
So, in the end, I think your problem is achievable, but it's going to be a lot of work, and probably more than we can offer here.
I am looking to OCR some digital numbers in a couple of different formats. I have a function which levels text on the horizontal plane to enable me to create bounding boxes in Opencv which works for one of my digit images. However, the second digit style is slightly leaning (italicised), which sometimes works, but I have found the decimal point mostly gets lost as it gets incorporated into one of the digits bounding rectangles.
Is there a way to align the digits based on the vertical lines of the actual digit?
Below is my working function for the horizontal plane:
def deskew(img):
img_edges = cv2.Canny(img, 100, 100, apertureSize=3)
lines = cv2.HoughLinesP(img_edges, 1, math.pi / 180.0, 100, minLineLength=20, maxLineGap=50)
angles = []
for x1, y1, x2, y2 in lines[0]:
angle = math.degrees(math.atan2(y2 - y1, x2 - x1))
angles.append(angle)
med_angle = np.median(angles)
rotated_img = ndimage.rotate(img, med_angle, cval=255)
cv2.imshow("rotated image", rotated_img)
cv2.waitKey(0)
return rotated_img
Below is the type of image/digit format I am trying to deskew and OCR, I have found through some manual entries that an angle of around 5 degrees seems to work with accurately drawing separate bounding rectangles to capture the digits and decimal points.
Below is the manually adjusted angle, showing all digits and decimal point captured, which can be OCR'd
Suppose i have image and need to find where i draw a rectangle using,
cv2.rectangle(image,(250,70),(150,45),(0,255,0),5)
on an image?
How to find particularly values of this rectangle in cv/python?
Yes, it is possible to extract the RGB value of the region in the bounding box.
Assuming you read the image with CV2, then the image is already represented as BGR. All you'd have to do is find a point in the bounding box and extract the BGR value of the point. Note I'm using BGR because that is how OpenCV reads images.
Using the bounding box vertices you used, just calculate a midpoint in the bounding box and extract the BGR value of that midpoint in the image.
# midpoint is ((y_max - y_min) // 2 + y_min, (x_max - x_min) // 2 + x_min)
point = ((70 - 45) // 2 + 45, (250 - 150) // 2 + 150)
b, g, r = image[point]
If the pixels in the bounding boxes have varying colors though, this method won't work. In that case, maybe you want to get the mean BGR values.
# image[y_min:y_max, x_min:x_max]
region = image[45:70, 150:250]
b, g, r = np.mean(region, axis=(0, 1))
I have small set of color data that I want to investigate. It is in the form of a list of RGB data.
[(255, 255, 255), (124, 144, 231), ...]
The image uses a restricted palette, and I would like to see how these colors are "distributed" by plotting them along the color wheel. As alternative, I tried histogram of individual channels, but this did not give me the insight I am interested in.
I googled and learned that HSL color more accurately maps to color wheel. However, after converting to HSL, I'm still confused. 3 pieces of data still make up the color: Hue, saturation, and luminosity. How do you map 3 piece of data onto a 2d plane?
I read about polar coordinates here: https://www.mathsisfun.com/polar-cartesian-coordinates.html. I try ignoring luminosity and plotting by treating HSL data as Polar coordinate (hue = angle, saturation = length of radius (scaled by some factor))
def polar2cartesian(hsl):
color_circle_radius = 100
radius = hsl.saturation * color_circle_radius
x = radius * math.cos(math.radians(hsl.hue))
y = radius * math.sin(math.radians(hsl.hue))
return x, y
...
for hsl in colors:
x, y = polar2cartesian(hsl)
im.point(x, y, hsl.to_rgb())
This is not correct result. As it shows same red color hue in multiple places like this example:
bad chart
What is the correct way to translate from RGB to a position on color wheel?
The problem of mapping a 3D (H, S, V) colour onto a 2D plane is a tough one to solve objectively, so I thought I'd give a crack at it and come up with results that I find pleasing.
My approach is as follows:
For every (R, G, B) pixel in the image, convert it to (H, S, V).
Convert the (H, S, V) colour to a 2D vector using the H value as the angle and the S value as the magnitude.
Find the position of that vector in our 2D output image, and only write the pixel if the value (V) is greater than the value of what was previously in that pixel. (My reasoning is that since an image is likely to have multiple pixels of similar enough colours that they appear in the same place on our colour wheel, since we are not plotting using the value, we should give higher value pixels precedence to be shown.)
Now, in code: (Entire file)
Create a table to store the largest value in every particular position
highest_value = numpy.zeros((image_size, image_size))
Convert RGB to HSV
def rgb_to_point(rgb):
hsv = colorsys.rgb_to_hsv(*rgb)
Convert that to a vector
rads = math.tau * hsv[0] - math.pi
mag = hsv[1] * (image_size/2) - 1
Convert that to a point on our image
x = int(math.cos(rads) * mag + (image_size/2))
y = int(math.sin(rads) * mag + (image_size/2))
If the value is higher, return the point, otherwise None
if(hsv[2] > highest_value[x][y]):
highest_value[x][y] = hsv[2]
return (x, y)
I called all that the rgb_to_point function, now we will use it for every pixel in our image:
for pixel in img.getdata():
c = rgb_to_point(pixel)
if(c):
imgo.putpixel(c, pixel)
if(c) determines whether the value was higher, since c is None when it wasn't.
Here's the results:
Note: Part of the reason I am dealing with value like this is because the alternatives I thought of were not as good. Ignoring value completely lead to darker pixels turning up on the output image, which usually lead to an ugly wheel. Turning the value up to 1 for every output pixel lead to very generic looking wheels that didn't really give a good idea of the original input image.
I've extracted a Circle shaped mask from an image in OpenCV. I used the following code for the same:
H, W = img.shape
x, y = np.meshgrid(np.arange(W), np.arange(H))**
d2 = (x - xc)**2 + (y - yc)**2**
mask = d2 < r **2**
And, used the mask value to find the average color outside the circle.
outside = np.ma.masked_where(mask, img)**
average_color = outside.mean()**
I want to extract an Ellipse from an image in the same above process in OpenCV Python.
Thank You.
Drawing Ellipse
To draw the ellipse, we need to pass several arguments. One argument is the center location (x,y). Next argument is axes lengths (major axis length, minor axis length). angle is the angle of rotation of ellipse in anti-clockwise direction. startAngle and endAngle denotes the starting and ending of ellipse arc measured in clockwise direction from major axis. i.e. giving values 0 and 360 gives the full ellipse. For more details, check the documentation of cv2.ellipse(). Below example draws a half ellipse at the center of the image.
cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1)
Taken from Miki's Link in the Question Comments