detecting rectangle contours not accurate in UML diagrams, openCV - opencv

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)

Related

Identify visible stones in the image - Any Approach in OpenCV & Deeplearning

I have samples images of stones present in the images. I need to identify the visible stones only. The approach which I tried is threshold based filtering and detecting cv2.contours. Also, I am looking into ENet Architecture for semantic segmentation based deep learning approach. The samples images are below.
Example image1:
Example image2:
The code which I tried for contour based detection is as below
image = cv2.imread(os.path.join(img_path, img_name2))
# threshold based customization
lower_bound = np.array([0, 0, 0])
upper_bound = np.array([250,55,100])
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
#masking the image using inRange() function
imagemask = cv2.inRange(hsv, lower_bound, upper_bound)
plt.figure(figsize=(20,10))
plt.imshow(imagemask, cmap="gray")
# erode and diluation to smoothen the edeges
final_mask = cv2.erode(imagemask, np.ones((3, 3), dtype=np.uint8))
final_mask = cv2.dilate(imagemask, np.ones((5, 5), dtype=np.uint8))
# find contours based on the mask
contours = cv2.findContours(final_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# draw contours
img_conts = cv2.drawContours(image.copy(), contours[0], -1, (0,255,0), 3)
plt.figure(figsize=(20,10))
plt.imshow(img_conts, cmap="gray")
The sample contours ouput. I know that the thresholds can be tuned for better results here.
But, what I am looking here for the any better approach or solution can work in this heavy environment for detection small particles like stones. Any ideas to solve in better way?
Here is how you can use the Canny edge detector to detect the rocks in your images:
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 103, 255, cv2.THRESH_BINARY)
img_blur = cv2.GaussianBlur(thresh, (23, 23), 0)
img_canny = cv2.Canny(img_blur, 65, 0)
img_dilate = cv2.dilate(img_canny, None, iterations=2)
return cv2.erode(img_dilate, None, iterations=2)
imgs = [cv2.imread("image1.jpg"), cv2.imread("image2.jpg")]
for i, img in enumerate(imgs):
contours = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[0]
cv2.drawContours(img, contours, -1, (0, 255, 0), 1)
cv2.imshow(str(i), img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output for sample images 1 and 2:
You can also tweak the parameters using OpenCV trackbars using the code below:
import cv2
import numpy as np
from random import randint, sample
def process(img, c_t1, c_t2):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 103, 255, cv2.THRESH_BINARY)
img_blur = cv2.GaussianBlur(thresh, (23, 23), 0)
img_canny = cv2.Canny(img_blur, c_t1, c_t2)
img_dilate = cv2.dilate(img_canny, None, iterations=2)
return cv2.erode(img_dilate, None, iterations=2)
def show(imgs, win="Image", scale=1):
imgs = [cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) if len(img.shape) == 2 else img for img in imgs]
img_concat = np.concatenate(imgs, 1)
h, w = img_concat.shape[:2]
cv2.imshow(win, cv2.resize(img_concat, (int(w * scale), int(h * scale))))
d = {"Canny Threshold 1": (65, 500),
"Canny Threshold 2": (0, 500)}
imgs = [cv2.imread("image1.jpg"), cv2.imread("image2.jpg")]
cv2.namedWindow("Track Bars")
for i in d:
cv2.createTrackbar(i, "Track Bars", *d[i], id)
while True:
c_t1, c_t2 = (cv2.getTrackbarPos(i, "Track Bars") for i in d)
for i, img in enumerate(imgs):
img_copy = img.copy()
processed = process(img, c_t1, c_t2)
contours = cv2.findContours(processed, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[0]
cv2.drawContours(img_copy, contours, -1, (0, 255, 0), 1)
show([img_copy, processed], str(i))
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
Output:
(Click image to expand)

Can not detect the marked square using OpenCV

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)

Remove coins shadow using opencv

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)

Skewed image - Not able to detect horizontal lines using CV2

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:

opencv area of contour

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

Resources