Count non-zero pixels in area rotated rectangle - python-3.x

I've got a binary image with an object and a rotated rectangle over it, found with cv2.findContours and cv2.minAreaRect. The image is normalized to [0;1]
What is the most efficient way to count non-zero area within the bounding rectangle?

Create new zero values Mat that has the same size of your original image.
Draw your rotated rectangle on it in (fillConvexPoly using the RotatedRect vertices).
Bitwise_and this image with your original mask
apply findnonzero function on the result image
You may also apply the previous steps on ROI of the image since you have the bounding box of your rotated rectangle.

According to Humam Helfawi's answer I've tuned a bit suggested steps, so the following code seems doing what i need:
rectangles = [(cv2.minAreaRect(cnt)) for cnt in contours]
for rect in rectangles:
rect = cv2.boxPoints(rect)
rect = np.int0(rect)
coords = cv2.boundingRect(rect)
rect[:,0] = rect[:,0] - coords[0]
rect[:,1] = rect[:,1] - coords[1]
area = cv2.contourArea(rect)
zeros = np.zeros((coords[3], coords[2]), np.uint8)
cv2.fillConvexPoly(zeros, rect, 255)
im = greyscale[coords[1]:coords[1]+coords[3],
coords[0]:coords[0]+coords[2]]
print(np.sum(cv2.bitwise_and(zeros,im))/255)

contours is a list of points. You can fill this shape on an empty binary image with the same size using cv2.fillConvexPoly and then use cv2.countNonZero or numpy.count_nonzero to get the number of occupied pixels.

Related

how to improve edge smoothness of an image rotated using pillow

I have this image
And I want to rotate it, and keep a smooth looking edge.
I have tried this approach below which adds some transparent borders to the image, to allow for the interpolation of the rotation to sample the transparent padding and the opaque image intensities when it renders the edge.
img = Image.open("sunset200x100.jpg")
im_array = np.asarray(img)
w, h = img.size
padding = 4
new_padded_size = (w+padding, h+padding)
img = img.convert('RGBA') # converting to RGBA adds transparency to the areas that aren't opaque
img = ImageOps.pad(img, size=new_padded_size)
im_array_rgba_padded = np.asarray(img)
rotated_im = img.rotate(56, expand=True, resample=PIL.Image.BICUBIC)
as_array = np.asarray(rotated_im)
#rotated_im.show()
rotated_im.save("rotated_sunset200x100_padded_with_2px.png")
However, it doesn't seem to do interpolation on the left, and right sides of the image. Inspecting the im_array_rgba_padded, I see that the first line, and last line of pixels have been made all black, however the left and right haven't got the same zero padding.
So the result ends up looking like this:-
wondering how I can get the padding into the left and right aswell, using the pad function, so that the left and right edges also look smooth ?? or why it is that the padding is not applied to the left and right aswell ?
you can use this change your code :
mg = Image.open("sunset200x100.jpg")
im_array = np.asarray(img)
w, h = img.size
print((w,h))
padding =4
new_padded_size = (w+padding, h+padding)
img = img.convert('RGBA') # converting to RGBA adds transparency to the areas that aren't opaque
# img = ImageOps.pad(img, size=new_padded_size)
img = ImageOps.expand(img,new_padded_size,fill='black')
im_array_rgba_padded = np.asarray(img)
rotated_im = img.rotate(56, expand=True, resample=PIL.Image.BICUBIC)
as_array = np.asarray(rotated_im)
rotated_im.show()
this work in windows10,python 3.9, pillow 8.3.
for more information go to this and pillow/ImageOps

How to find the direction of triangles in an image using OpenCV

