Hello guys. Today we will learn a pattern matching algorithm in python using the OpenCV library. Check out the video below to get a gist of what we are going to build. I have black and white grid patterns with white as background and black foreground.
As usual, we will start by first importing the required libraries and define a variable to capture the video from my webcam.
import cv2 import numpy as np cap = cv2.VideoCapture(0)
Now, we write a while loop and capture the image frames. Also, we need to mirror the frames so that we can see it right.
while 1: ret, frame = cap.read() ##Read image frame frame = cv2.flip(frame, +1) ##Mirror image frame if not ret: ##If frame is not read then exit break if cv2.waitKey(1) == ord('s'): ##While loop exit condition break
In the third part, we have to convert the image frames into a binary image. But why binary images? You must ask.
1. We want to build a fast algorithm, so do not burden the processor with unnecessary calculations.
2. The grid patterns are black and white in color. So there is no point in using a color image for pattern matching.
Suppose your ROI(defined later) is 400 x 400 pixels and you use the color image directly for pattern matching then, just take a look at the amount of calculation your processor will have to go through.
color image size = 400 x 400 x 3 = 480,000
These are the number of elements that your processor will have to deal with. Again each element will have 256 different color values.
So let us convert the colored image into a binary image. For that, we will first have to convert image into grayscale and then threshold it.
frame2 = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) ret, thresh1 = cv2.threshold(frame2, 150, 255, cv2.THRESH_BINARY)
To convert the image to grayscale, we have to use the cvtColor method. This method takes two arguments. The image we want to convert and the method to convert it into grayscale. To convert the gray image into binary we need to use the threshold method. This method takes 4 arguments. The image we want to convert, threshold value, maximum value, and thresholding method. After using the above method the result will look as shown below.
Now let's define the region of interest for our image. But what is Region Of Intrest(ROI)? ROI is the part of an image where we actually want to perform our computer vision algorithm. In general, not the whole image is useful. Most of it is noise.
My image size is 1280 x 720 px. So I defined the ROI of 180 x 200 px. We will extract ROI from the original image and perform the next steps on it.
x1 = 750 x2 = 930 y1 = 100y2 = 300
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 3)
cv2.imshow('Pattern Template', thresh1[y1:y2, x1:x2])
(x1,y1) and (x2, y2) are the start and end coordinates of the ROI.
Ready for the next step? This is an interesting one. But first, take a look at the amount of calculation that the CPU has to go through right now.
Total matrix elements = 180 x 200 = 36,000
Still, this is a large number too deal with. On PC this will run smoothly but on stand-alone systems dealing with 36000 matrix elements will take a considerable amount of time. The solution is to again reduce the number of pixels we are dealing with. But how?
Take a look at the pattern below.
I hope you got the idea. We are going to reduce 180 x 200 px to 9 x 9 px. Just divide 180 and 200 into 9 equal parts. That means each block of 20x22 pixels will contribute to a single element in the 9x9 matrix. The following code will do this task.
Pattern_Matrix = np.zeros((9, 9), np.uint8)
width = int((x2-x1)/9) height = int((y2-y1)/9) W1 = x1 H1 = y1 W2 = x1 + width H2 = y1 + height for i in range(0, 9): for j in range(0, 9): Sum = np.sum(thresh1[H1: H2, W1:W2]) ##56100 if Sum > 56100: Pattern_Matrix[i, j] = 255 else: Pattern_Matrix[i, j] = 0 W1 = W2 W2 = W2 + width #print('W1 = ' +str(W1)) #print('W2 = ' +str(W2)) #print('H1 = ' +str(H1)) #print('H2 = ' +str(H2)) #print("Sum = "+str(Sum)) Sum = 0 W1 = x1 W2 = x1 + width H1 = H2 H2 = H2 + height
You must ask what is that 56100 number in if condition? Well, if you take a block of 20x22 pixels and if all of them are white in color(i.e. 255 value) then what will be the sum of all those pixel values?
Sum = 20 x 22 x 255 = 112,200
If you divide this number by 2 we get 56100. This simply means that if in a 20x22 pixel block more than 50% pixels are white then consider that whole block as white and vise versa. We store the result in another 9x9 matrix.
And now it's time to compare the patterns. For that first, we need to save templates of patterns in our code.
Pattern_1 = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 255, 255, 255, 0, 0, 0, 255, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 255, 255, 255, 0, 255, 255, 255, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 255, 0, 0, 0, 255, 255, 255, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], ], dtype=np.uint8) Pattern_2 = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 255, 0, 255, 255, 255, 0, 255, 0], [0, 255, 255, 0, 255, 0, 255, 255, 0], [0, 255, 255, 255, 0, 255, 255, 255, 0], [0, 255, 255, 0, 255, 0, 255, 255, 0], [0, 255, 0, 255, 255, 255, 0, 255, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], ], dtype=np.uint8) Pattern_3 = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 255, 0, 0, 0, 0, 0, 255, 0], [0, 255, 0, 255, 255, 255, 0, 255, 0], [0, 255, 0, 255, 255, 255, 0, 255, 0], [0, 255, 0, 255, 255, 255, 0, 255, 0], [0, 255, 0, 0, 0, 0, 0, 255, 0], [0, 255, 255, 255, 255, 255, 255, 255, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], ], dtype=np.uint8) Pattern_4 = np.array([ [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 255, 255, 255, 255, 255, 0, 255, 0], [0, 255, 0, 255, 255, 255, 0, 255, 0], [0, 255, 255, 255, 255, 255, 0, 255, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 255, 0, 255, 255, 255, 255, 255, 0], [0, 255, 0, 255, 255, 255, 0, 255, 0], [0, 255, 0, 255, 255, 255, 255, 255, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], ], dtype=np.uint8)
The following code compares the image patterns with the saved templates. Remember, a match is found only when there is more than a 90% match. Also, all the operations are performed on the binary image but the results are shown on the original image.
##*******************Pattern Comparison*********************## for a in range(0, 9): for b in range (0, 9): if Pattern_Matrix[a, b] == Pattern_1[a, b]: Result_Count1 = Result_Count1 + 1 if Pattern_Matrix[a, b] == Pattern_2[a, b]: Result_Count2 = Result_Count2 + 1 if Pattern_Matrix[a, b] == Pattern_3[a, b]: Result_Count3 = Result_Count3 + 1 if Pattern_Matrix[a, b] == Pattern_4[a, b]: Result_Count4 = Result_Count4 + 1 ##print(Result_Count) Match_Percentage1 = (Result_Count1 / 81) * 100Match_Percentage2 = (Result_Count2 / 81) * 100Match_Percentage3 = (Result_Count3 / 81) * 100Match_Percentage4 = (Result_Count4 / 81) * 100 Result_Count1 = 0Result_Count2 = 0Result_Count3 = 0Result_Count4 = 0 if Match_Percentage1 > 90: S = "Pattern 1 " + str(round(Match_Percentage1, 1)) + '%' cv2.putText(frame, S, (5, 50), font, 2, (0, 0, 255), 2, cv2.LINE_AA) elif Match_Percentage2 > 90: S = "Pattern 2 " + str(round(Match_Percentage2, 1)) + '%' cv2.putText(frame, S, (5, 50), font, 2, (0, 0, 255), 2, cv2.LINE_AA) elif Match_Percentage3 > 90: S = "Pattern 3 " + str(round(Match_Percentage3, 1)) + '%' cv2.putText(frame, S, (5, 50), font, 2, (0, 0, 255), 2, cv2.LINE_AA) elif Match_Percentage4 > 90: S = "Pattern 4 " + str(round(Match_Percentage4, 1)) + '%' cv2.putText(frame, S, (5, 50), font, 2, (0, 0, 255), 2, cv2.LINE_AA) else: cv2.putText(frame, "No Match Found", (5, 50), font, 2, (0, 0, 255), 2, cv2.LINE_AA)
This is it, guys. Let me know if you were able to do it. Enjoy Pattern Matching...!!!
For Full Code:- https://github.com/vibhor69meshram/Pattern-Matching
Great, Thanks Very logical ever seen on any web and well explained
ReplyDelete