Fitting ellipse to random distributed uniform regular shapes - python

We can think of the shapes in the representative picture as randomly scattered pencils or sticks on a table. I've been trying to find the areas of each shape by fitting ellipses, but I haven't been able to fit ellipses properly. Can you help me? Thanks.
First image is : input image
The code that I tried,
import cv2
import numpy as np
import random as rng
import math
img = cv2.imread('sticks.png', 1)
imge= cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
gray = cv2.cvtColor(imge, cv2.COLOR_BGR2GRAY)
blur = cv2.blur(gray, (2,2), 3)
rng.seed(1)
def thresh_callback(val):
threshold = val
canny_output = cv2.Canny(blur, threshold, threshold * 4)
contours, _ = cv2.findContours(canny_output, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
minRect = [None]*len(contours)
minEllipse = [None]*len(contours)
for i, c in enumerate(contours):
minRect[i] = cv2.minAreaRect(c)
if c.shape[0] > 5:
minEllipse[i] = cv2.fitEllipse(c)
(x,y),(minor_axis,major_axis),angle = minEllipse[i]
half_major= major_axis/2
half_minor= minor_axis/2
pixel= 37.795275591
half_major1= half_major/pixel
half_minor1= half_minor/pixel
area= math.pi * half_major1 * half_major1
print(area)
drawing = np.zeros((canny_output.shape[1], canny_output.shape[1], 3), dtype=np.uint8)
for i, c in enumerate(contours):
color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
cv2.drawContours(drawing, contours, i, color)
if c.shape[0] > 5:
cv2.ellipse(drawing, minEllipse[i], color, 1)
cv2.imshow('Fitting Ellips', drawing)
source_window = 'Source'
cv2.namedWindow(source_window)
cv2.imshow(source_window, img)
max_thresh = 255
thresh = 100
cv2.createTrackbar('Canny Thresh:', source_window,thresh, max_thresh, thresh_callback)
thresh_callback(thresh)
cv2.waitKey()
Second image is: expected result (fitting ellipse each line like this)

This is not the final result and definitely has errors. You need to take the time to achieve the desired result. But it can be a good idea to start with:
import sys
import cv2
import math
import numpy as np
# Check it there is a black area in specific position of an image
def checkPointArea(im, pt):
x, y = pt[0], pt[1]
return im[y, x, 0] == 0 or im[y, x+1, 0] == 0 or im[y, x-1, 0] == 0 or im[y+1, x, 0] == 0 or im[y-1, x, 0] == 0
# Load image
pth = sys.path[0]
im = cv2.imread(pth+'/im.jpg')
H, W = im.shape[:2]
# Make grayscale and black and white versions
im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
bw = cv2.threshold(im, 110, 255, cv2.THRESH_BINARY)[1]
# Try to clear the parts of the image that are stuck together
bw = cv2.dilate(bw, np.ones((5, 5), np.uint8))
# Convert im back to BGR
im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
# Make some copies
org = im.copy()
empty = im.copy()
empty[:] = 255
# Find contours and sort them by position
cnts, _ = cv2.findContours(bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnts.sort(key=lambda x: cv2.boundingRect(x)[0])
# Thikness of random lines
thickness = 5
# Find and draw ellipses
for cnt in cnts:
x, y, w, h = cv2.boundingRect(cnt)
if w < W:
cv2.rectangle(im, (x, y), (x+w, y+h), (10, 230, 0)
if w < h else (200, 0, 128), 1)
hw, hh = w//2, h//2
cx, cy = x+hw, y+hh
r = int(math.sqrt(w**2+h**2))
t, c = math.atan(hw/hh), (255, 0, 0)
if checkPointArea(org, (x, y)) and checkPointArea(org, (x+w-1, y+h-1)):
t, c = math.atan(hw/-hh), (100, 0, 200)
deg = math.degrees(t)
if w <= thickness*2:
deg = 0
if h <= thickness*2:
deg = 90
cv2.ellipse(im, (x, y), (1, 1), 0, 0, 360, c, 4)
cv2.ellipse(im, (cx, cy), (thickness, r//2),
deg, 0, 360, (40, 0, 255), 2, lineType=cv2.LINE_AA)
#cv2.ellipse(empty, (x, y), (1, 1), 0, 0, 360, c, 2)
cv2.ellipse(empty, (cx, cy), (thickness, r//2),
deg, 0, 360, c, 2, lineType=cv2.LINE_AA)
# Save output
bw = cv2.cvtColor(bw, cv2.COLOR_GRAY2BGR)
top = np.hstack((org, empty))
btm = np.hstack((bw, im))
cv2.imwrite(pth+'/im_.png', np.vstack((top, btm)))
Each section:
Final Result:
Errors:
You have to spend more time for these two parts, the first is due to my weak code. Removable with more time. The second is due to the overlap of two lines. Clearing the image did not help this part. You may be able to prevent such interference from occurring later.

Related

how to cut image according to two points in opencv?

I have this input image (feel free to download it and try your solution, please):
I need to find points A and B that are closest to the left down and right upper corner. And than I would like to cut of the image. See desired output:
So far I have this function, but it does not find points A, B correctly:
def CheckForLess(list1, val):
return(all(x < val for x in list1))
def find_corner_pixels(img):
# Get image dimensions
height, width = img.shape[:2]
# Find the first non-black pixel closest to the left-down and right-up corners
nonempty = []
for i in range(height):
for j in range(width):
# Check if the current pixel is non-black
if not CheckForLess(img[i, j], 10):
nonempty.append([i, 1080 - j])
return min(nonempty) , max(nonempty)
Can you help me please?
EDIT:
Solution by Achille works on one picture, but if I change input image to this:
It gives wrong output:
I noticed that your image has an alpha mask that already segment the foreground. This imply using the flag cv.IMREAD_UNCHANGED when reading the image with openCV (cv.imread(filename, cv.IMREAD_UNCHANGED)).
If this is the case you can have a try to the following:
import sys
from typing import Tuple
import cv2 as cv
import numpy as np
class DetectROI:
def __init__(self,
alpha_threshold: int = 125,
display: bool = False,
gaussian_sigma: float = 1.,
gaussian_window: Tuple[int, int] = (3, 3),
relative_corner: float = 0.25,
relative_line_length: float = 0.25,
relative_max_line_gap: float = 0.02,
working_size: Tuple[int, int] = (256, 256)):
self.alpha_threshold = alpha_threshold
self.display = display
self.working_size = working_size
self.gaussian_sigma = gaussian_sigma
self.gaussian_window = gaussian_window
self.relative_line_length = relative_line_length
self.relative_max_line_gap = relative_max_line_gap
self.relative_corner = relative_corner
self._origin: Tuple[int, int] = (0, 0)
self._src_shape: Tuple[int, int] = (0, 0)
def __call__(self, src):
# get cropped contour
cnt_img = self.get_cropped_contour(src)
left_lines, right_lines = self.detect_lines(cnt_img)
x, y, w, h = self.get_bounding_rectangle(left_lines + right_lines)
# top_left = (x, y)
top_right = (x + w, y)
# bottom_right = (x + w, y + h)
bottom_left = (x, y + h)
if self.display:
src = cv.rectangle(src, bottom_left, top_right, (0, 0, 255, 255), 3)
cv.namedWindow("Source", cv.WINDOW_KEEPRATIO)
cv.imshow("Source", src)
cv.waitKey()
return bottom_left, top_right
def get_cropped_contour(self, src):
self._src_shape = tuple(src.shape[:2])
msk = np.uint8((src[:, :, 3] > self.alpha_threshold) * 255)
msk = cv.resize(msk, self.working_size)
cnt, _ = cv.findContours(msk, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cnt_img = cv.drawContours(np.zeros_like(msk), cnt, 0, (255,))
cnt = cnt[0]
x, y, w, h = cv.boundingRect(np.array(cnt))
top_left = (x, y)
# top_right = (x + w, y)
bottom_right = (x + w, y + h)
# bottom_left = (x, y + h)
self._origin = top_left
cnt_img = cnt_img[self._origin[1]:bottom_right[1], self._origin[0]:bottom_right[0]]
if self.display:
cv.namedWindow("Contours", cv.WINDOW_KEEPRATIO)
cv.imshow("Contours", cnt_img)
return cnt_img
def detect_lines(self, img):
img = cv.GaussianBlur(img, self.gaussian_window, self.gaussian_sigma)
lines = cv.HoughLinesP(img, 1, np.pi / 180, 50, 50,
int(self.relative_line_length*img.shape[0]),
int(self.relative_max_line_gap*img.shape[0]))
if self.display:
lines_img = np.repeat(img[:, :, None], 3, axis=2)
if lines is not None:
for i in range(0, len(lines)):
l = lines[i][0]
cv.line(lines_img, (l[0], l[1]), (l[2], l[3]), (255, 0, 0), 2, cv.LINE_AA)
# keep lines close to bottom left and bottom right images
corner = self.relative_corner
left_lines = []
right_lines = []
if lines is not None:
# left side
for i in range(0, len(lines)):
l = lines[i][0]
if (l[1] > (1 - corner) * img.shape[1] and l[0] < corner * img.shape[0]) \
or (l[3] > (1 - corner) * img.shape[1] and l[2] < corner * img.shape[0]):
left_lines.append(l)
elif (l[1] > (1 - corner) * img.shape[1] and l[0] > (1 - corner) * img.shape[0]) \
or (l[3] > (1 - corner) * img.shape[1] and l[2] > (1 - corner) * img.shape[0]):
right_lines.append(l)
if self.display:
if lines is not None:
for l in left_lines + right_lines:
cv.line(lines_img, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 2, cv.LINE_AA)
cv.namedWindow("Contours", cv.WINDOW_KEEPRATIO)
cv.imshow("Contours", lines_img)
return left_lines, right_lines
def get_bounding_rectangle(self, lines):
cnt = sum(([(l[0], l[1]), (l[2], l[3])] for l in lines), [])
x, y, w, h = cv.boundingRect(np.array(cnt))
x += self._origin[0]
y += self._origin[1]
y = np.int32(np.round(y * self._src_shape[0] / self.working_size[0]))
h = np.int32(np.round(h * self._src_shape[0] / self.working_size[0]))
x = np.int32(np.round(x * self._src_shape[1] / self.working_size[1]))
w = np.int32(np.round(w * self._src_shape[1] / self.working_size[1]))
return x, y, w, h
def main(argv):
default_file = r'book.png'
filename = argv[0] if len(argv) > 0 else default_file
src = cv.imread(filename, cv.IMREAD_UNCHANGED)
detector = DetectROI(display=True)
return detector(src)
if __name__ == "__main__":
print("bottom_left: {}, top_right: {}".format(*main(sys.argv[1:])))
The underlying idea is the following:
threshold the alpha mask to get the foreground
compute the contour of the alpha mask
detect the lines (assuming the right and left border to be rather strait)
keep the lines that start from the bottom left and the bottom right of the image (drawn in red)
Here is the obtained result
I hope this is robust enough
I'm a bit rusted, haven't practiced opencv2 for a long time but this is what I came up with:
import numpy as np
import cv2
img = cv2.imread("book.png")
timg = img.copy()
cv2.imshow("img", img)
# Get a mask to get only the colour you need (cover of the book)
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower = np.array([10, 150, 150])
upper = np.array([35, 255, 255])
mask = cv2.inRange(hsv_img, lower, upper)
masked = cv2.bitwise_and(hsv_img, hsv_img, mask=mask)
img[mask == 0] = 255
cv2.imshow("mask", img)
# Find contours of the masked image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# For some reason, first contour was the entire screen so only take the second rectangle
contours = sorted(contours, key=cv2.contourArea, reverse=True)[1:2]
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
# get the corners of the rectangle
top_left = (x, y)
top_right = (x + w, y)
bottom_right = (x + w, y + h)
bottom_left = (x, y + h)
height, width = img.shape[:2]
pt1 = (0, top_left[1])
pt2 = (width, top_left[1])
pt3 = (0, bottom_left[1])
pt4 = (width, bottom_left[1])
cv2.line(timg, pt1, pt2, [10, 150, 150],1 )
cv2.line(timg, pt3, pt4, [10, 150, 150], 1)
cv2.imshow("Bounding Rectangles", timg)
cv2.waitKey(0)
hope this helps (Note that you could retrieve only the book by getting the content of the contours
Then, cropping is really easy
# Select the area to crop
cropped = img[y1:y2, x1:x2]

Draw bounding box around a sequence of contours

I am trying to put a bounding box around a sequence of contours like the following. A top contour and a bottom contour
image1
I wrote the following basic code and this was the result image2
import cv2
import numpy as np
img = cv2.imread('light2.png')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (0, 0, 46), (179, 255, 255))
kernel = np.ones((5,5),np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
try: hierarchy = hierarchy[0]
except: hierarchy = []
height, width, _ = img.shape
min_x, min_y = width, height
max_x = max_y = 0
for contour, hier in zip(contours, hierarchy):
(x, y, w, h) = cv2.boundingRect(contour)
min_x, max_x = min(x, min_x), max(x+w, max_x)
min_y, max_y = min(y, min_y), max(y+h, max_y)
if w > 80 and h > 80:
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
if max_x - min_x > 0 and max_y - min_y > 0:
cv2.rectangle(img, (min_x, min_y), (max_x, max_y), (255, 0, 0), 2)
I am kind of struggling with the logic when there are other contours in the environment like in image3. And still want to put a bounding box around the top and bottom contour detection only (something like this image4). But with the current code, it puts the bounding box like this image5. Any help is appreciated.
You need to explain computer what you want using tools that you have. I suggest use threshold -> connectedComponents -> filter wrong bboxes -> find 2 bbox with same X position and ≈ area (not implemented) -> union bboxes
Code example:
import cv2
import numpy as np
def drawStats(img: np.array, arr: np.array):
for i in range(arr.shape[0]):
w = arr[i, cv2.CC_STAT_WIDTH]
h = arr[i, cv2.CC_STAT_HEIGHT]
l = arr[i, cv2.CC_STAT_LEFT]
t = arr[i, cv2.CC_STAT_TOP]
cv2.rectangle(img, (l, t), (l+w,t+h), (20, 0, 255), 3)
def filterStats(arr: np.array) -> np.array:
result = []
for i in range(arr.shape[0]):
w = arr[i, cv2.CC_STAT_WIDTH]
h = arr[i, cv2.CC_STAT_HEIGHT]
if w > h * 4:
result.append(arr[i])
result = np.array(result)
return result
img = cv2.imread("/Users/alex/Downloads/exo7R.jpg", cv2.IMREAD_GRAYSCALE)
_, img2 = cv2.threshold(img, 230, 255, cv2.THRESH_BINARY)
comp = cv2.connectedComponentsWithStats(img2, connectivity=8)
debugImg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
stats = filterStats(comp[2])
drawStats(debugImg, stats)
cv2.imshow("threshold", img2)
cv2.imshow("found components", debugImg)
cv2.waitKey()

How to select correct contour the group of six digits

I have a batch of screenshots like here:
and I try to detect the region with six digits and recognize them. The second part works like a charm. I have a problem detecting the correct region because it can be placed with a shift depending on screen dimensions. For example, crop image looks like this:
Seems it looks ok, but I have to add some workaround in code to select the right place.
My code:
import cv2
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 6))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (8, 8))
# Load and resize image to standard size
img0 = Image.open('./data/test.png')
img0.thumbnail((720, 1423))
img = np.array(img0)
# The magic from https://www.pyimagesearch.com/2017/07/17/credit-card-ocr-with-opencv-and-python/
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
locs = []
for (i, c) in enumerate(cnts):
(x, y, w, h) = cv2.boundingRect(c)
ar = w / h
if x > 140 and x < 220 and w > 100 and h > 12 and h < 20 and ar >= 4 and ar <= 6:
locs.append((x, y, w, h))
# Calculate the crop rectangle
LEFT_TOP = (181, 316)
RIGHT_BOTTOM = (299, 346)
if len(locs) > 0:
(x, y, w, h) = locs[0]
LEFT_TOP = (x - 5, y - 5) # workaround place
RIGHT_BOTTOM = (x + w + 5, y + h) # workaround place
print(LEFT_TOP, RIGHT_BOTTOM)
img1 = img0.crop(LEFT_TOP + RIGHT_BOTTOM)
Selected contour looks like:
It selects a contour smaller than the actual region. Why? How to fix it?
Thank you!
Test file:
There are no magics in software...
Inappropriate filters "eats" part of your digits.
Remove the tophat filter.
Remove the Sobel filter.
Replace cv2.THRESH_BINARY with cv2.THRESH_BINARY_INV.
Increase the size of sqKernel.
I recommend you to draw the contours, and show (or save) intermediate results for testing.
Here is the modified code:
import cv2
import numpy as np
#from matplotlib import pyplot as plt
from PIL import Image
#rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 6))
#sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (8, 8))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 11))
# Load and resize image to standard size
img0 = Image.open('./data/test.png')
img0.thumbnail((720, 1423))
img = np.array(img0)
# The magic from https://www.pyimagesearch.com/2017/07/17/credit-card-ocr-with-opencv-and-python/
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
#tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
#gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
#gradX = np.absolute(gradX)
#(minVal, maxVal) = (np.min(gradX), np.max(gradX))
#gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
#gradX = gradX.astype("uint8")
#gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
#thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
locs = []
# Draw contours for testing
tmp_im = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)
cv2.drawContours(tmp_im, cnts, -1, (0, 255, 0), 1) # Draw green line around the contour
cv2.imshow('tmp_im', tmp_im)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite('./data/tmp_im.png', tmp_im)
for (i, c) in enumerate(cnts):
(x, y, w, h) = cv2.boundingRect(c)
ar = w / h
if x > 140 and x < 220 and w > 100 and h > 12 and h < 20 and ar >= 4 and ar <= 6:
locs.append((x, y, w, h))
# Calculate the crop rectangle
LEFT_TOP = (181, 316)
RIGHT_BOTTOM = (299, 346)
if len(locs) > 0:
(x, y, w, h) = locs[0]
#LEFT_TOP = (x - 5, y - 5) # workaround place
#RIGHT_BOTTOM = (x + w + 5, y + h) # workaround place
LEFT_TOP = (x, y) # workaround place
RIGHT_BOTTOM = (x + w, y + h) # workaround place
print(LEFT_TOP, RIGHT_BOTTOM)
img1 = img0.crop(LEFT_TOP + RIGHT_BOTTOM)
img1.show()
img1.save('./data/digits.png')
Result:
tmp_img (for testing):

Color identification in an image using opencv in python

![enter image description here][1]
Able to identify and get blue colour but culdnt identify the red colour range unable to fix colour range for red/purple stripes.I have used contour and created range for red,green,blue colour .That stripes colour range is not correct I tried settig maximum range for red/purple
import numpy as np
import cv2
img = cv2.imread(r'/home/pavithra/Downloads/pic.jpeg')
hsvFrame = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Set range for red color and
red_lower = np.array([136, 86, 86], np.uint8)
red_upper = np.array([239, 12, 50], np.uint8)
red_mask = cv2.inRange(hsvFrame, red_lower, red_upper)
green_lower = np.array([25, 52, 72], np.uint8)
green_upper = np.array([102, 255, 255], np.uint8)
green_mask = cv2.inRange(hsvFrame, green_lower, green_upper)
blue_lower = np.array([94, 80, 2], np.uint8)
blue_upper = np.array([120, 255, 255], np.uint8)
blue_mask = cv2.inRange(hsvFrame, blue_lower, blue_upper)
kernal = np.ones((5, 5), "uint8")
red_mask = cv2.dilate(red_mask, kernal)
res_red = cv2.bitwise_and(img, img,
mask = red_mask)
# For green color
green_mask = cv2.dilate(green_mask, kernal)
res_green = cv2.bitwise_and(img, img,
mask = green_mask)
# For blue color
blue_mask = cv2.dilate(blue_mask, kernal)
res_blue = cv2.bitwise_and(img, img,
mask = blue_mask)
# Creating contour to track red color
contours, hierarchy = cv2.findContours(red_mask,
cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
for pic, contour in enumerate(contours):
area = cv2.contourArea(contour)
if(area > 300):
x, y, w, h = cv2.boundingRect(contour)
img = cv2.rectangle(img, (x, y),
(x + w, y + h),
(0, 255, 0), 2)
cv2.putText(img, "Red", (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 1.0,
(0, 255, 0),2)
contours, hierarchy = cv2.findContours(green_mask,
cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
for pic, contour in enumerate(contours):
area = cv2.contourArea(contour)
if(area > 300):
x, y, w, h = cv2.boundingRect(contour)
img = cv2.rectangle(img, (x, y),
(x + w, y + h),
(0, 255, 0), 2)
cv2.putText(img, "Green", (x, y),
cv2.FONT_HERSHEY_SIMPLEX,
1.0, (0, 255, 0),2)
contours, hierarchy = cv2.findContours(blue_mask,
cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
for pic, contour in enumerate(contours):
area = cv2.contourArea(contour)
if(area > 300):
x, y, w, h = cv2.boundingRect(contour)
img = cv2.rectangle(img, (x, y),
(x + w, y + h),
(255, 0, 0), 2)
cv2.putText(img, "Blue", (x, y),
cv2.FONT_HERSHEY_SIMPLEX,
1.0, (255, 0, 0),2)
resize = cv2.resize(img, (800, 480))
cv2.imshow("All clg", resize)
cv2.waitKey(0)
I split the image into half just to avoid unnecessary noise reductions. Then rotated the image to make the text horizontal. Binarized the image and obtained the three big blue-colored covid19 labels. Sorted them according to their y-coordinate (top to bottom). Performed some operations to obtain the region of interest (ROI, the two stripes which I named labels). Binarized each label to obtain the two contours of the colored stripes. Adjusted the gamma of the labels so that the colors become a little darker. Obtained the red-component of each stripe. Sorted them according to their x-coordinate (left to right). Assigned the stripe with the highest red value the red color, the other as purple.
Code:
def adjust_gamma(image, gamma=1.0):
# build a lookup table mapping the pixel values [0, 255] to
# their adjusted gamma values
invGamma = 1.0 / gamma
table = np.array([((i / 255.0) ** invGamma) * 255
for i in np.arange(0, 256)]).astype("uint8")
# apply gamma correction using the lookup table
return cv2.LUT(image, table)
def rotate(image: np.ndarray,angle, background_color):
old_width, old_height = image.shape[:2]
angle_radian = math.radians(angle)
width = abs(np.sin(angle_radian) * old_height) + abs(np.cos(angle_radian) * old_width)
height = abs(np.sin(angle_radian) * old_width) + abs(np.cos(angle_radian) * old_height)
image_center = tuple(np.array(image.shape[1::-1]) / 2)
rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
rot_mat[1, 2] += (width - old_width) / 2
rot_mat[0, 2] += (height - old_height) / 2
return cv2.warpAffine(image, rot_mat, (int(round(height)), int(round(width))), borderValue=background_color)
img = cv2.imread("covid.jpg")
rows,cols = img.shape[:2]
img = img[:,:cols//2]
rot = rotate(img,90,(255,255,255))
gray = cv2.cvtColor(rot,cv2.COLOR_BGR2GRAY)
otsu = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
opening = cv2.morphologyEx(otsu,cv2.MORPH_OPEN,np.ones((3,3),np.uint8),iterations=3)
dilate = cv2.dilate(opening,np.ones((1,5),np.uint8),iterations=5)
dilate2 = cv2.dilate(dilate,np.ones((3,3),np.uint8),iterations=3)
contours,_ = cv2.findContours(dilate2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
lables = [] # the three covid19
y_coords = [] # to sort the lables from top to bottom (the one with single color comes 1st)
for cnt in contours:
x,y,w,h = cv2.boundingRect(cnt)
x = x + 10
w = w - 20
y_coords.append(y)
lables.append(rot[y+h//3:y+h-h//3,x+w//2:x+int(w*0.70)])
y_coords,lables = zip(*sorted(zip(y_coords,lables))) # sorting the lables from top to bottom
y_coords = list(y_coords)
lables = list(lables)
otsus = []
grays = []
for image in lables:
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
grays.append(gray)
mean = np.mean(gray)
median = np.median(gray)
otsus.append(cv2.threshold(gray,median-10,255,cv2.THRESH_BINARY)[1])
s = [] # just a list to store all the red and purple stripes
for i in range(len(otsus)):
otsus[i] = ~otsus[i]
rows,cols = otsus[i].shape[:2]
M = np.float32([[1,0,5],[0,1,5]])
lables[i] = cv2.warpAffine(lables[i],M,(cols+5,rows+10)) # giving some padding
# adjusting the gamma to make the colors more dark
lables[i] = adjust_gamma(lables[i],gamma=0.40)
otsus[i] = cv2.warpAffine(otsus[i],M,(cols+5,rows+10)) # same padding
strips,_ = cv2.findContours(otsus[i],cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
x_coords = [] # to sort the red and purple stripes from left to right
color = ["red","red"] # setting inital colors both to red
red_components = []
for j in range(len(strips)):
x,y,w,h = cv2.boundingRect(strips[j])
x_coords.append(x)
# strip = lables[i][y:y+h,x:x+w,:]
# strip = lables[i][y+h//4:y+h-h//4,x+w//4:x+w-w//4,:]
strip = lables[i][y:y+h,x+w//5:x+w-w//5,:]
s.append(strip)
try:
strip_color_b = int(np.mean(strip[0]))
strip_color_g = int(np.mean(strip[1]))
strip_color_r = int(np.mean(strip[2]))
# strip_color = (strip_color_b,strip_color_g,strip_color_r)
red = strip_color_r
red_components.append(red)
except IndexError:
print("Lable number ",i,"has only a single color.")
x_coords,red_components = zip(*sorted(zip(x_coords,red_components))) # simultaneously sorting the red and purple stripes from left to right
try:
if red_components[0] < red_components[1]:
color[0] = "purple"
else:
color[1] = "purple"
# print("red components = ",red_components)
print("Lable = ",i,"strip = ",0,"color = ",color[0])
print("Lable = ",i,"strip = ",1,"color = ",color[1]) # LEFT to RIGHT
except IndexError:
print("Lable number ",i,"is excluded. Continuing with further labels.")
print("TOTAL NUMBER OF LABLES = ",len(lables))
# reading the text
custom_oem_psm_config = r'--oem 3 --psm 3'
print(pytesseract.image_to_string(otsu,config=custom_oem_psm_config))
cv2.imshow("Lable 0",lables[0])
cv2.imshow("Lable 1",lables[1])
cv2.imshow("Lable 2",lables[2])
cv2.imshow("rotated",rot)
cv2.waitKey(0)
OUTPUT:
Lable number 0 has only a single color.
Lable number 0 is excluded. Continuing with further labels.
Lable = 1 strip = 0 color = purple
Lable = 1 strip = 1 color = red
Lable = 2 strip = 0 color = purple
Lable = 2 strip = 1 color = red
TOTAL NUMBER OF LABLES = 3
COVID-19Aq
COVID-19Ag
Please correct me if I am wrong.

How to detect shape in Cytologique image python opencv

I try to detect forms in cytology image and I get this result enter image description here my input image is enter image description here But My result does not see good can anyone help me ???
My stage was,
Color image by (COLORMAP_HOT)
Convert to grayscal image
Apply the canny filter
Find count
Test the contour
and i use python3.5 and opencv3
my code :
#!/usr/bin/env python
import cv2
import numpy as np
from pyimagesearch.shapedetector import ShapeDetector
import argparse
import imutils
from scipy import ndimage
import math
import matplotlib.pyplot as plt
if __name__ == '__main__' :
im = cv2.imread("23.png")
#im_out = np.zeros((670, 543, 3), np.uint8);
#resized = imutils.resize(im, width=300)
#ratio = im.shape[0] / float(resized.shape[0])
#coloration
im_color = cv2.applyColorMap(im, cv2.COLORMAP_HOT)
imgg = im_color[:, :, 1]
#cv2.putText(im_color, colormap_name(k), (30, 180), cv2.FONT_HERSHEY_DUPLEX, 0.5, (255, 255, 255),1);
im_out = im_color
gray = cv2.cvtColor(im_color, cv2.COLOR_RGB2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
canny = cv2.Canny(blurred, 120, 200)
kernel = np.ones((5,5),np.uint8)
#morph
dilation = cv2.dilate(canny,kernel,iterations = 1)
erosion = cv2.erode(dilation,kernel,iterations = 1)
dilation = cv2.dilate(erosion,kernel,iterations = 1)
erosion = cv2.erode(dilation,kernel,iterations = 1)
blurred = cv2.GaussianBlur(erosion, (3, 3), 0)
canny = cv2.Canny(blurred, 200, 200)
cv2.imshow("dilation", dilation)
cv2.imshow("canny", canny)
cv2.imshow("erosion", erosion)
#(thresh, im_bw) = cv2.threshold(im_gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
cv2.imshow("im_out", im_out);
cv2.imshow("gray ", gray);
#contour
cnts = cv2.findContours(canny, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
# loop over the contours
for c in cnts:
M = cv2.moments(c)
if(M["m00"]==0):M["m00"]=1
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
#shape = detect(c)
c = c.astype("float")
c = c.astype("int")
#cv2.drawContours(im, [c], -1, (0, 255, 0), 2)
#cv2.putText(im, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,0.5, (255,0,0), 2)
area = cv2.contourArea(c)
perimeter = cv2.arcLength(c,True)
M = cv2.moments(c)
# initialize the shape name and approximate the contour
shape = " "
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.05 * peri, True)
(x, y, w, h) = cv2.boundingRect(approx)
area = cv2.contourArea(c)
radius = w/2
if len(approx) == 3:
shape = ""
# if the shape has 4 vertices, it is either a square or
# a rectangle
elif len(approx) == 4:
if (M['m00']==0):
M['m00']=1
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
# compute the bounding box of the contour and use the
# bounding box to compute the aspect ratio
#(x,y) be the top-left coordinate of the rectangle and (w,h) be its width and height.
(x, y, w, h) = cv2.boundingRect(approx)
print ("area",area,"perimeter",perimeter,"cx",cx,"cy",cy,"x",x,"y", y,"w", w, "h",h)
#fichier.write("area",area,"perimeter",perimeter,"cx",cx,"cy",cy)
print (sep="\n")
ar = w / float(h)
shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle"
cv2.drawContours(im, [c], -1, (255, 0, 0), 2)
cv2.putText(im, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,0.5, (255,0,0), 2)
# if Cystine>6
elif len(approx) == 6:
if (M['m00']==0):
M['m00']=1
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print ("area",area,"perimeter",perimeter,"cx",cx,"cy",cy)
print (sep="\n")
shape = "HEXA"
cv2.drawContours(im, [c], -1, (255, 0, 0), 2)
cv2.putText(im, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,0.5, (255,0,0), 2)
# otherwise, we assume the shape is a circle
elif (abs(1 - (float(w)/h))<=2 and abs(1-(area/(math.pi*radius*radius)))<=0.2):
if (M['m00']==0):
M['m00']=1
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print ("area",area,"perimeter",perimeter,"cx",cx,"cy",cy)
print (sep="\n")
shape = "circle"
cv2.drawContours(im, [c], -1, (255,0, 0), 2)
cv2.putText(im, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,0.5, (255,0, 0), 2)
# show the output image
cv2.imshow("Image", im)
cv2.waitKey(0);

Categories