I am trying to find the direction of triangles in an image. below is the image:
These triangles are pointing upward/downward/leftward/rightward. This is not the actual image. I have already used canny edge detection to find edges then contours and then the dilated image is shown below.
My logic to find the direction:
The logic I am thinking to use is that among the three corner coordinates If I can identify the base coordinates of the triangle (having the same abscissa or ordinates values coordinates), I can make a base vector. Then angle between unit vectors and base vectors can be used to identify the direction. But this method can only determine if it is up/down or left/right but cannot differentiate between up and down or right and left. I tried to find the corners using cv2.goodFeaturesToTrack but as I know it's giving only the 3 most effective points in the entire image. So I am wondering if there is other way to find the direction of triangles.
Here is my code in python to differentiate between the triangle/square and circle:
#blue_masking
mask_blue=np.copy(img1)
row,columns=mask_blue.shape
for i in range(0,row):
for j in range(0,columns):
if (mask_blue[i][j]==25):
mask_blue[i][j]=255
else:
mask_blue[i][j]=0
blue_edges = cv2.Canny(mask_blue,10,10)
kernel_blue = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(2,2))
dilated_blue = cv2.dilate(blue_edges, kernel)
blue_contours,hierarchy =
cv2.findContours(dilated_blue,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for cnt in blue_contours:
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt,True)
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
if(12<(perimeter*perimeter)/area<14.8):
shape="circle"
elif(14.8<(perimeter*perimeter)/area<18):
shape="squarer"
elif(18<(perimeter*perimeter)/area and area>200):
shape="triangle"
print(shape)
print(area)
print((perimeter*perimeter)/area,"\n")
cv2.imshow('mask_blue',dilated_blue)
cv2.waitKey(0)
cv2.destroyAllWindows()
Source image can be found here: img1
Please help, how can I found the direction of triangles?
Thank you.
Assuming that you only have four cases: [up, down, left, right], this code should work well for you.
The idea is simple:
Get the bounding rectangle for your contour. Use: box = cv2.boundingRect(contour_pnts)
Crop the image using the bounding rectangle.
Reduce the image vertically and horizontally using the Sum option. Now you have the sum of pixels along each axis. The axis with the largest sum determines whether the triangle base is vertical or horizontal.
To identify whether the triangle is pointing left/right or up/down: you need to check whether the bounding rectangle center is before or after the max col/row:
The code (assumes you start from the cropped image):
ver_reduce = cv2.reduce(img, 0, cv2.REDUCE_SUM, None, cv2.CV_32F)
hor_reduce = cv2.reduce(img, 1, cv2.REDUCE_SUM, None, cv2.CV_32F)
#For smoothing the reduced vector, could be removed
ver_reduce = cv2.GaussianBlur(ver_reduce, (3, 1), 0)
hor_reduce = cv2.GaussianBlur(hor_reduce, (1, 3), 0)
_,ver_max, _, ver_col = cv2.minMaxLoc(ver_reduce)
_,hor_max, _, hor_row = cv2.minMaxLoc(hor_reduce)
ver_col = ver_col[0]
hor_row = hor_row[1]
contour_pnts = cv2.findNonZero(img) #in my code I do not have the original contour points
rect_center, size, angle = cv2.minAreaRect(contour_pnts )
print(rect_center)
if ver_max > hor_max:
if rect_center[0] > ver_col:
print ('right')
else:
print ('left')
else:
if rect_center[1] > hor_row:
print ('down')
else:
print ('up')
Photos:
Well, Mark has mentioned a solution that may not be as efficient but perhaps more accurate. I think this one should be equally efficient but perhaps less accurate. But since you already have a code that finds triangles, try adding the following code after you have found triangle contour:
hull = cv2.convexHull(cnt) # convex hull of contour
hull = cv2.approxPolyDP(hull,0.1*cv2.arcLength(hull,True),True)
# You can double check if the contour is a triangle here
# by something like len(hull) == 3
You should get 3 hull points for a triangle, these should be the 3 vertices of your triangles. Given your triangles always 'face' only in 4 directions; Y coordinate of the hull will have close value to the Y coordinate of the centroid for triangle facing left or right and whether it's pointing left or right will depend on whether hull X is less than or greater than centroid X. Similarly use hull and centroid X and Y for triangle pointing up or down.

python3 reportlab drawImage High to center

c = canvas.Canvas('data.pdf', pagesize= [width*inch,height*inch])
c.drawImage('dataptah',x,y, width,height)
c.save()
I can't height center the picture,
so I need know x and y unit,
or put something.
First of all, you can use units for the x, y, width, and height values in the drawImage call, just as you did for the pagesize. Thus, if you know the aspect ratio of your image, you can calculate these values for the exact centered position.
The reference documentation mentions two other parameters, that could be helpful:
preserveAspectRation=True keeps the aspect ratio of the image even if the box specified with x, y, width, height has a different aspect ratio.
anchor='c' specifies the anchor position of the image, center in this case.
Thus, if you add these two parameters and center the box on the page, then your image should appear centered as well. Here is an example:
c.drawImage('dataptah',
width/4*inch, height/4*inch,
width/2*inch, height/2*inch,
preserveAspectRatio=True, anchor='c'
)

How to set relative position (oCoords) in FabricJs?

I have a Text in fabricJs. I set top and left.
This sets the aCoords properly to those values.
However the oCoords dont match. And the Text is not displayed at the right position.
I suspect that I need to set to oCoords somehow. So that the Text is displayed at the right pixel coordinates (top & left) on the canvas.
aCoords and oCoords are two different things and should not be in sync.
In your comment you speak about scaled canvas.
Top and Left are 2 absolute values that represent the position of the object on the canvas. This position match with the canvas pixels when the canvas has a identity transform matrix.
If you apply a zoom, this coordinates diverge.
To get the position of pixel 300,100 of the scaled canvas on the unscaled canvas, you need to apply some basic math.
1) get the transform applied to the canvas
canvas.viewportTransform
2) invert it
var iM = fabric.util.invertTransform(canvas.viewportTransform)
3) multiply the wanted point by this matrix
var point = new fabric.Point(myX, myY);
var transformedPoint = fabric.util.transformPoint(point, iM)
4) set the object at that point.

androidplot background image shift

I'm trying to separate the background of the graph grid in 3 areas using this code:
int[] data = {0xff000000, 0x80008000, 0xff000000};
bgBitmap = Bitmap.createBitmap(data, 1, 3, Bitmap.Config.ARGB_8888);
RectF rect = plot.getGraphWidget().getGridRect();
BitmapShader myShader = new BitmapShader(
Bitmap.createScaledBitmap(bgBitmap, 1, (int) rect.height(), false),
Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
plot.getGraphWidget().getGridBackgroundPaint().setShader(myShader);
So scaling a 3 pixel bitmap to the graph height and repeating it over the whole domain area.
However the resulting graph show that the background seems to be shifted up a bit.
It looks like the shift size is about equal to the domain label height.
How can I fix this?
Hm cannot post picture because of 'reputation' sigh.
Link to the example graph: http://marcel.mesa.nl/androidplot.png
I think you're running into the issue mentioned near the end of this thread. Essentially, the origin of the shader is the top-left corner of the screen, not the top-left corner of component for which the background is being drawn using the shader. The solution is to translate to the top-left point of the graphWidget like this:
RectF rect = plot.getGraphWidget().getGridRect();
Matrix m = new Matrix();
m.setTranslate(rect.left, rect.top);
shader.setLocalMatrix(m); // where shader is your shader instance

Resources