I am implementing "Bubbles algorithm" for classification recognition analysis.
in this algorithm, we have to make mask with gaussian circles.
the center of circles would be 1(255) and it will decrease over the radius which will be 0.
i have problem when i put circles on each other for creating the mask, it will put a black line between circles and i can not remove it.
this is my code:
def make_gaussian(circle_center, Gaussian_base):
for t in range(circle_center[1] - radius, circle_center[1] + radius):
for tt in range(circle_center[0] - radius, circle_center[0] + radius):
distance = int(compute_distance(tt, circle_center[0], t, circle_center[1]))
if distance < radius :
value = int(((radius - distance) * (1 / radius)) * 255)
if Gaussian_base[t][tt].mean() < value:
Gaussian_base[t][tt] = [value, value, value]
return Gaussian_base
Gaussian_base = np.zeros((height, width, 3), np.uint8)
for s in centers:
#xmin, ymin, xmax, ymax
Gaussian_base = make_gaussian(s, Gaussian_base)
cv2.imwrite('gaussianMask.jpg', Gaussian_base)
you can use this list as centers:
centers = [(139, 102), (223, 193), (94, 385), (205, 301), (90, 147), (190, 209), (45, 349), (193, 259), (110, 343), (159, 99)]
and the output is like this:
enter image description here
the problem is the black line(area) between two overlapped circles which should be removed and the joint area should be continous.
Try this...
def make_gaussian(circle_center, Gaussian_base):
for t in range(circle_center[1] - radius, circle_center[1] + radius):
for tt in range(circle_center[0] - radius, circle_center[0] + radius):
distance = int(compute_distance(tt, circle_center[0], t, circle_center[1]))
if distance < radius :
value = int(((radius - distance) * (1 / radius)) * 200)
#if Gaussian_base[t][tt].mean() < value:
Gaussian_base[t][tt] += np.array([value, value, value], dtype=np.uint8)
return Gaussian_base
the "black line" in your posted image between balls is an optical illusion. it is a local minimum and your eyes recognize that.
open your image in an image editor. take the color picker. move along a ball and into the "black line". the indicated color/brightness moves smoothly. it doesn't dip and it's not black.
Related
So I am trying to implement a method from this paper. I am stuck at the part where I have to find the angle between the major axis of the lesion’s best-fit ellipse and the x-axis of the coordinate system.
Here is the sample image:
Here is what I got so far:
Is it possible to find that angle? And after the angle has been found, I have to flip the RoI along x-axis by the angle.
UPDATE ----------
Google drive link to Roi Image: RoI image
Implementing method step by step based on the paper.
First, I should recenter the RoI to the center of the image coordinate. In the paper, they centered the RoI using its centroid. I manage to do it based on this code I found in this answer. The result is fine if my RoI is small and not touching the image border. But if I have large image the result is really bad. So I ended up centering the RoI using boundingRect. Here is the result of centering:
Code for centering RoI:
import math
import cv2
import numpy as np
import matplotlib.pyplot as plt
# read image
cont_img = cv2.imread(r"C:\Users\Pandu\Desktop\IMD064_lesion.bmp", 0)
cont_rgb = cv2.cvtColor(cont_img, cv2.COLOR_GRAY2RGB)
# fit ellipse and find ellipse properties
hh, ww = cont_img.shape
contours, hierarchy = cv2.findContours(cont_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
ellipse = cv2.fitEllipse(contours[0])
(xc, yc), (d1, d2), angle = ellipse
# centering by centroid
half_width = int(ww/2)
half_height = int(hh/2)
offset_x = (half_width-xc)
offset_y = (half_height-yc)
T = np.float32([[1, 0, offset_x], [0, 1, offset_y]])
centered_by_centroid = cv2.warpAffine(cont_img.copy(), T, (ww, hh))
plt.imshow(centered_by_centroid, cmap=plt.cm.gray)
# centering by boundingRect
# This centered RoI is (L)
x, y, w, h = cv2.boundingRect(contours[0])
startx = (ww - w)//2
starty = (hh - h)//2
centered_by_boundingRect = np.zeros_like(cont_img)
centered_by_boundingRect[starty:starty+h, startx:startx+w] = cont_img[y:y+h, x:x+w]
plt.imshow(centered_by_boundingRect, cmap=plt.cm.gray)
Second, after centering the RoI, I should find the orientation angel and rotate the RoI based on that angel and then flip . Using code from this answer. (is this the correct way to rotate the RoI?):
# find ellipse properties of centered RoI
contours, hierarchy = cv2.findContours(centered_by_boundingRect, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
ellipse = cv2.fitEllipse(contours[0])
(xc, yc), (d1, d2), angle = ellipse
roi_centroid = (xc, yc)
rot_angle = 90 - angle
if rot_angle < 0:
rot_angle += 180
# This rotated RoI is (Lx)
M = cv2.getRotationMatrix2D(roi_centroid, -rot_angle, 1.0)
rot_im = cv2.warpAffine(centered_by_boundingRect, M, (ww, hh))
plt.imshow(rot_im, cmap=plt.cm.gray)
# (Ly)
# by passing 0 to flip() should flip image around x-axis, but I get the same result as the paper
res_flip_y = cv2.flip(rot_im.copy(), 0)
plt.imshow(res_flip_y , cmap=plt.cm.gray)
# (L) (xor) (Lx)
res_x_xor = cv2.bitwise_xor(centered_by_boundingRect, rot_im)
plt.imshow(res_x_xor, cmap=plt.cm.gray)
# (L) (xor) (Ly)
res_y_xor = cv2.bitwise_xor(centered_by_boundingRect, res_flip_x)
plt.imshow(res_y_xor, cmap=plt.cm.gray)
I still can't get the same result as the paper, the rotating operation also produce bad result on large RoI. Help...
UPDATE ---------- 20/03/2021
Small RoI: fine result on rotation and looks similar with the paper, but still not getting the same end result on the L (xor) Lx or L (xor) Ly
Large RoI: bad result on rotation as the RoI get out of border/image
The angle you're looking for is returned from fitEllipse. It's just rotated a bit according to a different reference frame. You can get your counter-clockwise rotation angle by doing 90 - angle. As for rotating the roi you can either use minAreaRect to get a minimum-fit rectangle directly, or you can fit a bounding box to the contour and rotate each point individually.
The green rectangle is the minAreaRect(), the red rectangle is the boundingRect() after it's been rotated.
import cv2
import numpy as np
import math
# rotate point
def rotate2D(point, deg):
rads = math.radians(deg);
x, y = point;
rcos = math.cos(rads);
rsin = math.sin(rads);
rx = x * rcos - y * rsin;
ry = x * rsin + y * rcos;
rx = round(rx);
ry = round(ry);
point[0] = rx;
point[1] = ry;
# translate point
def translate2D(src, target, sign):
tx, ty = target;
src[0] += tx * sign;
src[1] += ty * sign;
# read image
cont_img = cv2.imread("blob.png", 0)
cont_rgb = cv2.cvtColor(cont_img, cv2.COLOR_GRAY2RGB)
# find contour
_, contours, hierarchy = cv2.findContours(cont_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# fit ellipse and get ellipse properties
ellipse = cv2.fitEllipse(contours[0])
(xc, yc), (d1, d2), angle = ellipse
# -------- NEW STUFF IN HERE --------------
# calculate counter-clockwise angle relative to x-axis
rot_angle = 90 - angle;
if rot_angle < 0:
rot_angle += 180;
print(rot_angle);
# if you want a rotated ROI I would recommend using minAreaRect rather than rotating a different rectangle
# fit a minrect to the image # this is taken directly from OpenCV's tutorials
rect = cv2.minAreaRect(contours[0]);
box = cv2.boxPoints(rect);
box = np.int0(box);
cv2.drawContours(cont_rgb, [box], 0, (0,255,0), 2);
# but if you really want to use a different rectangle and rotate it, here's how to do it
# create rectangle
x,y,w,h = cv2.boundingRect(contours[0]);
rect = [];
rect.append([x,y]);
rect.append([x+w,y]);
rect.append([x+w,y+h]);
rect.append([x,y+h]);
# rotate it
rotated_rect = [];
center = [x + w/2, y + h/2];
for point in rect:
# for each point, center -> rotate -> uncenter
translate2D(point, center, -1);
rotate2D(point, 90 - rot_angle); # "90 - angle" is because rotation goes clockwise
translate2D(point, center, 1);
rotated_rect.append([point]);
rotated_rect = np.array(rotated_rect);
cv2.drawContours(cont_rgb, [rotated_rect.astype(int)], -1, (0,0,255), 2);
# ------------- END OF NEW STUFF -----------------
# draw fitted ellipse and centroid
target_ellipse = cv2.ellipse(cont_rgb.copy(), ellipse, (37, 99, 235), 10)
centroid = cv2.circle(target_ellipse.copy(), (int(xc), int(yc)), 20, (250, 204, 21), -1)
# draw major axis
rmajor = max(d1, d2)/2
if angle > 90:
angle = angle - 90
else:
angle = angle + 90
xtop_major = xc + math.cos(math.radians(angle))*rmajor
ytop_major = yc + math.sin(math.radians(angle))*rmajor
xbot_major = xc + math.cos(math.radians(angle+180))*rmajor
ybot_major = yc + math.sin(math.radians(angle+180))*rmajor
top_major = (int(xtop_major), int(ytop_major))
bot_major = (int(xbot_major), int(ybot_major))
target_major_axis = cv2.line(centroid.copy(),
top_major, bot_major,
(0, 255, 255), 5)
## image center coordinate
hh, ww = target_major_axis.shape[:2];
x_center_start = (0, int(hh/2))
x_center_end = (int(ww), int(hh/2))
y_center_start = (int(ww/2), 0)
y_center_end = (int(ww/2), int(hh))
img_x_middle_coor = cv2.line(target_major_axis.copy(), x_center_start, x_center_end, (219, 39, 119), 10)
img_y_middle_coor = cv2.line(img_x_middle_coor.copy(), y_center_start,
y_center_end, (190, 242, 100), 10)
# show
cv2.imshow("image", img_y_middle_coor);
cv2.waitKey(0);
For the future: check that your code runs before pasting it on here. Aside from the missing "import" lines it was also missing this line:
hh, ww = target_major_axis.shape[:2]
If the sample code you paste has errors, then everyone who wants to help will have to waste some time bug-stomping before they can begin working on a solution.
Is there any convenient way to draw text right into OpenCV circle? Haven't found any similar answer in Google.
If I simply use circle's Centroid_X and Centroid_Y for putText I get something like in the picture below but I want Text completely fit circle and cannot find any elegant way to draw text inside circle.
cv2.putText(frame, text, (cX, cY), FONT, 1.5, TEXT_COLOUR, int(TEXT_THICKNESS), cv2.LINE_AA)
The problem occurs due to the fact that the position given to cv2.putText defines the origin (bottom left corner of the rectangular area that fits the drawn text).
In order to have the text centered around some given point, you first need to measure the size of this rectangular area (bounding box). This can be done using the function cv2.getTextSize.
Now, to have the text centered, the origin needs to move down by half the height of the bouding box, and to the left by half the width of the bounding box.
text_origin = (CENTER[0] - text_size[0] / 2, CENTER[1] + text_size[1] / 2)
Code:
import cv2
import numpy as np
img = np.zeros((128, 128, 3), dtype=np.uint8)
CENTER = (64, 64)
cv2.circle(img, CENTER, 48, (127,0,127), -1)
TEXT_FACE = cv2.FONT_HERSHEY_DUPLEX
TEXT_SCALE = 1.5
TEXT_THICKNESS = 2
TEXT = "0"
text_size, _ = cv2.getTextSize(TEXT, TEXT_FACE, TEXT_SCALE, TEXT_THICKNESS)
text_origin = (CENTER[0] - text_size[0] / 2, CENTER[1] + text_size[1] / 2)
cv2.putText(img, TEXT, text_origin, TEXT_FACE, TEXT_SCALE, (127,255,127), TEXT_THICKNESS, cv2.LINE_AA)
cv2.imwrite('centertext_out.png', img)
Output image:
I want to crop a rectangle shape area from an image using Pillow in python. The problem is that the rectangle is not necessary parallel with the image margins so I cannot use the .crop((left, top, right, bottom)) function.
Is there a way to achieve this with Pillow? (assuming we know the coordinates of all 4 points of rectangle)
If not, how it can be done using a different Python library?
You can use min rotated rectangle in OpenCV:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
As a result You have: center coordinates (x,y), width, height, angle of rotation of rectangle. You can rotate whole image with angle from this rectangle. You image now will be rotated:
You can calculate new coordinates of four rectangle vertices (you got angle). Then just calculate normal rectangle for this points (normal rectangle = not minimal, without any rotation). With this rect You can crop Your rotated image. In this crop image will be what You want if I understand You correctly. Something like that:
So You only need Opencv. Maybe there is some library with which You can do it easier.
Here's a solution based on scikit-image (not Pillow) that you might find useful.
You could pass the vertices of the region you wish to crop to the function skimage.draw.polygon and then use the retrieved pixel coordinates to mask the original image (for example, through the alpha channel).
import numpy as np
from skimage import io, draw
img = io.imread('https://i.stack.imgur.com/x5Ym4.png')
vertices = np.asarray([[150, 140],
[300, 240],
[210, 420],
[90, 320],
[150, 150]])
rows, cols = draw.polygon(vertices[:, 0], vertices[:, 1])
crop = img.copy()
crop[:, :, -1] = 0
crop[rows, cols, -1] = 255
io.imshow(crop)
I adapted this opencv-based solution (sub_image) for use with PIL. It takes a (center, size, theta) rect which I'm getting from cv2.minAreaRect, but could be constructed mathmatically from points, etc.
I've seen a few other solutions but they left some weird artifacts.
def crop_tilted_rect(image, rect):
""" crop rect out of image, handing rotation
rect in this case is a tuple of ((center_x, center_y), (width, height), theta),
which I get from opencv's cv2.minAreaRect(contour)
"""
# Get center, size, and angle from rect
center, size, theta = rect
width, height = [int(d) for d in size]
if 45 < theta <= 90:
theta = theta - 90
width, height = height, width
theta *= math.pi / 180 # convert to rad
v_x = (math.cos(theta), math.sin(theta))
v_y = (-math.sin(theta), math.cos(theta))
s_x = center[0] - v_x[0] * (width / 2) - v_y[0] * (height / 2)
s_y = center[1] - v_x[1] * (width / 2) - v_y[1] * (height / 2)
mapping = np.array([v_x[0],v_y[0], s_x, v_x[1],v_y[1], s_y])
return image.transform((width, height), Image.AFFINE, data=mapping, resample=0, fill=1, fillcolor=(255,255,255))
I need to remove the outer area of the circle.
I need to use only the inner area of the circle to avoid errors in image processing, currently I can only find the circle and mark it
I do not know if I'm doing it right, using cv2.circle
Please help me
circles = cv2.HoughCircles(cinza, cv2.HOUGH_GRADIENT, 1, 20)
if circles is not None:
maior = 0
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
radius = i[2]
if radius > maior:
maior = radius
for i in circles[0, :]:
center = (i[0], i[1])
radius = i[2]
if radius == maior:
cv2.circle(image, center, 1, (0, 100, 100), 3)
cv2.circle(image, center, radius, (255, 0, 255), 3)
First use numpy meshgrid to get a matrix for x and y with the indices of the image. Then calculate the distance of the indices to the center of your circle by subtracting the centroid from the indice matrices, for x and y each.
Afterwards calculate the distances of the pixels to the centroid using
distances = (x**2 + y**2)**0.5
x, y, image and distances must have the same shape now.
Then use boolean indexing on the distances matrix to pick the pixels within the circle and set them e.g. to zero like
image[distances > 0.5] = 0.0
You can use mask functions of OpenCV. In this link, you can see mask functions on c++, I could not find Python function on official pages but there is a code for masking by OpenCV on Python.
As a summary, you can mask outer size of a circle by OpenCV mask functions to remove outer area of the circle.
I am trying to draw a pie shape with filled colour. I've tried to do this in different ways. Here's the code:
ball = pygame.draw.circle(self.screen, self.pink, self.pos, self.r, 0)
pygame.gfxdraw.pie(self.screen, 60,60, 40, 0, 90,(0,255,0))
pygame.gfxdraw.arc(self.screen, 60,60, 40, 180, 270,(0,255,255))
pygame.draw.arc(self.screen, (255,0,255),ball,0, math.pi/4, ball.width/2)
The output image is like:
I want the pie shapes filled with colour, as the magenta coloured shape does. I used the arc function and set the line with = the radius to achieve this (4th line in the code). However, the colour isn't evenly filled. I also tried to draw a pie shape (2nd line in the code) However, I cannot find a way to fill the colour...
Thank you very much for your help!
You can just draw a sufficiently fine polygon (e.g in one degree intervals):
import math
import pygame
# Center and radius of pie chart
cx, cy, r = 100, 320, 75
# Background circle
pygame.draw.circle(screen, (17, 153, 255), (cx, cy), r)
# Calculate the angle in degrees
angle = val*360/total
# Start list of polygon points
p = [(cx, cy)]
# Get points on arc
for n in range(0,angle):
x = cx + int(r*math.cos(n*math.pi/180))
y = cy+int(r*math.sin(n*math.pi/180))
p.append((x, y))
p.append((cx, cy))
# Draw pie segment
if len(p) > 2:
pygame.draw.polygon(screen, (0, 0, 0), p)