Calculate new coordinates of keypoints after transformation - opencv

How to get the new coordinates of the points a and b in this exemple after a transformation M (40 degrees counter-clockwise rotation) ?
import cv2
cap = cv2.VideoCapture("http://i.imgur.com/7G91d2im.jpg")
a, b = (100, 100), (200, 200)
if cap.isOpened():
ret, im = cap.read()
rows, cols = im.shape[:2]
im_keypoints = im.copy()
for point in [a, b]:
cv2.circle(im_keypoints, point, 6, (0, 0, 255), -1)
cv2.imwrite("im_keypoints.jpg", im_keypoints)
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 40, 1)
im_rotated = cv2.warpAffine(im, M, (cols, rows))
cv2.imwrite("im_rotated.jpg", im_rotated)

M is a 2 by 3 rotation matrix, so all you need to do it apply M to your points.
im_rotated_keypoints = im_rotated.copy()
for point in [a, b]:
# Convert to homogenous coordinates in np array format first so that you can pre-multiply M
rotated_point = M.dot(np.array(point + (1,)))
cv.circle(im_rotated_keypoints, (int(rotated_point[0]), int(rotated_point[1])), 6, (0, 0, 255), -1)
And you should be able to see

Related

Opencv, combination of chroma key correction and contour detection gives unexpected result

I am doing contour detection on a chroma key corrected image. Everything works fine when I filter out just the blues, but when I try to get a better chroma correction by also filtering the reds, suddenly my contours cannot be detected anymore. Anyone any suggestions?
WITH BLUE FILTER:
img = cv2.imread('yellowcropped.jpg', 1)
lower_blue = np.array([0, 0, 15]) ##[R value, G value, B value]
upper_blue = np.array([255, 255, 60])
mask = cv2.inRange(image_copy, lower_blue, upper_blue)
WITH BLUE AND RED FILTER:
lower_blue = np.array([180, 0, 15]) ##[R value, G value, B value]
upper_blue = np.array([255, 255, 60])
(notice the top left image get's much crisper, but NO CONTOURS are detected anymore.)
BELOW MY CONTOUR FINDING CODE:
imgContour = image_original.copy()
imgBlur = cv2.GaussianBlur(img, (7, 7), 1)
imgGray = imgBlur
imgCanny = cv2.Canny(imgGray,threshold1,threshold2)
kernel = np.ones((5, 5))
imgDil = cv2.dilate(imgCanny, kernel, iterations=1)
getContours(imgDil,imgContour)
def getContours(img,imgContour):
""" DRAWS AND FINDS CONTOURS, THEN RETURNS a list of lists incl x0, y0, w, h"""
contour_list = []
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# print('contours:', contours)
for cnt in contours:
area = cv2.contourArea(cnt)
areaMin = cv2.getTrackbarPos("Area", "Parameters")
if area > areaMin and area < 5000:
cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 7)
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
# print(len(approx))
x , y , w, h = cv2.boundingRect(approx)
print('contour bounding:', x,y,w,h)
center_x = int(x + w/2)
center_y = int(y + h/2)
cv2.circle(imgContour,(center_x, center_y), 5, (0, 0, 255), 5)
cv2.rectangle(imgContour, (x , y ), (x + w , y + h ), (0, 255, 0), 5)
cv2.putText(imgContour, "Points: " + str(len(approx)), (x + w + 20, y + 20), cv2.FONT_HERSHEY_COMPLEX, .7,
(0, 255, 0), 2)
cv2.putText(imgContour, "Area: " + str(int(area)), (x + w + 20, y + 45), cv2.FONT_HERSHEY_COMPLEX, 0.7,
(0, 255, 0), 2)
if area < 3500:
cv2.putText(imgContour, "THIS IS A SMALL PART" , (x + w + 20, y + 70), cv2.FONT_HERSHEY_COMPLEX, 0.7,
(0, 255, 0), 2)
contour_list.append([x,y,w,h])
return contour_listenter code here
So i still do not entirely know what went wrong here but I found a solution for anyone in the future looking to first chroma key correct (Remove background) and then do contour detection:
I dropped the gaussian filter, dilate and canny and instead just inverted the image's colours (contour detection only detects white parts on black background) using:
mask = cv2.bitwise_not(mask)
I then changed the contour detection from cv2.RETR_EXTERNAL to cv2.RETR_LIST
Somehow that fixed it, the result is now really good.

openCV Cascade Classifier rectangle box and its 4 coordinates

I am trying to figure rectangle coordinates (4 points) that comes up when a object or face is detected using Cascade Classifier
I am using python and the code is based on the widely available sample:
import cv2
custom_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
def find_face(img):
to_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
object_detected = custom_cascade.detectMultiScale(to_gray, 1.4, 4)
for (x, y, w, h) in object_detected:
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 1)
# NOTE
# Circle coordinate shows start of the corner (very top left)
# Need to find all four coordinate of the rectangle
cv2.circle(img, (x, y), 2, (0, 255, 0), 2)
return x, y, w, h
How i can get the pixel coordinates and manipulate it? Is it the same with x + w & y + h or some way around. What if i need to find centroid of an object?
(x, y) are the top left coordinates of the rectangle drawn by cv2.rectangle. w is the width and h is the height
You can manipulate that value to get the midpoint
or another way you can use cv2.boundingRect (cnt)
if you want to find centroid change this line cv2.circle(img, (x, y), 2, (0, 255, 0), 2) to this
cent_x = int( x+w/2) cent_y = int(y+h/2) cv2.circle(img, (cent_x,cent_y), 2, (0, 255, 0), 2)
Ok guys,
After brushing up my openCV and maths a bit and went through some docs, this one works:
center_point_x = abs(x + 0.5 * w)
center_point_y = abs(y + 0.5 * h)
cv2.circle(img, (int(center_point_x), int(center_point_y)), 2, (255, 0, 255), 2)
There is possibilities to use numpy as well. But i did a quick one to check for the operation. Hopefully this will help others.
Thank you all.

