OpenCV Annulus region of interest - opencv

I defined annulus ROI selection function and i would like to find contours in this area. But contours pixel values are neighbors to the zero and out of masked areas equal to zero. Therefore contours couldn't be catch thresholded image.
How can i define annulus ROI or find the contours if function is ok
def annulusROI(img, center, innerR, outerR):
"""
img: Image matrix
center: ROI center point [px] (x,y tuple)
innerR: ROI inner radius [px]
outerR: ROI outer radius [px]
mode: Mask selection for white (255, 255, 255), for black (0, 0, 0) [BGR tuple]
return roi matrix and left-top start point coordinate
"""
outRoi, rectC = rectangleROI(img, center, outerR*2, outerR*2)
mask1 = np.zeros_like(outRoi)
mask2 = np.zeros_like(outRoi)
mask1 = cv2.circle(mask1, (round(outerR),round(outerR)), innerR, (255, 255, 255), -1)
mask2 = cv2.circle(mask2, (round(outerR),round(outerR)), outerR, (255, 255, 255), -1)
mask = cv2.subtract(mask2, mask1)
roi = cv2.bitwise_and(outRoi, mask)
return roi, (center[0]-outerR, center[1]-innerR)
contour
thresholded
roi returned image

After thresholding and before getting the contours you can separate the region of interest from the outer area. Or even better you can cut your region of interest after thresholding, not before. Finally you can filter out the releavant contours by area size.
import cv2
# get image threshold
img = cv2.imread("img.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 64, 255, 0)
# separate annulus from outer area
h, w, _ = img.shape
center = (round(w / 2), round(h / 2))
innerR = 246
outerR = 306
cv2.circle(thresh, center, innerR, 255)
cv2.circle(thresh, center, outerR, 255)
# filter contours by relevant area size
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cc = [c for c in contours if 100 < cv2.contourArea(c) < 5000]
cv2.drawContours(img, cc, -1, (0, 0, 255))
cv2.imwrite("out.png", img)
Result:

Related

Remove Yellow rectangle from image

I am using this code to remove this yellow stamp from an image :
import cv2
import numpy as np
# read image
img = cv2.imread('input.jpg')
# threshold on yellow
lower = (0, 200, 200)
upper = (100, 255, 255)
thresh = cv2.inRange(img, lower, upper)
# apply dilate morphology
kernel = np.ones((9, 9), np.uint8)
mask = cv2.morphologyEx(thresh, cv2.MORPH_DILATE, kernel)
# get largest contour
contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(big_contour)
# draw filled white contour on input
result = img.copy()
cv2.drawContours(result, [big_contour], 0, (255, 255, 255), -1)
cv2.imwrite('yellow_removed.png', result)
# show the images
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
I get the following error:
big_contour = max(contours, key=cv2.contourArea) ValueError: max() arg
is an empty sequence
Obviously, it is not detecting any contours, and the contours array is empty, but I could not figure out why that is or how to fix it.
Help is appreciated!
Check your lower thresholds. It worked for me for both images when I changed the lower threshold to lower = (0, 120, 120).
The thresholds is the reason due to the second image being darker. Lowering these thresholds captures more of the yellow area, but will still leave some holes when drawing the contour.
lower = (0, 130, 130)
You can fix this by drawing the bounding rectangle instead.
cv2.rectangle(result,(x,y),(x+w,y+h),(255,255,255),-1)
Using HSV color space is great for figuring out a particular shade/tone of color. When you have dominant colors to isolate, you can opt for the LAB color space. I have explained as to why this is better in this answer.
Code:
img = cv2.imread('bill.jpg')
# create another copy for the result
img2 = img.copy()
# convert to LAB space and store b-channel
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
b_channel = lab[:,:,-1]
Notice how bright the yellow region is above.
# Perform Otsu threshold
th = cv2.threshold(b_channel, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# Find the contour with largest area
contours, hierarchy = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
# draw the contour on plain black image of same shape as original
mask = np.zeros((img.shape[0], img.shape[1]), np.uint8)
mask = cv2.drawContours(mask,[c],0,255, -1)
# dilation to avoid border effects
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
dilate = cv2.dilate(mask, kernel, iterations=1)
img2[dilate == 255] = (255, 255, 255)
Another example:
Input:
Result:

OpenCV mask unwanted peaks

I have a problem with the contour, i.e. the mask I get after that. I am bothered by all the peaks, I marked them with a red circle, which remain after the graph. Is there any easy way to get rid of them?
h, w = img.shape[:2]
mask = np.zeros((h, w), np.uint8)
# Transform to gray colorspace and threshold the image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# Search for contours and select the biggest one and draw it on mask
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cntsSorted = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
for n in range(0,2):
cnt = cntsSorted[n]
cv2.drawContours(mask, [cnt], 0, 255, -1)
# Perform a bitwise operation
color = cv2.bitwise_and(img, img, mask=mask)
x, y, w, h = cv2.boundingRect(cnt)
crop = color[y:(y + h)-3, x+3:x + w]

Opencv: merge intersected contours into bigger rectangles

I would like to draw a big rectangle around the red sentences red as in the attached image. I was able to draw multiple rectangles around each character. Is it possible to merge intersected rectangles into a single big rectangle?
Here is my code:
image = cv2.imread('test-opencv.png')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 50, 20])
upper = np.array([5, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
cv2.imwrite('mask3.jpg', hsv)
# Gen lower mask (0-5) and upper mask (175-180) of RED
mask1 = cv2.inRange(hsv, (0, 50, 20), (5, 255, 255))
mask2 = cv2.inRange(hsv, (175, 50, 20), (180, 255, 255))
# Merge the mask and crop the red regions
mask = cv2.bitwise_or(mask1, mask2)
croped = cv2.bitwise_and(image, image, mask=mask)
ret, thresh = cv2.threshold(mask, 0, 255, 0)
contours, hierarchy = cv2.findContours(thresh, 3, 5)
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
image = cv2.rectangle(image, (x, y), (x+w+10, y+h+10), (0, 255, 0), 2)
cv2.imshow("mask", image)
cv2.waitKey()
Here is also the original image

How to remove inside contour in OpenCV

I want to set a contour ROI and remove it along with the area inside it. I
am using the below code to detect and remove the contour, but how can I remove the area inside too?
def get_skin_area(self):
# Get pointer to video frames from primary device
sourceImage = cv2.imread(self.img)
# Constants for finding range of skin color in YCrCb
min_YCrCb = np.array([0, 133, 77], np.uint8)
max_YCrCb = np.array([255, 173, 127], np.uint8)
# Convert image to YCrCb
imageYCrCb = cv2.cvtColor(sourceImage, cv2.COLOR_BGR2YCR_CB)
# Find region with skin tone in YCrCb image
skinRegion = cv2.inRange(imageYCrCb, min_YCrCb, max_YCrCb)
# Do contour detection on skin region
_, contours, hierarchy = cv2.findContours(skinRegion, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
mask = np.ones(sourceImage.shape[:2], dtype="uint8") * 255
# Draw the contour on the source image
for i, c in enumerate(contours):
area = cv2.contourArea(c)
if area > 1000:
# cv2.drawContours(sourceImage, contours, i, (0, 255, 0), 3)
cv2.drawContours(mask, contours, i, (0, 255, 0), 3)
image = cv2.bitwise_and(sourceImage, sourceImage, mask=mask)
cv2.imshow("Mask", mask)
cv2.imshow("After", image)
you can use cv.fillPoly to fill the contour with a color. docs.opencv.org/3.0-beta/modules/imgproc/doc/

opencv findContours, contourArea value not consistant

I have a test image (see the 1st image below), and a very simple code to blur and make canny edge detection of this image, then use findcontours to get contours.
image = cv2.imread("testimage.jpg")
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(image, (11,11), 0)
cv2.imshow("Blurred", blurred)
canny = cv2.Canny(blurred, 50, 130)
cv2.imshow("Canny", canny)
(_, conts, _) = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
img = image.copy()
for c in conts:
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
cv2.drawContours(img, [c], -1, (0,255,0), 1)
cv2.putText(img, "area:"+str(cv2.contourArea(c)), (cX-20,cY-20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
cv2.imshow("Contours", img)
cv2.waitKey(0)
I put the value of each contour on the image. As you may see, the contours look similar, but there is one contour with area value extremely low (only 16.0).
What might be the reason for this? And how to get consistent values among these contours?

Resources