Related
I'm trying to plot a rotated xyz axis using OpenCV projectPoints function. When testing rotation about the X and Y axis, I noticed that the Z axis is much longer than the X and Y axis when they should be the same length, but I am unsure why. Any help would be greatly appreciated!
Here are some images I generated:
Here is my code:
import numpy as np
import cv2
import sys
def rotByXAxis(angle):
return np.array([
[1, 0, 0],
[0, np.cos(angle), -np.sin(angle)],
[0, np.sin(angle), np.cos(angle)],
], dtype=np.float32)
def rotByYAxis(angle):
return np.array([
[ np.cos(angle), 0, np.sin(angle)],
[ 0, 1, 0],
[-np.sin(angle), 0, np.cos(angle)],
], dtype=np.float32)
def rotByZAxis(angle):
return np.array([
[ np.cos(angle), np.sin(angle), 0],
[-np.sin(angle), np.cos(angle), 0],
[ 0, 0, 1],
], dtype=np.float32)
def createCanvas(self, height, width):
blank_image = np.zeros((height, width, 3), np.uint8)
blank_image[:, :] = (255, 255, 255)
return blank_image
def draw3DAxis(self, image, rvec, tvec, cameraMatrix, scale=1, dist=None):
"""
Draw a 6d of axis (XYZ -> RGB) in the given rotation and translation
:param image - rgb numpy array
:rvec - euler rotations, numpy array of length 3,
use cv2.Rodrigues(R)[0] to convert from rotation matrix
:tvec - 3d translation vector, in meters (dtype must be float)
:cameraMatrix - intrinsic calibration matrix , 3x3
:scale - factor to control the axis lengths
:dist - optional distortion coefficients, numpy array of length 4. If None distortion is ignored.
"""
image = image.astype(np.float32)
dist = np.zeros(4, dtype=float) if dist is None else dist
if rvec.shape == (3, 3):
rvec, _ = cv2.Rodrigues(rvec)
points = scale * np.float32([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[0, 0, 0]
]).reshape(-1, 3)
axis_points, _ = cv2.projectPoints(points, rvec, tvec, cameraMatrix, dist)
print(axis_points)
image = cv2.arrowedLine(
image,
tuple(int(e) for e in axis_points[3].ravel()),
tuple(int(e) for e in axis_points[0].ravel()),
(255, 0, 0),
3,
tipLength=0.01 * scale,
)
image = cv2.arrowedLine(
image,
tuple(int(e) for e in axis_points[3].ravel()),
tuple(int(e) for e in axis_points[1].ravel()),
(0, 255, 0),
3,
tipLength=0.01 * scale,
)
image = cv2.arrowedLine(
image,
tuple(int(e) for e in axis_points[3].ravel()),
tuple(int(e) for e in axis_points[2].ravel()),
(0, 0, 255),
3,
tipLength=0.01 * scale,
)
return image
if __name__ == "__main__":
height = 300
width = 400
image = createCanvas(height, width)
rvec = rotByZAxis(-pi/2)
cameraMatrix = np.array([
[1.0, 0, width/2],
[ 0, 1.0, height/2],
[ 0, 0, 1.0 ]], dtype=np.float32)
image = draw3DAxis(
image,
rvec=rvec,
tvec=np.zeros(3, dtype=float),
cameraMatrix=cameraMatrix,
scale=20,
)
cv2.imshow("output", image)
key = cv2.waitKey(0)
if key:
sys.exit(1)
I have some specific images of two objects (a phone and a TV remote) and I want to calculate the angle between two edges that intersect of these. I used Canny to detect the edges and Hough line for the angle, but the hough_line() function found too many angles that doesnt match the requirement.
Original image:
This is the requirement:
And this is which I made:
My code:
import cv2
from skimage.transform import hough_line, hough_line_peaks
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def edge_detection(img, blur_ksize=5, threshold1=100, threshold2=200):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_gaussian = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0)
img_canny = cv2.Canny(img_gaussian, threshold1, threshold2)
return img_canny
image = edge_detection(cv2.imread('img1.png'))
h, theta, d = hough_line(image)
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
ax = axes.ravel()
ax[0].imshow(image)
ax[0].set_title('Input image')
ax[0].set_axis_off()
ax[1].imshow(image, cmap=cm.gray)
for _, angle, dist in zip(*hough_line_peaks(h, theta, d)):
y0 = (dist - 0 * np.cos(angle)) / np.sin(angle)
y1 = (dist - image.shape[1] * np.cos(angle)) / np.sin(angle)
ax[1].plot((0, image.shape[1]), (y0, y1), '-r')
ax[1].set_xlim((0, image.shape[1]))
ax[1].set_ylim((image.shape[0], 0))
ax[1].set_axis_off()
ax[1].set_title('Detected lines')
plt.tight_layout()
plt.show()
angle = []
dist = []
for _, a , d in zip(*hough_line_peaks(h, theta, d)):
angle.append(a)
dist.append(d)
angle = [a*180/np.pi for a in angle]
print(angle)
Are there any ways to detect and calculate exactly one angle I need in opencv? Thanks a lot
Update
I tried different values of blur_ksize, threshold1 and threshold2 in Canny detection, it's seem like I could remove redundant lines, but now the angles those hough_line_peaks() return are negative. Can anyone explain this for me? And I also want to put the angle values to the peaks in plot, to see which angle has which value
here is a sample solution, but I don't know whether it works for all images. You have to tune the hough transform parameters.
import cv2
import numpy as np
import matplotlib.pyplot as plt
def edge_detection(img, blur_ksize=5, threshold1=70, threshold2=200):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gaussian = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0)
img_canny = cv2.Canny(img_gaussian, threshold1, threshold2)
return img_canny
img = cv2.imread('stack.png')
image = edge_detection(img)
minLineLength = 300
maxLineGap = 80
lines = cv2.HoughLinesP(image,1,np.pi/180,50,minLineLength,maxLineGap)
equations = []
for line in lines:
x1,y1,x2,y2 = line[0]
equations.append(np.cross([x1,y1,1],[x2,y2,1]))
cv2.line(img,(x1,y1),(x2,y2),(255,0,0),2)
font = cv2.FONT_HERSHEY_SIMPLEX
thetas = []
N = len(equations)
for ii in range(1,N):
a1,b1,c1 = equations[0]
a2,b2,c2 = equations[ii]
# intersection point
pt = np.cross([a1,b1,c1],[a2,b2,c2])
pt = np.int16(pt/pt[-1])
# angle between two lines
num = a1*b2 - b1*a2
den = a1*a2 + b1*b2
if den != 0:
theta = abs(np.arctan(num/den))*180/3.1416
# show angle and intersection point
cv2.circle(img, (pt[0],pt[1]), 5, (255,0,0), -1)
cv2.putText(img, str(round(theta, 1)), (pt[0]-20,pt[1]-20), font, 0.8, (255,0,0), 2, 0)
thetas.append(theta)
plt.imshow(img)
plt.show()
I have a neural network that outputs segments of a face - I'm working on a function that combines these segments together and then clones them into a real face.
Example images are here: https://imgur.com/a/HnpqhEE, I do not have the reputation to include them inline.
So far my function takes the makeup face and lips segment and combines them with addition. The left and right eyes are then cloned in with seamlessClone (the right eye is flipped first).
Then the combined makeup segments are cloned into the normal face.
Very occasionally, my combination function fails and returns a (-215:Assertion failed) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in function 'cv::Mat::Mat' error.
My function is below, I have only seen it error at the last seamlessClone
def combineFace(images, radius = 70):
# Given image segments and eye radii, combine face.
realFace = tensor2im(images['realNormal'])
makeupFace = tensor2im(images['fakeMakeupFace'])
makeupLeft = tensor2im(images['fakeMakeupLeft'])
makeupRight = tensor2im(images['fakeMakeupRight'])
makeupLips = tensor2im(images['fakeMakeupLips'])
makeupRight = cv2.flip(makeupRight, 1)
# I use cv2 and dlib to get face landmarks and interesting points.
normalLandmarks = faceLandmarks(realFace)
facePoints = getFacePoints(normalLandmarks)
# PP means pupil points
outerPoints, leftPP, rightPP, lipPoints, eyeMids = facePoints
# eyeMid is (x, y) of center of eye obtained from landmark points
leftEye = eyeMids[0]
rightEye = eyeMids[1]
faceMask = np.zeros(realFace.shape, realFace.dtype)
cv2.fillPoly(faceMask, [outerPoints], [255, 255, 255])
cv2.fillPoly(faceMask, [lipPoints], [0, 0, 0])
cv2.fillPoly(faceMask, [leftPP], [0, 0, 0])
cv2.fillPoly(faceMask, [rightPP], [0, 0, 0])
# Occasionally, the eye segments overlap eachother so I cut the right eye from the left and vice
# versa
leftMask = np.zeros(realFace.shape, realFace.dtype)
cv2.circle(leftMask, leftEye, radius, [255, 255, 255], -1)
cv2.circle(leftMask, rightEye, radius, [0, 0, 0], -1)
# Errors if i do not use UMat
cv2.circle(cv2.UMat(makeupLeft), rightEye, radius, [0, 0, 0], -1)
rightMask = np.zeros(realFace.shape, realFace.dtype)
cv2.circle(rightMask, rightEye, radius, [255, 255, 255], -1)
cv2.circle(rightMask, leftEye, radius, [0, 0, 0], -1)
cv2.circle(cv2.UMat(makeupRight), leftEye, radius, [0, 0, 0], -1)
# Combine face output and lips
baseCombine = makeupFace + makeupLips
# Left Eye
output = cv2.seamlessClone(makeupLeft, baseCombine, leftMask, leftEye, cv2.MIXED_CLONE)
output = cv2.seamlessClone(makeupRight, output, rightMask, rightEye, cv2.MIXED_CLONE)
# Get center of face
faceRect = cv2.boundingRect(outerPoints)
x, y, w, h = faceRect
output = cv2.bitwise_and(output, faceMask)
center = ( x + w // 2, y + h // 2)
# I have only seen the function error at this point
combinedFace = cv2.seamlessClone(output, realFace, faceMask, center, cv2.MIXED_CLONE)
return combinedFace
Any idea why this is occasionally erroring?
All input images have the form (256, 256, 3)
This version of the function works much better. There was something wrong with my face center calculation which was causing the error
def combineFace(images, radius = 70):
# Given image segments and eye radii, combine face.
realFace = tensor2im(images['realNormal'])
makeupFace = tensor2im(images['fakeMakeupFace'])
makeupLeft = tensor2im(images['fakeMakeupLeft'])
makeupRight = tensor2im(images['fakeMakeupRight'])
makeupLips = tensor2im(images['fakeMakeupLips'])
# Right eye is flipped before input into the network.
makeupRight = cv2.flip(makeupRight, 1)
normalLandmarks = faceLandmarks(realFace)
facePoints = getFacePoints(normalLandmarks)
outerPoints, leftPP, rightPP, lipPoints, eyeMids = facePoints
leftEye = eyeMids[0]
rightEye = eyeMids[1]
leftMask = np.zeros(makeupLeft.shape, makeupLeft.dtype)
cv2.circle(leftMask, leftEye, radius, [255, 255, 255], -1)
cv2.circle(leftMask, rightEye, radius, [0, 0, 0], -1)
# Errors if i do not use cv2.UMat
cv2.circle(cv2.UMat(makeupLeft), rightEye, radius, [0, 0, 0], -1)
rightMask = np.zeros(makeupRight.shape, makeupRight.dtype)
cv2.circle(rightMask, rightEye, radius, [255, 255, 255], -1)
cv2.circle(rightMask, leftEye, radius, [0, 0, 0], -1)
cv2.circle(cv2.UMat(makeupRight), leftEye, radius, [0, 0, 0], -1)
# Base output is combination of face without lips and pupils + lips
baseCombine = makeupFace + makeupLips
# Areas around eyes are changes
output = cv2.seamlessClone(makeupLeft, baseCombine, leftMask, leftEye, cv2.MIXED_CLONE)
output = cv2.seamlessClone(makeupRight, output, rightMask, rightEye, cv2.MIXED_CLONE)
# Find center of face
faceRect = cv2.boundingRect(outerPoints)
x, y, w, h = faceRect
if x < 0:
x = 0
if y < 0:
y = 0
faceCenter = ( x + w // 2, y + h // 2)
croppedOutput = output[y:y+h, x:x+w]
faceMask = np.zeros(realFace.shape, realFace.dtype)
cv2.fillPoly(faceMask, [outerPoints], [255, 255, 255])
cv2.fillPoly(faceMask, [lipPoints], [0, 0, 0])
cv2.fillPoly(faceMask, [leftPP], [0, 0, 0])
cv2.fillPoly(faceMask, [rightPP], [0, 0, 0])
croppedMask = faceMask[y:y+h, x:x+w]
if len(croppedOutput) == 0:
print("OUTPUT 0")
print("FACE RECT: ", faceRect)
sourceW, sourceH, sCH = realFace.shape
width, height, ch = croppedOutput.shape
faceWidth = width/2
faceHeight = height/2
xdiff = 0
ydiff = 0
cx = faceCenter[0]
cy = faceCenter[1]
if cx - faceWidth < 0:
# Face overflows left
xdiff = abs(cx - faceWidth)
if cx + faceWidth > sourceW:
xdiff = (cx + faceWidth - sourceW) * -1
if cy + faceHeight > sourceH:
ydiff = (cy + faceHeight - sourceH) * -1
if cy - faceHeight < 0:
ydiff = abs(cy - faceHeight)
centerx = int(cx + xdiff)
centery = int(cy + ydiff)
center = (centerx, centery)
# We move center, also move mask?
combinedFace = cv2.seamlessClone(croppedOutput, realFace, croppedMask, center, cv2.MIXED_CLONE)
return combinedFace
I am trying to achieve perspective transformation using Python and Open CV. While the transformation is done by selecting 4 points on the image, the output image is highly blurred. Even when I don't use the mouse event for selecting the 4 points(rather hard coding it), the image quality is still blurred.Here is my programmatic attempt to this:
`def draw_circle(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDBLCLK:
cv2.circle(img, (x, y), 5, (255, 0, 0), -1)
p = (x, y)
l.append(p)
print(l)
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
img = cv2.imread('Path to my input image')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.resizeWindow('image', 600, 600)
cv2.setMouseCallback('image', draw_circle)
while 1:
cv2.imshow('image', img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
rows, cols, channels = img.shape
pts1 = np.float32(l)
pts2 = np.float32([[0, 0], [200, 0], [200, 100], [0, 100]])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, M, (200, 100), cv2.INTER_LINEAR)
h1 = math.sqrt((abs(pts1[1][0] - pts1[0][0])) ** 2 + (abs(pts1[1][1] - pts1[0][1])) ** 2)
h2 = math.sqrt((abs(pts1[3][0] - pts1[2][0])) ** 2 + (abs(pts1[3][1] - pts1[2][1])) ** 2)
v1 = math.sqrt((abs(pts1[3][0] - pts1[0][0])) ** 2 + (abs(pts1[3][1] - pts1[0][1])) ** 2)
v2 = math.sqrt((abs(pts1[2][0] - pts1[1][0])) ** 2 + (abs(pts1[2][1] - pts1[1][1])) ** 2)
max_h = int(max(h1, h2))
max_v = int(max(v1, v2))
dst = cv2.resize(dst, (max_h, max_v))
plt.subplot(121), plt.imshow(img), plt.title('Input')
plt.subplot(122), plt.imshow(dst), plt.title('Output')
plt.show()`
Here is my input image: This is a fridge image with selective beverages
Here is my output image: This is the output image after perspective transform
replace in your code this line
pts2 = np.float32([[0, 0], [200, 0], [200, 100], [0, 100]])
to this one (maybe you have to switch v/h order, I don't know python syntax):
pts2 = np.float32([[0, 0], [max_h,0], [max_h,max_v], [0,max_v]])
by moving the max_h/max_v computation to before transformation computation. Then remove the resizing code.
At the moment you first (implicitly) resize to a 100x200 temporary image, which will be very blurry if you resize it to a bigger image afterwards.
I've been trying to do a 4 point perspective transform in order to start doing some OCR.
Starting with the following image I can detect the number plate
and crop it out with the green box being the bounding box and the red dots being the corners of the rectangle I want to square up.
This is the output of the transform.
At a first look it seams to have done the transform inside out (taking the parts either side rather than between the points).
I'm using the imutils package to do the transform and working from this and this as a guide. I'm sure it's something relatively simple I'm missing.
#!/usr/bin/python
import numpy as np
import cv2
import imutils
from imutils import contours
from imutils.perspective import four_point_transform
img = cv2.imread("sample7-smaller.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.bilateralFilter(gray,15,75,75)
v = np.median(blurred)
lower = int(max(0, (1.0 - 0.33) * v))
upper = int(min(255, (1.0 + 0.33) * v))
edged = cv2.Canny(blurred, lower, upper, 255)
conts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
conts = conts[0] if imutils.is_cv2() else conts[1]
conts = sorted(conts, key=cv2.contourArea, reverse=True)
for cnt in conts:
approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True)
if len(approx) == 4:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
for i in approx:
cv2.circle(img,(i[0][0], i[0][1]),2,(0,0,255), thickness=4)
warped = four_point_transform(img, approx.reshape(4,2))
cv2.imshow("crop",img[y:y+h,x:x+w])
cv2.imshow("warped", warped)
cv2.waitKey(0)
I would recommend you to use the OpenCV Perspective Transform method, to get the desired results, as per the given image:
First mark the position of src points:
src_pts = np.array([[8, 136], [415, 52], [420, 152], [14, 244]], dtype=np.float32)
And suppose you want to fit this number plate in a matrix of shape 50x200, so destination points would be:
dst_pts = np.array([[0, 0], [200, 0], [200, 50], [0, 50]], dtype=np.float32)
Find the perspective Transform Matrix as :
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warp = cv2.warpPerspective(img, M, (200, 50))
EDIT: As you didn't wanted to hard code the final width, height of plate, So in order to make the calculations more flexible you can calculate the width and height of the plate from the 4 marker points as:
def get_euler_distance(pt1, pt2):
return ((pt1[0] - pt2[0])**2 + (pt1[1] - pt2[1])**2)**0.5
src_pts = np.array([[8, 136], [415, 52], [420, 152], [14, 244]], dtype=np.float32)
width = get_euler_distance(src_pts[0][0], src_pts[0][1])
height = get_euler_distance(src_pts[0][0], src_pts[0][3])
dst_pts = np.array([[0, 0], [width, 0], [width, height], [0, height]], dtype=np.float32)
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warp = cv2.warpPerspective(img, M, (width, height))