How to reduce noise in contours using Python OpenCV - python-3.x

I am trying to detect orange beats in below image
To detect these, I have first cropped the area from original image and then setting high and low hsv values to detect orange. This seems to be working fine. Below is the detected image:
Below is the code:
import cv2
import numpy as np
win_name = "Image"
cv2.namedWindow(win_name)
img = cv2.imread('image.png')
orangeImg = img[420:510, 457:953]
hsv = cv2.cvtColor(orangeImg, cv2.COLOR_BGR2HSV)
lower_bound = np.array([0, 80, 80])
upper_bound = np.array([20, 255, 255])
origMask = cv2.inRange(hsv, lower_bound, upper_bound)
contours, hierarchy = cv2.findContours(origMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for _, c in enumerate(contours):
areas = [cv2.contourArea(c) for c in contours]
for area in areas:
if area >= 20.0:
boundRect = cv2.boundingRect(c)
rectX = boundRect[0]
rectY = boundRect[1]
rectWidth = boundRect[2]
rectHeight = boundRect[3]
color = (0, 0, 255)
cv2.rectangle(orangeImg, (int(rectX), int(rectY)), (int(rectX + rectWidth), int(rectY + rectHeight)), color, 2)
cv2.imshow(win_name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
In the output image, you can notice that it still has some noise around the bbox created. Is there a better way to reduce the noise in it. Also is there a way to count the detected contours in the image?

Related

Python ROI not being croped properly from contour

Hello everybody before you close this question, i have already searched here, here too and also here
I am using python code to detect the Leaf in an image, using contours finding and then find out the largest contour, the part works best, but then i want only the leaf part of the image and skip the rest of the image to avoid unnecessary content in the resultant output, some of the methods in the link suggests bounding box but this still includes extra content in the image as the shape is not rectangular it's irregular, sample is attached
The code is following
import cv2
import numpy as np
img = cv2.imread("blob.jpg", -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 101, 3)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
blob = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
blob = 255 - cv2.morphologyEx(blob, cv2.MORPH_CLOSE, kernel)
cnts = cv2.findContours(blob, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(cnts) == 2:
cnts = cnts[0]
else:
cnts = cnts[1]
big_contour = max(cnts, key = cv2.contourArea)
blob_area_thresh = 1000
blob_area = cv2.contourArea(big_contour)
if blob_area < blob_area_thresh:
print("Leaf is Too Small")
else:
#problem starts from here . i tested big_contour is just perfect by drawing on actual image
mask = np.zeros_like(img)
cv2.drawContours(mask, big_contour, -1, 255, -1)
out = np.zeros_like(img)
out[mask == 255] = img[mask == 255]
cv2.imwrite('output.jpg', out)
Now the problem is i am getting the resultant image as black nothing cropped all black pixels
There is a problem with your contour because it is not circulating the leaf as the end of the leaf on the right is out of the image.
You can see this when I try to fill the contour to create a mask using
cv2.fillPoly(mask, pts =[big_contour], color=(255,255,255))
it doesn't fill the leaf.
However I tried something although not perfect and has some background left but it crops the leaf to some extend.
import cv2
import numpy as np
img = cv2.imread("96Klg.jpg", -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 101, 3)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
blob = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
#kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
#blob = 255 - cv2.morphologyEx(blob, cv2.MORPH_CLOSE, kernel)
cnts = cv2.findContours(blob, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(cnts) == 2:
cnts = cnts[0]
else:
cnts = cnts[1]
big_contour = max(cnts, key = cv2.contourArea)
blob_area_thresh = 1000
blob_area = cv2.contourArea(big_contour)
if blob_area < blob_area_thresh:
print("Leaf is Too Small")
else:
#problem starts from here . i tested big_contour is just perfect by drawing on actual image
mask = np.ones_like(img)
mask.fill(255)
cv2.fillPoly(mask, pts =[big_contour], color=(0,0,0))
#cv2.drawContours(mask, big_contour, -1, (255,255,255), 1)
out = np.zeros_like(img)
out[mask == 255] = img[mask == 255]
width = int(gray.shape[1] * 0.25)
height = int(gray.shape[0] * 0.25)
dim = (width, height)
# resize image
resized = cv2.resize(out, dim, interpolation = cv2.INTER_AREA)
resizedmask = cv2.resize(mask, dim, interpolation = cv2.INTER_AREA)
cv2.imshow('gray',resized)
cv2.imshow('out',resizedmask)
Output

Display only bounding box region and neglect other part

I have this code of haarcascade which detect face and draw bounding box around it. I want to display only bounding box area in the original image in its original place and black out all other part just like we do it in color detection from opencv. Is there any way to do so?
cascPath = "haarcascade_frontalface_default.xml"
image = cv2.imread(imagePath)
faceCascade = cv2.CascadeClassifier(cascPath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
=
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags = cv2.CASCADE_SCALE_IMAGE
)
print("Found {0} faces!".format(len(faces)))
for ((x, y, w, h),i) in zip(faces,range(len(faces))):
a=cv2.rectangle(image, (x, y), (x+w, y+h), 2)
roi_color=image[y:y+h, x:x+w]
I would try to crop the ROI and then put it in a img which is a all black rectangle. In Pillow this is very easy to do.
as I don't have face images is hard to reproduce your code. I will use some random image but It should look something like this:
I put blue color just to highlight the background, but it's just a matter to change it to whatever color you want
from PIL import Image
img = Image.open('watch.jpeg', 'r')
img_w, img_h = img.size
left = img_w/8
top = img_h/8
right = 3 * img_w/8
bottom = 3 * img_h/8
cropped_img = img.crop((left, top, right, bottom))
cropped_img.save("cropped.png")
background = Image.new('RGB', (1440, 900), (0, 0, 255))
bg_w, bg_h = background.size
offset = ((bg_w - img_w) // 2, (bg_h - img_h) // 2)
background.paste(cropped_img, offset)
background.save('out.png')
input image:
output image:

How to clear numbers from the image using openCV?

I'm trying to remove numbers which are laying inside the circular part of image, numbers are in black in color and background varies between red,yellow, blue and green.
I am using opencv to remove those numbers. I used a mask which extracts numbers from image, with help of cv2.inpaint tried to remove those numbers from images.
For my further analysis I required to have clear image. But my current approach gives distorted image and numbers are not completely removed.
I tried changing the threshold values, lowering will neglect numbers from dark shaded area such as from green and red.
import cv2
img = cv2.imread('scan_1.jpg')
mask = cv2.threshold(img,50,255,cv2.THRESH_BINARY_INV)[1][:,:,0]
cv2.imshow('mask', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
dst = cv2.inpaint(img, mask, 5, cv2.INPAINT_TELEA)
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('ost_1.jpg',dst)
Input images: a) scan_1.jpg
b) scan_2.jpg
Output images: a) ost_1.jpg
b) ost_2.jpg
Expected Image: Circles can ignored, but something similar to it is required.
Here is my attempt, a better/easier solution might be acquired if you do not care about preserving texts outside of your circle.
import cv2
import numpy as np
# connectivity method used for finding connected components, 4 vs 8
CONNECTIVITY = 4
# HSV threshold for finding black pixels
H_THRESHOLD = 179
S_THRESHOLD = 255
V_THRESHOLD = 150
# read image
img = cv2.imread("a1.jpg")
img_height = img.shape[0]
img_width = img.shape[1]
# save a copy for creating resulting image
result = img.copy()
# convert image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# found the circle in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.7, minDist= 100, param1 = 48, param2 = 100, minRadius=70, maxRadius=100)
# draw found circle, for visual only
circle_output = img.copy()
# check if we found exactly 1 circle
num_circles = len(circles)
print("Number of found circles:{}".format(num_circles))
if (num_circles != 1):
print("invalid number of circles found ({}), should be 1".format(num_circles))
exit(0)
# save center position and radius of found circle
circle_x = 0
circle_y = 0
circle_radius = 0
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
for (x, y, radius) in circles:
circle_x, circle_y, circle_radius = (x, y, radius)
cv2.circle(circle_output, (circle_x, circle_y), circle_radius, (255, 0, 0), 4)
print("circle center:({},{}), radius:{}".format(x,y,radius))
# keep a median filtered version of image, will be used later
median_filtered = cv2.medianBlur(img, 21)
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of black color in HSV
lower_val = np.array([0,0,0])
upper_val = np.array([H_THRESHOLD,S_THRESHOLD,V_THRESHOLD])
# Threshold the HSV image to get only black colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# find connected components
components = cv2.connectedComponentsWithStats(mask, CONNECTIVITY, cv2.CV_32S)
# apply median filtering to found components
#centers = components[3]
num_components = components[0]
print("Number of found connected components:{}".format(num_components))
labels = components[1]
stats = components[2]
for i in range(1, num_components):
left = stats[i, cv2.CC_STAT_LEFT] - 10
top = stats[i, cv2.CC_STAT_TOP] - 10
width = stats[i, cv2.CC_STAT_WIDTH] + 10
height = stats[i, cv2.CC_STAT_HEIGHT] + 10
# iterate each pixel and replace them if
#they are inside circle
for row in range(top, top+height+1):
for col in range(left, left+width+1):
dx = col - circle_x
dy = row - circle_y
if (dx*dx + dy*dy <= circle_radius * circle_radius):
result[row, col] = median_filtered[row, col]
# smooth the image, may be necessary?
#result = cv2.blur(result, (3,3))
# display image(s)
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("found circle:", circle_output)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result for a1:

How do I crop the solar panels captured by drone?

I am currently working on solar panel cropping from the images taken by the drone(attaching sample image). I have tried using contours but there wasn't a proper outcome. It was not detecting all solar panels in the image some of them were missing. I struck here itself. How do I proceed further? Please help me with this problem.
Thank you,
Sample Code:
import cv2
import numpy as np
img = cv2.imread('D:\\SolarPanel Images\\solarpanel.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
edges = cv2.Canny(blur,100,200)
th3 = cv2.adaptiveThreshold(edges,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)
im2, contours, hierarchy = cv2.findContours(th3, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("Len of contours",len(contours)
try: hierarchy = hierarchy[0]
except: hierarchy = []
height, width, = edges.shape
min_x, min_y = width, height
max_x = max_y = 0
# computes the bounding box for the contour, and draws it on the image,
for contour, hier in zip(contours, hierarchy):
area = cv2.contourArea(contour)
if area > 10000 and area < 250000:
(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)
cv2.imshow('cont imge', img)
cv2.waitKey(0)
To find contours in images where the object of importance is clearly distinguishable from the background, you can always try converting the image to HSV format and then contour. I did the following:
import cv2
import numpy as np
img = cv2.imread('panel.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
ret,thresh1 = cv2.threshold(hsv[:,:,0],100,255,cv2.THRESH_BINARY)
im2, contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
try: hierarchy = hierarchy[0]
except: hierarchy = []
for contour, hier in zip(contours, hierarchy):
area = cv2.contourArea(contour)
if area > 10000 and area < 250000:
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img,[box],0,(0,0,255),2)
cv2.imshow('cont imge', img)
cv2.waitKey(0)
cv2.imwrite("result.jpg",img)
Result:

drawing lines and distance to them on image opencv python

I encountered such a problem: I can not draw lines on the image where the color was determined, and also find out the distance to this place. Help to make it as in the image below:
My code:
import cv2
import numpy as np
from PIL import ImageGrab
while True:
screen = np.array(ImageGrab.grab(bbox=(0,40,800,640)))
rgb_screen = cv2.cvtColor(screen, cv2.COLOR_BGR2RGB)
lower = np.array([72, 160, 160])
upper = np.array([112, 249, 249])
mask = cv2.inRange(rgb_screen, lower, upper)
output = cv2.bitwise_and(rgb_screen, rgb_screen, mask=mask)
cv2.imshow('window', output)
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
I can't help much because I do not have your original image. But you could read my code and maybe get an idea. For distance I do not know what u mean, so I made an example on how to get distance of top left corner to bottom left. You can apply other points or apply it as ratio depanding on your demands.
import cv2
import numpy as np
img = cv2.imread('untitled.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, threshold = cv2.threshold(gray,150,255,cv2.THRESH_BINARY)
im, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
area = sorted(contours, key=cv2.contourArea, reverse=True)
c = area[0]
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
print(box)
box = np.int0(box)
cv2.drawContours(img,[box],0,(0,0,255),2)
extreme_left = tuple(c[c[:, :, 0].argmin()][0])
extreme_top = tuple(c[c[:, :, 1].argmin()][0])
x1 = box[1,0]
y1 = box[1,1]
x2 = box[0,0]
y2 = box[0,1]
distance = np.sqrt( (x1 - x2)**2 + (y1 - y2)**2 )
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'Distance: '+str(distance),(1,300), font, 0.5,(255,255,255),2,cv2.LINE_AA)
cv2.circle(img, (x2,y2), 5, (255, 0, 0), -1)
cv2.circle(img, (x1,y1), 5, (255, 0, 0), -1)
cv2.imshow('image', img)

Resources