How to find bounding box of non white area of image with OpenCV Python - bounding-box

I have an image with a white border. I want to find the bounding box of the non-border part. I also have some images with a black border and need to do the same for them but for black. I do not want to crop the image just get the origin and size of the bounding box and then draw a green rectangle around it.

You should try to get the threshold image and calculate the bounding box coordinates from it:
img = cv2.imread('img.jpg')
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gry,(3,3), 0)
th = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
coords = cv2.findNonZero(th)
x,y,w,h = cv2.boundingRect(coords)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
Also, you can use 'cv2.THRESH_BINARY' instead of 'cv2.THRESH_BINARY_INV' in the threshold section for finding the black border bounding box.

Related

Blurred Circle detection

I am new to opencv and want to detect the center point of these circles. I tried with Hough Circles with thresholding but it doesn't seem to generate good results all the time.
This image is easy to get using contours and threshloding:
It is harder to do this one:
The thresholding and Hough circle doesn't work with this image:
Adding more images for help
Can you suggest any method that will be reliable for all the images?
Since the circle is the only bright thing in the image, we can get the center by looking for the centroid of the white blob. We'll auto-threshold with otsu's and use findContours to get the centroid of the mask.
import cv2
import numpy as np
# load image
img = cv2.imread("circ1.png");
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
# threshold
gray = cv2.GaussianBlur(gray, (5,5), 0);
_, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU);
# contour
_, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
# center
M = cv2.moments(contours[0]);
cx = int(M['m10']/M['m00']);
cy = int(M['m01']/M['m00']);
center = (int(cx), int(cy));
# draw
img = cv2.circle(img, center, 4, (0,0,200), -1);
# show
cv2.imshow("marked", img);
cv2.imshow("mask", mask);
cv2.waitKey(0);

How to detect edges for pattern on space?

I'm building a new machine and have a problem with edge detection. I have a piece of paper put on one cylinder as shown in the image below. How to detect edges of this paper?
I attempted to build a led background but this paper is very large. Therefore, my machine don't have enough spaces to run.
You can use below code as a reference. Here, I am basically using cv2.inRange function to segment light green color from the image(not dark green, otherwise edge of one of the axis will also be detected) and finally applying Canny edge detection on the grayscaled version of the segmented image, i.e., cv2.Canny.
import cv2
import numpy as np
img = cv2.imread('cylinder.png')
# convert to HSV color space
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Threshold the HSV image to get green colors by defining range of green color in HSV
mask = cv2.inRange(img_hsv, (36,0,0), (55,255,255))
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img, img, mask = mask)
# coverting image with green colored region of interest from HSV to RGB
img_hsv2bgr = cv2.cvtColor(res, cv2.COLOR_HSV2BGR)
# coverting image from RGB to GRAYSCALE
img_gray = cv2.cvtColor(img_hsv2bgr, cv2.COLOR_BGR2GRAY)
# canny edge detection
edges = cv2.Canny(img_gray, 100, 200)
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
output:
EDIT: After making some modification to above code, like segmenting yellow color portion along with light green color and applying Gaussian blurring before passing into the cv2.Canny function gives even better output than above.
Code:
# Threshold the HSV image to get both green and yellow colors by defining range of color in HSV
mask_green = cv2.inRange(img_hsv, (36,0,0), (55,255,255))
mask_yellow = cv2.inRange(img_hsv, (21, 39, 64), (38, 255, 255))
mask = cv2.bitwise_or(mask_green, mask_yellow)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img, img, mask = mask)
# coverting image with green colored region of interest from HSV to RGB
frame_hsv2bgr = cv2.cvtColor(res, cv2.COLOR_HSV2BGR)
# coverting image from RGB to GRAYSCALE
frame_gray = cv2.cvtColor(frame_hsv2bgr, cv2.COLOR_BGR2GRAY)
gaussian_blurred = cv2.GaussianBlur(frame_gray,(5, 3), 0)
# canny edge detection
edges = cv2.Canny(gaussian_blurred, 100, 200)
output:

openCV problem with detecting contours of shapes fully

