OpenCV: MatchShapes totally wrong - python

I'm using the cv2.matchShapes() function in OpenCV to find the shape in an image most similar to another shape.
It's giving me some weird results, e.g. when I match the shape (a circular coin) against the shape of a sweater it returns 0.09, a close score, and a better score than when it matches against an actual coin.
This is my code:
for contour in cnts:
box = bounding_box(contour)
orig = image.copy()
cv2.drawContours(orig, [contour, reference_contour],
-1, (0, 0, 255), 2)
cv2.putText(
orig, "SIMILARITY: {0:.4f}".format(
cv2.matchShapes(
contour, reference_contour, 1, 0.0)
),
(10, 70), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 0, 0), 2)
cv2.imshow("coin_metric_cnn.py", orig)
cv2.waitKey(0)
Am I doing something wrong?

Related

Shapes(rectangle,diamond) detection using OpenCV library

I have two connected rectangles with a diamond in between them. In Entity Relationship Diagram (ERD) we have the same scenario. My aim is to detect both the rectangles and the diamond which is in between them.
import cv2
import numpy as np
from matplotlib import pyplot as plt
# reading image
img = cv2.imread('pic4.jpeg')
# converting image into grayscale image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# setting threshold of gray image
_, threshold = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# using a findContours() function
contours, _ = cv2.findContours(
threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
i = 0
# list for storing names of shapes
for contour in contours:
# here we are ignoring first counter because
# findcontour function detects whole image as shape
if i == 0:
i = 1
continue
# cv2.approxPloyDP() function to approximate the shape
approx = cv2.approxPolyDP(
contour, 0.01 * cv2.arcLength(contour, True), True)
# using drawContours() function
cv2.drawContours(img, [contour], 0, (0, 0, 0), 5)
# finding center point of shape
M = cv2.moments(contour)
if M['m00'] != 0.0:
x = int(M['m10']/M['m00'])
y = int(M['m01']/M['m00'])
# putting shape name at center of each shape
if len(approx) == 3:
cv2.putText(img, 'Triangle', (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
elif len(approx) == 4:
cv2.putText(img, 'Rectangle', (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
elif len(approx) == 5:
cv2.putText(img, 'Pentagon', (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
elif len(approx) == 6:
cv2.putText(img, 'Hexagon', (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
else:
cv2.putText(img, 'circle', (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
# displaying the image after drawing contours
cv2.imshow('pic4', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Input image
Output image after shapes detection
The output image is not detecting correct shape. It must have to write a rectangle once in both rectangle shapes.

Trying to Capture Shapes in an image in openCV Python

i have the image shown below and i am attempting to just capture the Squares and place a green line around them any help or direction would be great.
import numpy as np
import cv2
img = cv2.imread('beef.png')
imgGry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret , thrash = cv2.threshold(imgGry, 240 , 255, cv2.CHAIN_APPROX_NONE)
contours , hierarchy = cv2.findContours(thrash, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for contour in contours:
approx = cv2.approxPolyDP(contour, 0.01* cv2.arcLength(contour, True), True)
cv2.drawContours(img, [approx], 0, (0, 0, 0), 5)
x = approx.ravel()[0]
y = approx.ravel()[1] - 5
x, y , w, h = cv2.boundingRect(approx)
aspectRatio = float(w)/h
print(aspectRatio)
if aspectRatio >= 0.95 and aspectRatio < 1.05:
cv2.putText(img, "square", (x, y), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0))
else:
cv2.putText(img, "rectangle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0))
cv2.imshow('shapes', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
I don't know exactly what your problem is (are the contours wrong? Nothing is being drawn on the image? etc) but here are some guesses.
To draw lines around the contours, it looks like you are currently using white (0,0,0) to outline it. -1 is to draw all contours to see if you are getting any results.
Also some cv2 functions are destructive so I never use img when I am creating the output image. I would imShow and drawContours on a copy of the original img.
cv2.drawContours(img, contours, -1, GREEN, 5)

OpenCV addweight does not draw an overlay

I am trying to overlay a transparent red rectangle box on top of an image and return the whole thing from a function. For this, I'm using cv2.addWeight(). I have a previous PIL rectangle function call that draws a rectangle outline to mark where the output should go in case it doesn't appear. However, nothing is drawn unfortunately. This is my output when I use the overlay function:
This is my code:
def overlay(path, lg_x, lg_y):
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
overlay = cv2.rectangle(img, (0, 0), (lg_x, lg_y), (0, 0, 255), cv2.FILLED)
result = cv2.addWeighted(img, 1, overlay, 0.5, 1)
pil_image = Image.fromarray(np.uint8(result))
return pil_image
First, I have to say that I didn't manage to reproduce the same error as yours using your function. There must be another problem somewhere. I had another error: a fully filled rectangle.
There is a problem in your code that does that:
overlay = cv2.rectangle(img, (0, 0), (lg_x, lg_y), (0, 0, 255), cv2.FILLED) is also adding a rectangle over img therefore no matter what weights you put in cv2.addWeighted, your image will be full.
Here is a code that worked for me:
def overlay(path, lg_x, lg_y):
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
overlay = cv2.rectangle(img.copy(), (0, 0), (lg_x, lg_y), (0, 0, 255), cv2.FILLED)
alpha = 0.2
output = cv2.addWeighted(img, 1-alpha, overlay, alpha, 0)
output = cv2.cvtColor(np.uint8(output), cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(output)
return pil_image
I used cv2.rectangle on a copy of the image, and changed the way you applicate the weights so that the total is always 1.0 (Here we have 20% opacity for the rectangle).
The Image.fromarray somehow made my colors wrong, I guess there must be another problem but since it wasn't the main problem, I didn't check deeper.
Try:
result = cv2.addWeighted(img, 0.5, overlay, 0.5, 0)

How can i get the rgb color values from inside of a contour in image using opencv?

I know here already some questions were asked but they did't help me to solve my problem. I will appreciate any help to solve my problem.
I'm new to opencv.
I have an image and apply some code to get contours from image. Now i want to get the RGB color values from detected contours. How can i do that?
I do research on it and find that it could be solved by using contours so i try to implement contours and now finally i want to get the color values of the contours.
Here is my Code:
import cv2
import numpy as np
img = cv2.imread('C:/Users/Rizwan/Desktop/example_strip1.jpg')
img_hsv = cv2.cvtColor(255-img, cv2.COLOR_BGR2HSV)
lower_red = np.array([40, 20, 0])
upper_red = np.array([95, 255, 255])
mask = cv2.inRange(img_hsv, lower_red, upper_red)
contours, _ = cv2.findContours(mask, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
color_detected_img = cv2.bitwise_and(img, img, mask=mask)
print(len(contours))
for c in contours:
area = cv2.contourArea(c)
x, y, w, h = cv2.boundingRect(c)
ax = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 2)
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
box = np.int0(box)
im = cv2.drawContours(color_detected_img, [box], -1, (255, 0, 0), 2)
cv2.imshow("Cropped", color_detected_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
I expect the output should be the RGB values of the detected color inside the contours.
As asked in the comments, here's a possible solution to extract the BGR(!) values from the pixels of an image inside a before found contour. The proper detecting of the desired, colored stripes is omitted here as also discussed in the comments.
Having an image and a filled mask of a contour, for example from cv2.drawContours, we can simply use NumPy's boolean array indexing by converting the (most likely uint8) mask to an bool_ array.
Here's a short code snippet, that uses NumPy's savetxt to store all values in some txt file:
import cv2
import numpy as np
# Some dummy image
img = np.zeros((100, 100, 3), np.uint8)
img = cv2.rectangle(img, (0, 0), (49, 99), (255, 0, 0), cv2.FILLED)
img = cv2.rectangle(img, (50, 0), (99, 49), (0, 255, 0), cv2.FILLED)
img = cv2.rectangle(img, (50, 50), (99, 99), (0, 0, 255), cv2.FILLED)
# Mask of some dummy contour
mask = np.zeros((100, 100), np.uint8)
mask = cv2.fillPoly(mask, np.array([[[20, 20], [30, 70], [70, 50], [20, 20]]]), 255)
# Show only for visualization purposes
cv2.imshow('img', img)
cv2.imshow('mask', mask)
# Convert mask to boolean array
mask = np.bool_(mask)
# Use boolean array indexing to get all BGR values from img within mask
values = img[mask]
# For example, save values to txt file
np.savetxt('values.txt', values)
cv2.waitKey(0)
cv2.destroyAllWindows()
The dummy image looks like this:
The dummy contour mask looke like this:
The resulting values.txt has some >1000 entries, please check yourself. Attention: Values are BGR values; e.g. prior converting the image to RGB is needed to get RGB values.
Hope that helps!

What is the difference between contours and contours[0] when len(contours)=1?

I want to find the contours of an image to then draw its convex hull. What I am doing is loading the image, threshold it, find its contours and then draw the convex hull.
gray = cv2.imread(test_paths[i], 0)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
The number of contours detected is equal to 1.
The problem comes when I try to plot the contours, if I do
cv2.drawContours(cnt_dst, cnt, -1, (255, 0, 0), 3)
plt.imshow(cnt_dst)
If I change the code to the following:
cv2.drawContours(cnt_dst, contours, 0, (255, 0, 0), 3)
plt.imshow(cnt_dst)
The contours are different:
Note that I am getting the same (nice) result with this:
cv2.drawContours(cnt_dst, contours, -1, (255, 0, 0), 3)
Any ideas of why this is happening?
cv2.drawContours(cnt_dst, contours, 0, (255, 0, 0), 3) or cv2.drawContours(cnt_dst, contours, -1, (255, 0, 0), 3) are the same in that case
-1 tells opencv to draw all the contours of the contours array, and 0 tells it to draw the first contour of the contours array.
Since there's only one contour, the result is the same.
The other call cv2.drawContours(cnt_dst, cnt, -1, (255, 0, 0), 3) is probably bogus / should be better checked on the opencv side.
In this blog it indicates:
Now you want to draw "cnt" only. It can be done as follows:
cv2.drawContours(im,[cnt],0,(255,0,0),-1)
Note the square bracket around "cnt". Third argument set to 0, means only that particular contour is drawn.

Categories

Resources