I need to detect the option marked using OpenCV. Currently, I have been able to detect all the squares but the one that is marked. I have done this using the following piece of code.
canny = (cv2.Canny(roi_box, 30, 100))
cv2_imshow(canny)
img = roi_box.copy()
contours, heirarchy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cntsSorted = sorted(contours, key=lambda x:cv2.contourArea(x))
print("contours %i" % len(contours))
for i in range(45, 0, -1):
cv2.drawContours(img, cntsSorted[i], -1, (0, 255,0), 4)
if (cv2.contourArea(cntsSorted[i]) > 300):
cv2_imshow(img)
The area of the square that is marked is around 50. Can someone suggest to me how can I solve this problem?
Find the features of your image.
For each channel (blue, green, red), you can apply medianBlur, Canny, and bitwise-or together.
img = cv2.imread("npDro.png")
bor = np.zeros(img.shape[:2], dtype="uint8")
for chn in cv2.split(img):
chn = cv2.medianBlur(chn, 11)
cny = cv2.Canny(chn, 50, 200)
bor = cv2.bitwise_or(bor, cny)
Result: (rescaled: w/2, h/2)
Applying medianBlur, Canny and bitwise-or operations are not must-do pre-processing. However, applying only Canny or only MedianBlur was not useful in this example. You may find another combination. The above code is just an example.
Find contours
cnt = cv2.findContours(bor.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = imutils.grab_contours(cnt)
cnt = sorted(cnt, key=cv2.contourArea, reverse=True)[:4]
The reason I sorted the contours is that the text value If is also detected. Therefore I get only the first four contours which are squares.
For each detected contourdraw the rectangle.
for (i, c) in enumerate(cnt):
M = cv2.moments(c)
cX = int(M["m30"] / M["m20"])
cY = int(M["m03"] / M["m02"])
cv2.rectangle(img,
pt1=(cX-30, cY-30),
pt2=(cX+20, cY+20),
color=(255, 0, 0), thickness=3)
Result:
Code:
import cv2
import imutils
import numpy as np
img = cv2.imread("npDro.png")
bor = np.zeros(img.shape[:2], dtype="uint8")
for chn in cv2.split(img):
chn = cv2.medianBlur(chn, 11)
cny = cv2.Canny(chn, 50, 200)
bor = cv2.bitwise_or(bor, cny)
cnt = cv2.findContours(bor.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnt = imutils.grab_contours(cnt)
cnt = sorted(cnt, key=cv2.contourArea, reverse=True)[:4]
for (i, c) in enumerate(cnt):
M = cv2.moments(c)
cX = int(M["m30"] / M["m20"])
cY = int(M["m03"] / M["m02"])
cv2.rectangle(img,
pt1=(cX-30, cY-30),
pt2=(cX+20, cY+20),
color=(255, 0, 0), thickness=3)
cv2.imshow("img", img)
cv2.waitKey(0)
Related
I am trying to count how many coins there are in the image using the latest version of OpenCV, but I am struggling with the shadows.
The Canny Edge detector method is being used but as you can see in the second image, it is not working as expected because of the shadows... Any ideas about how I could deal with this problem?
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
median = np.median(image)
lower = int(max(0, 0.67 * median))
upper = int(min(255, (1.33) * median))
canny = cv2.Canny(blurred, lower, upper)
contours, hierachy = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
coins = cv2.drawContours(image, contours, -1, (0, 255, 0), 2)
cv2.imshow("Coins", coins)
You can use the coin selection by color.
import cv2 as cv
import numpy as np
low_H = 0
low_S = 50
low_V = 0
high_H = 255
high_S = 255
high_V = 255
frame = cv.imread('PzB9I.png')
frame_HSV = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
frame_threshold = cv.inRange(frame_HSV, (low_H, low_S, low_V), (high_H, high_S, high_V))
# filling holes
im_floodfill = frame_threshold.copy()
h, w = frame_threshold.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
cv.floodFill(im_floodfill, mask, (0,0), 255);
im_floodfill_inv = cv.bitwise_not(im_floodfill)
mask = frame_threshold | im_floodfill_inv
# find contours
contours, hierachy = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
coins = cv.drawContours(frame, contours, -1, (0, 255, 0), 1)
cv.imshow("Coins", coins)
In this image I am trying to detect horizontal lines. The code works well when image is not skewed. However, it is not working on such skewed images. I have tried this method to detect the right angle by histogram but many times is actually making it more skewed - python-opencv-skew-correction-for-ocr
Below is code to detect horizontal lines:
gray=cv2.cvtColor(img_final_bin,cv2.COLOR_BGR2GRAY)
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (100,1))
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
detected_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts, hierarchy = cv2.findContours(detected_lines, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
boundingBoxes = [list(cv2.boundingRect(c)) for c in cnts]
Below is the code for skew correction, which is giving wrong results to me:
def correct_skew(image, delta=0.001, limit=3):
def determine_score(arr, angle):
data = inter.rotate(arr, angle, reshape=False, order=0)
histogram = np.sum(data, axis=1)
score = np.sum((histogram[1:] - histogram[:-1]) ** 2)
return histogram, score
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
print("thresh", thresh.shape)
thresh1 = thresh[0:500, 0:500]
print("thresh1", thresh1.shape)
scores = []
angles = np.arange(-limit, limit + delta, delta)
for i, angle in enumerate(angles):
histogram, score = determine_score(thresh1, angle)
scores.append(score)
# if i%100 == 0:
# print(score, angle, len(angles), i)
best_angle = angles[scores.index(max(scores))]
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, best_angle, 1.0)
rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, \
borderMode=cv2.BORDER_REPLICATE)
return best_angle, rotated
Python Wand, which is based upon ImageMagick has a deskew function.
Input:
from wand.image import Image
from wand.display import display
with Image(filename='table.png') as img:
img.deskew(0.4*img.quantum_range)
img.save(filename='table_deskew.png')
display(img)
Result:
I want to extract some rectangles at the top from a UML sequence diagram in jpg format by using OpenCV.
The algorithm I use finds way too many rectangles that are super small and not needed.
I think the mess up is somewhere in the beginning of the code where I apply canny edge detection but I am not sure.
I want to capture only the big rectangles from the top and center.
Thanks for any help.
import cv2
import numpy as np
import imutils
image = cv2.imread("./diagrams/sd2.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 90, 150, 3)
cnts = cv2.findContours(edges, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
cv2.drawContours(image, cnts, -1, (0, 255, 0), 1)
def detect(c):
shape = "unidentified"
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.03 * peri, True)
if len(approx) == 4:
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle"
return shape
# loop over the contours
for c in cnts:
M = cv2.moments(c)
if M["m00"] != 0:
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
shape = detect(c)
c = c.astype("float")
c = c.astype("int")
if(shape == "rectangle"):
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (0, 0, 0), 2)
# show the output image
cv2.imshow("Image", image)
cv2.waitKey(0)
I am trying to find the contours of an animal from a picture. Let's assume it is a chicken. From the picture I could find its contours but they aren't closed. Also, I am getting a lot of noise from the background which is white ( same as the chicken).
I am using a simple code found on stackoverflow.
import numpy as np
import cv2
img = cv2.imread('lateral.jpg')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# blurred = cv2.GaussianBlur(imgray, (5, 5), 0)
# edged = cv2.Canny(blurred, 10, 11) # 10 and 40 to be more perceptive
# contours_canny= cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]
edges = cv2.Canny(imgray, 10,30)
cv2.imshow('edges', edges)
k = cv2.waitKey()
Is there a way to find just the contour of this chicken?
Thanks in advance.
Finding contour is quite easy. The problem is that your image has low contrast between the chicken and the background. So, your idea of using canny edges was not bad, it just needed some post processing.
I guess this is what you are looking for:
import cv2
import numpy as np
image = cv2.imread("./chicken.jpg", cv2.IMREAD_COLOR)
image = cv2.resize(image, (0,0), fx=0.5, fy=0.5)
imgray = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)[...,0]
edges = cv2.Canny(imgray, 10,30)
blurred = cv2.GaussianBlur(edges, (9, 9), 0)
clahe = cv2.createCLAHE(clipLimit=5.0, tileGridSize=(32,32))
contrast = clahe.apply(blurred)
ret, thresh = cv2.threshold(contrast, 20, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
_, contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
maxArea = 0
best = None
for contour in contours:
area = cv2.contourArea(contour)
print (area)
if area > maxArea :
maxArea = area
best = contour
cv2.drawContours(image, [best], 0, (0, 0, 255), -1)
while True:
cv2.imshow("result", image)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
I have the above number plate image with me. My goal is to segment each character individually and pass into my neural network. I have tried to find countours and use bounding rectangles to segment these characters using the following code:
img = cv2.imread('download.jpeg')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_img = cv2.GaussianBlur(gray_img, (5,5), 0)
ret, im_th = cv2.threshold(gray_img, 90, 255, cv2.THRESH_BINARY_INV)
im_th = cv2.adaptiveThreshold(gray_img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,75, 10)
im_th = cv2.bitwise_not(im_th)
ctrs, hier = cv2.findContours(im_th.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, ctrs, -1, (0,255,0), 3)
rects = [cv2.boundingRect(ctr) for ctr in ctrs]
print len(rects)
for rect in rects:
cv2.rectangle(img,(rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3]), (0,255,0),3)
length = int(rect[3] * 1.6)
pt1 = int(rect[1] + rect[3] // 2 - length // 2)
pt2 = int(rect[0] + rect[2] // 2 - length // 2)
roi = img[pt1:pt1+length, pt2:pt2+length]
The above code creates regions that include bounding rectangles other than the characters. Although I could manually filter out these regions, it would vary from image to image. How would I go about this if I need to only extract the regions with characters?
#read image
img = cv2.imread('input_image.png')
#grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.waitKey(0)
#binarize
ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY_INV)
cv2.waitKey(0)
#find contours
im2,ctrs, hier = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
#sort contours
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])
for i, ctr in enumerate(sorted_ctrs):
# Get bounding box
x, y, w, h = cv2.boundingRect(ctr)
# Getting ROI
roi = img[y:y+h, x:x+w]
# show ROI
#cv2.imwrite('roi_imgs.png', roi)
cv2.imshow('charachter'+str(i), roi)
cv2.rectangle(img,(x,y),( x + w, y + h ),(90,0,255),2)
cv2.waitKey(0)
cv2.imshow('marked areas',img)
cv2.waitKey(0)