I am doing this university project where i try to detect UI elements on screenshots of Android applications using openCV. I am not expecting a 100 percent accuracy for this detection of UI elements.
This is my code below. I convert the image to gray scale, apply Gaussian blur and then use adaptive threshold to convert the image to binary. After which i use the find contours method.
ap = argparse.ArgumentParser()
ap.add_argument("-i","--image", help = "path to an image", required =
True)
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray",gray)
cv2.waitKey(0)
blurred = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.adaptiveThreshold(blurred, 255,
cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 4)
cv2.imshow("thresh",thresh)
cv2.waitKey(0)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cv2.drawContours(image, cnts, -1, (0,255,0), 1)
cv2.imshow("contours", image)
cv2.waitKey(0)
for c in cnts:
area = cv2.contourArea(c)
print(area)
if area > 50:
M = cv2.moments(c)
cX = int(M['m10'] / M['m00'])
cY = int(M['m01'] / M['m00'])
#cv2.drawContours(image, [c], -1, (0,255,0), 2) # draw contours on image
(x,y,w,h) = cv2.boundingRect(c) # for each contour get a
bounding rectangle
mask = np.zeros(image.shape[:2], dtype = "uint8") # find
shape of the image dimensions and set up a mask
mask[y: y + h, x: x + w] = 255 # convert region of
interest into white
to_display = cv2.bitwise_and(image,image, mask = mask) # carry
out bitwise and
#cv2.putText(image, 'center', (c))
cv2.imshow("Image", to_display)
cv2.waitKey(0)
this is the screenshot that i am running my code on.
The leftmost screenshot represents the image after applying a threshold to it.
The middle image represents the image i get after drawing the contours.
The last image shows when i am examining each individual contour. The contour covers the line but does not encapsulate the rectangle.
I have a few questions.
1) Is it possible to sieve out the contours for the white rectangles. What alteration do i have to make to my code to be able to achieve this?
2) I am trying to sieve out the unimportant contours eg. the words and I was thinking if i could use the getArea() function to help me with it. The idea is that i would set a minimum contour size to filter out the smaller contours that account for the words.
This is another image that i have tried to identify the "objects" in this screenshots.
I face the same issue here where i cant identify the white rectangles. I am only identifying the borders of the rectangle.
Would appreciate any form of help as I am still new to openCv
Original images before processing:
There is no need to blur. In fact I makes it harder. Simple thresholding works best with hard transitions. The second image is easiest. There are white items on a grayish background. By selecting only very white values the items are selected.
Result:
Code:
# load image
img = cv2.imread("app.png")
# convert to gray
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# crate a mask that hold only white values (above 250)
ret,thresh1 = cv2.threshold(img2,250,255,cv2.THRESH_BINARY)
# find contours in mask
im2, contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# select large contours (menu items only)
for cnt in contours:
print(cv2.contourArea(cnt))
if cv2.contourArea(cnt) > 5000:
# draw a rectangle around the items
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0),3)
#cv2.drawContours(img, [cnt], 0, (0,255,0), 3) #also works, but has issues with letters at the last item
#show image
cv2.imshow("img", img)
#cv2.imshow("mask", thresh) # shows mask
cv2.waitKey(0)
cv2.destroyAllWindows()
The first image is more complex, because it is divided in by a very thin red line. Selecting colors is easier in HSV colorspace. Next red values are used to create a mask, some noise is removed and then contours are detected.
Result:
# load image
img = cv2.imread("app2.png")
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# set lower and upper color limits
lower_val = np.array([0,0,0])
upper_val = np.array([20,50,255])
# Threshold the HSV image
mask = cv2.inRange(hsv, lower_val, upper_val)
# remove noise
kernel = np.ones((1,2),np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
kernel = np.ones((1,5),np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# find contours in mask
im2, contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# select large contours (menu items only)
for cnt in contours:
print(cv2.contourArea(cnt))
if cv2.contourArea(cnt) > 1000:
# draw a rectangle around the items
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0),3)
#show image
cv2.imshow("img", img)
cv2.imshow("mask", mask)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV cv2.HoughCircles detection giving bad results on easy picture

I am new to opencv and I'm trying to do circle detection using HoughCircles, but it's giving me circles where there are none, and it's not detecting the huge obvious circle that I want it to. I tried changing the parameters but can't get it to work well. What am I doing wrong?
Original Image:
Image After Thresholding:
Canny Filtered with Circles:
path=r"minimap.png"
screen = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
cv2.imshow('Original', screen)
ret,screen = cv2.threshold(screen,200,255,cv2.THRESH_BINARY)
cv2.imshow('Thresholded', screen)
P=50
can = cv2.Canny(screen,P/2,P)
cv2.imshow('Canny', can)
if 1:
circles = cv2.HoughCircles(screen, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=P, param2=53, minRadius=0, maxRadius=0)
print(circles)
circles = np.uint16(np.around(circles))
can=cv2.cvtColor(can,cv2.COLOR_GRAY2RGB)
for i in circles[0,:]:
# draw the outer circle
cv2.circle(can, (i[0], i[1]), i[2], (0, 255, 0), 3)
# draw the center of the circle
cv2.circle(can, (i[0], i[1]), 2, (0, 0, 255), 5)
cv2.imshow('Circles', can)
cv2.waitKey()
You've to play with the cv2.HoughCircles parameters and I don't think the same values will give good results for all images. For your image:
image = cv2.resize(image, (0,0), fx=0.5, fy=0.5)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7,7), sigmaX=-1, sigmaY=-1)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, rows / 2,
param1=130, param2=100,
minRadius=0, maxRadius=0)

How to remove light shadow-like color from an image

I'm using opencv to process some images. I have a lot of images like below. They have some very light, shadow-like color.
What is the dimension that determine the color of the pixel is very light? What color space should I use to identify those light color pixels?
Here's a fairly simple method:
img = cv2.imread('4.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img[gray > 200] = 255
As mentioned by #RickM there are various color spaces available to analyze your image. Since you are focused on removing the shade/shadow you need to focus on channels that contain brightness information and keep the color channels aside.
In this case the LAB color space turned out to be helpful. The luminance channel expressed a lot of info on the amount of brightness in the image
img = cv2.imread('4.png')
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
cv2.imshow('Luminance', l)
Then I obtained a threshold and masked the result with the original image to get mask1:
ret2, th = cv2.threshold(l, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
mask1 = cv2.bitwise_and(img, img, mask = th)
cv2.imshow('mask1', mask1)
But now the background is not what you intended it to be.
I created an image with white pixels of the same image dimension (white) and masked the inverted threshold image with it to get mask2:
white = np.zeros_like(img)
white = cv2.bitwise_not(white)
mask2 = cv2.bitwise_and(white, white, mask = cv2.bitwise_not(th))
cv2.imshow('mask2', mask2)
Upon adding both these images you get he intended image:
cv2.imshow('fin_img', mask2 + mask1)
Keep in mind that this would work only for similar images provided in the question.

Resources