Why cv.matchShape is NOT invariant to translation as it claims?

I have two contours to match (think of them as any arbitrary 2D closed curves). opencv claims to have matchShapes function that is invariant under translation, rotation and scale. But it seems to me that this is not the case, when I add shift (10, 5) to one of the curves, the function returns a different result, let alone if I did something whackier. Why is that?
matchShape
Reproducible example:
t = np.arange(0, np.pi, 0.001)
x, y = np.cos(t), np.sin(t)
xy = np.stack([x, y], -1)
print(cv.matchShapes(xy, xy, 1, 0))
print(cv.matchShapes(xy, xy + (2, 10), 1, 0))
The objects you send to cv.matchShapes() need to be contour objects which are different to a straight up 2D numpy array. The following code converts your curves to a plot,
then to an image & the contours of the 2 curves are found.
Finally cv.matchShapes() is run.
The output: 0 for the self match & 6.637412841570267e-12 for the match with the translated curve, a pretty accurate match under translation.
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
t = np.arange(0, np.pi, 0.001)
x, y = np.cos(t), np.sin(t)
ax.plot(x, y)
x_new = x + 2
y_new = y + 10
ax.plot(x_new, y_new, 'b')
[s.set_visible(False) for s in ax.spines.values()]
[t.set_visible(False) for t in ax.get_xticklines()]
[t.set_visible(False) for t in ax.get_yticklines()]
ax.axis('off')
plt.savefig('xy.jpg')
xy_img = cv.imread('xy.jpg', cv.IMREAD_COLOR)
xy_cpy = cv.cvtColor(xy_img, cv.COLOR_BGR2GRAY)
(threshold, bw) = cv.threshold(xy_cpy, 127, 255, cv.THRESH_BINARY)
contours, hier = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
contours = contours[0:-1] # remove box surounding whole image
print(cv.matchShapes(contours[0], contours[0], method=cv.CONTOURS_MATCH_I1, parameter=0))
print(cv.matchShapes(contours[0], contours[1], method=cv.CONTOURS_MATCH_I1, parameter=0))
cv.namedWindow("xy")
cv.drawContours(xy_img, contours, -1, (0, 255, 0), 3)
cv.imshow("xy", xy_img)
cv.waitKey()

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:

How to find number from image in OCR?

I'm trying to get the number contours from an image.
Original image is in number_img:
After I've used the following code:
gray = cv2.cvtColor(number_img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (1, 1), 0)
ret, thresh = cv2.threshold(blur, 70, 255, cv2.THRESH_BINARY_INV)
img2, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
area = cv2.contourArea(c)
[x, y, w, h] = cv2.boundingRect(c)
if (area > 50 and area < 1000):
[x, y, w, h] = cv2.boundingRect(c)
cv2.rectangle(number_img, (x, y), (x + w, y + h), (0, 0, 255), 2)
Since there are small boxes in between, I tried to limit with height:
if (area > 50 and area < 1000) and h > 50:
[x, y, w, h] = cv2.boundingRect(c)
cv2.rectangle(number_img, (x, y), (x + w, y + h), (0, 0, 255), 2)
What other ways should I do to get the best contours of number to do OCR?
Thanks.
Just tried in Matlab. Hopefully you can adapt the code to OpenCV and tweak some parameters. It is not clear the right most blob is a number or not.
img1 = imread('DSYEW.png');
% first we can convert the grayscale image you provided to a binary
% (logical) image. It is always the best option in image preprocessing.
% Here I used the threshold .28 based on your image. But you may change it
% for a general solution.
img = im2bw(img1,.28);
% Then we can use the Matlab 'regionprops' command to identify the
% individual blobs in binary image. 'regionprops' gives us an output, the
% Area of the each blob.
s = regionprops(imcomplement(img));
% Now as you did, we can filter out the bounding boxes with an area
% threshold. I used 350 originally. But it can be changed for a better
% output.
s([s.Area] < 350) = [];
% Now we draw each bounding box on the image.
figure; imshow(img);
for k = 1 : length(s)
bb = s(k).BoundingBox;
rectangle('Position', [bb(1),bb(2),bb(3),bb(4)],...
'EdgeColor','r','LineWidth',2 )
end
Output image:
Update 1:
Just changed the area parameter in the above code as follows. Unfortunately I don't have Python OpenCV in my Mac. But, it is all about tweaking the parameters in you code.
s([s.Area] < 373) = [];
Output image:
Update 2:
Numbers 3 and 4 in the above figure were detected as one digit. If you look at carefully you are see that 3 and 4 are connected with each other, and that is why above code detected it as a single digit. So I used the imdilate function to get rid of that. Next, in your code, even the white holes inside some digits were detected as digits. To eliminate that we can fill the holes using imfill in Matlab.
Updated code:
img1 = imread('TCXeuO9.png');
img = im2bw(img1,.28);
img = imcomplement(img);
img = imfill(img,'holes');
img = imcomplement(img);
se = strel('line',2,90);
img = imdilate(img, se);
s = regionprops(imcomplement(img));
s([s.Area] < 330) = [];
figure; imshow(img);
for k = 1 : length(s)
bb = s(k).BoundingBox;
rectangle('Position', [bb(1),bb(2),bb(3),bb(4)],...
'EdgeColor','r','LineWidth',2 )
end
Output image:

Resources