Last active
August 8, 2020 20:15
-
-
Save crackwitz/32eaee7d923c139a6393de1ed2af80ee to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import os | |
import sys | |
import numpy as np | |
import cv2 as cv | |
def translation(tx, ty): | |
result = np.eye(3) | |
result[0:2, 2] = (tx, ty) # set last column | |
return result | |
def rotation_by_angle(degrees): | |
# let's be lazy | |
result = np.eye(3) | |
result[0:2, 0:3] = cv.getRotationMatrix2D(center=(0,0), angle=degrees, scale=1.0) | |
return result | |
def normalize(vector): | |
vector = np.array(vector) | |
length = np.linalg.norm(vector) | |
return vector / length | |
def rotation_by_axis(xaxis): | |
xaxis = normalize(xaxis) | |
(dx, dy) = xaxis | |
result = np.eye(3) | |
result[0:2, 0] = (dx, dy) # x coordinates are mapped to this | |
result[0:2, 1] = (-dy, dx) # y: 90 degree rotated vector | |
return result | |
def shear(xaxis, yaxis): | |
result = np.eye(3) | |
result[0:2, 0] = xaxis # x coordinates (x,0) are mapped to this | |
result[0:2, 1] = yaxis # (0,y) are mapped to this | |
return result | |
def scale(sx, sy): | |
result = np.eye(3) | |
result[0,0] = sx | |
result[1,1] = sy | |
return result | |
### MAIN PROGRAM ### | |
if len(sys.argv) >= 2: | |
srcpath = sys.argv[1] | |
else: | |
srcpath = cv.samples.findFile("lena.jpg") | |
src = cv.imread(srcpath) | |
sh, sw = src.shape[:2] | |
dw, dh = (1024, 768) # of anything you like | |
#dw, dh = sw, sh | |
# let's build two transformations | |
# first, shake and bake | |
M1 = cv.getRotationMatrix2D(center=(sw/2, sh/2), angle=20, scale=1.0) | |
# second, DIY | |
# let's use the forward transform, i.e. source point transformed into dest point | |
# p_dest = T2 * R * T1 * p_src | |
# T1 moves the rotation center in the source to the origin (subtract) | |
# R rotates (around "origin") | |
# T2 moves points to the rotation center in the destination (add) | |
T1 = translation(tx=-sw/2, ty=-sh/2) | |
T2 = translation(tx=+dw/2, ty=+dh/2) | |
# try these: | |
#R = rotation_by_angle(degrees=20) # replicates the rotation part of getRotationMatrix2D | |
R = rotation_by_axis((+10, +5)) # uses a (normalized) vector instead | |
R = shear(xaxis=(1, -0.1), yaxis=(0.5, 0.5)) # notice how the axes are mapped in the output, y is turned diagonal, x is slightly ascending (negative y) | |
H = T2 @ R @ T1 # @ is np.dot | |
# I'm calling it H because a 3x3 matrix of this purpose is generally called homography | |
# homographies can do perspective transformations | |
# we built an affine transformation | |
# so the "homography" part should be (0,0,1), i.e. not used at all | |
assert np.isclose(a=H[2], b=(0,0,1)).all() # check that the last row is (0,0,1) | |
M2 = H[0:2, :] # take 2x3 part | |
# WARP_INVERSE_MAP so it's using the matrix as is (by default would invert it) | |
# try it without that flag too, may give a different intuition | |
#dest = cv.warpAffine(src, M=M, dsize=(dw, dh), flags=cv.WARP_INVERSE_MAP | cv.INTER_CUBIC) | |
dest1 = cv.warpAffine(src, M=M1, dsize=(dw, dh), flags=cv.INTER_CUBIC) | |
dest2 = cv.warpAffine(src, M=M2, dsize=(dw, dh), flags=cv.INTER_CUBIC) | |
cv.imshow("src", src) | |
cv.imshow("dest1", dest1) | |
cv.imshow("dest2", dest2) | |
cv.waitKey(-1) | |
cv.destroyAllWindows() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment