An introduction to affine transformations

What is a radiative transformation

An affine transformation on an image, which is essentially a pixel in the image, moves to another place through some transformation.

Mathematically, it’s a linear transformation from one vector space to another by adding a translation vector.

Vector space m: m=(x,y)

Vector space n: n=(x ‘,y ‘)

The transformation from m to n of the vector space n=A∗m+b

Sorted out:

You combine A and B to form the affine matrix M. The dimension is 2∗3

Using different matrix M, different 2D affine transformation effects are obtained.

In OpencV, the realization of 2D affine transformation needs the help of warpAffine function.

cv2.warpAffine(image, M, (image.shape[1], image.shape[0])
Copy the code

Next, I will take you to analyze the transformation matrix of a specific 2D affine transformation.

Image translation

The formulas

Translation is kind of the simplest kind of space transformation. Its expression is:

Where (b0,b1) is the offset.

routine

If the shift is 10 pixels to the right and 30 pixels to the bottom, then the transformation matrix M can be written as:

Demo code

Pan it 10 pixels to the right and 30 pixels to the bottom:

Import cv2 import numpy as np img = cv2.imread('lena1.jpg') height,width,channel = img.shape = np.float32([[1, 0, 10], [0, 1, 30]]) # move 2D affine transform = cv2.warpAffine(img, M, (width, 0); height)) cv2.imwrite('shift_right_10_down_30.jpg', shifted)Copy the code

Original image:



Move the image 10 pixels to the right and 30 pixels down:

Shift 10 pixels to the left and 30 pixels up:

Shift the transformation matrix 10 pixels to the left, M = np.float32([[1, 0, -10], [0, 1, -30]]) # move 2D affine transform = cv2.warpAffine(img, M, (width, 0); height)) cv2.imwrite('shift_right_-10_down_-30.jpg', shifted)Copy the code

Affine transformation image:

Image translation v2

We can encapsulate this operation with the translate function:

def translate(image, x, y):

    M = np.float32([[1, 0, x], [0, 1, y]])
    shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
    return shifted
Copy the code

Complete some code:

import cv2
import numpy as np

img = cv2.imread('cat.png')

def translate(image, x, y):

    M = np.float32([[1, 0, x], [0, 1, y]])
    shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
    return shifted

shifted = translate(img, 10, 30)
cv2.imwrite('shift_right_10_down_30.png', shifted)
Copy the code

The processing results are the same as above…

The image rotation

Use getRotationMatrix2D to implement rotation

The getRotationMatrix2D function in OpencV can generate M directly for us without requiring us to calculate trig functions in the program:

getRotationMatrix2D(center, angle, scale)
Copy the code

Argument parsing

  • centerCenter of rotation(cx, cy)You can specify whatever you want
  • angleThe unit of rotation is Angle. The counterclockwise direction is positive, and the positive Angle represents counterclockwise
  • scaleThe scaling factor is equal to1.0Represents constant size

This function returns the affine transformation matrix M

The sample code

RotateMatrix = cv2.getRotationMatrix2D((100, 200), 90, Np. set_printoptions(precision=2,suppress=True) print(rotateMatrix) OUTPUT [[0\.1 \.-100.] [-1\. 0 \. 300.]]Copy the code

You can also encapsulate the rotation process for ease of use

Def rotate(image, Angle, center = None, scale = 1.0): (h, w) = image.shape[:2] def rotate(image, Angle, center = None, scale = 1.0): (h, w) = image.shape[:2] if center is None: center = (w / 2, h / 2) M = cv2.getRotationMatrix2D(center, angle, scale) rotated = cv2.warpAffine(image, M, (w, h)) return rotatedCopy the code

Demo code

# -*- coding: Utf-8 -*- "" Rotate around the origin (upper left corner of image) in a positive direction counterclockwise generate the affine matrix" "import numpy as NP import cv2 from math import using the getRotationMatrix2D function cos,sin,radians from matplotlib import pyplot as plt img = cv2.imread('lena1.jpg') height, width, Channel = img.shape # Cx = int(width / 2) cy = int(height / 2) # M = cv2. GetRotationMatrix2D (center=center, Angle =30, Scale =1.0) rotated_30 = cv2.warpAffine(img, M, M = cv2.getRotationMatrix2D(center=center, Angle =45, Scale =1.0) rotated_45 = cv2.warpAffine(img, M, M = cv2.getRotationMatrix2D(center=center, Angle =60, Scale =1.0) rotated_60 = cv2.warpAffine(img, M, new_dim) plt.subplot(221) plt.title("Src Image") plt.imshow(img[:,:,::-1]) plt.subplot(222) plt.title("Rotated 30 Degree") plt.imshow(rotated_30[:,:,::-1]) plt.subplot(223) plt.title("Rotated 45 Degree") plt.imshow(rotated_45[:,:,::-1]) plt.subplot(224) plt.title("Rotated 60 Degree") plt.imshow(rotated_60[:,:,::-1]) plt.show()Copy the code

Original graphics:



Image rotation image (counterclockwise 30 degrees, 45 degrees, 60 degrees) :

Use wrapAffine for scaling

Mathematical principle derivation

We rotate around the origin





From this we conclude that



So the corresponding transformation matrix is zero

Notice that when we do the derivation here, the origin is in the lower left corner, whereas in OpenCV the origin is in the upper left corner of the image, so we’re going to invert theta in our code.

We can use trigonometry in the Math package. One thing to note, however, is that the trigonometric input is in radians, not angles.

We need to use the radians(x) function to convert the Angle to radians.

The import math math. Radians (180) 3.141592653589793Copy the code

Code demo

# -*- coding: Utf-8 -*- "" rotate around the origin (upper left corner of the image) counterclockwise" "import numpy as NP import cv2 import math from matplotlib import Pyplot as PLT  img = cv2.imread('lena1.jpg') height, width, channel = img.shape def getRotationMatrix2D(theta): Radians (-1*theta) M = NP.float32 ([[math.cos(theta), -math.sin(theta), 0], [math.sin(theta), math.cos(theta), M = getRotationMatrix2D(30) rotated_30 = cv2.warpaffine (img, M, (width, M = getRotationMatrix2D(45) rotated_45 = cv2.warpAffine(img, M, (width, M = getRotationMatrix2D(60) rotated_60 = cv2.warpAffine(img, M, (width, height)) plt.subplot(221) plt.title("Src Image") plt.imshow(img[:,:,::-1]) plt.subplot(222) plt.title("Rotated 30 Degree") plt.imshow(rotated_30[:,:,::-1]) plt.subplot(223) plt.title("Rotated 45 Degree") plt.imshow(rotated_45[:,:,::-1]) plt.subplot(224) plt.title("Rotated 60 Degree") plt.imshow(rotated_60[:,:,::-1]) plt.show()Copy the code

Original image:



Illustration after rotation:

I rotate around any point

Mathematical principle derivation

So how do you rotate around any point?

You can shift the current center of rotation to the origin, rotate at the origin and then shift it back again.

Suppose the center of rotation is zero(cx,cy)



Among them



so

Code demo

# -*- coding: Utf-8 -*- "" rotate around any point in the screen import numpy as NP import cv2 from math import cos,sin,radians from matplotlib import pyplot as plt img = cv2.imread('lena1.jpg') height, width, channel = img.shape theta = 45 def getRotationMatrix2D(theta, cx=0, cy=0): Because the top left corner of the image is the origin, we need ×-1 theta = radians(-1 * theta) M = NP.Float32 ([[cos(theta), -sin(theta), (1-cos(theta))*cx + sin(theta)*cy], [sin(theta), cos(theta) *cx + (1-cos(theta))*cy]]) As the axis of rotation cx= int(width / 2) cy = int(height / 2) # Perform 2D affine transformation # Rotate 30 degrees counterclockwise around the origin M = getRotationMatrix2D(30, cx=cx, Rotated_30 = cv2. WarpAffine (img, M, (width, height)) # rotated_30 = cv2. Rotated_45 = cv2.warpAffine(img, M, (width, height)) # rotated_45 = cv2.warpAffine(img, M, (width, height)) # cy=cy) rotated_60 = cv2.warpAffine(img, M, (width, height)) plt.subplot(221) plt.title("Src Image") plt.imshow(img[:,:,::-1]) plt.subplot(222) plt.title("Rotated 30 Degree") plt.imshow(rotated_30[:,:,::-1]) plt.subplot(223) plt.title("Rotated 45 Degree") plt.imshow(rotated_45[:,:,::-1]) plt.subplot(224) plt.title("Rotated 60 Degree") plt.imshow(rotated_60[:,:,::-1]) plt.show()Copy the code

Rotation effect:

Rotate 30 degrees to 60 degrees around the center of the image

Image zooming

Use the resize function for scaling

Opencv actually has a function called resize for image scaling.

resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst
Copy the code

Argument parsing

  • srcThe input image
  • dsizeOutput the size of the picture
  • dstThe output image
  • fxThe scaling factor for the x axis
  • fyThe scaling factor for the y axis
  • interpolationThe interpolation method
  • INTER_NEAREST– Nearest neighbor interpolation
  • INTER_LINEAR– Linear interpolation (default)
  • INTER_AREA– Region interpolation
  • INTER_CUBIC– Cubic spline interpolation
  • INTER_LANCZOS4– Lanczos interpolation

When used, we can pass in the specified image size dsize

Import cv2 import numpy as NP img = cv2.imread('lena1.jpg') height,width,channel = img.shape # New_dimension = (400, 400) # Specifies dimension and interpolation algorithm (Interpolation) resized = cv2.resize(img, new_dimension) cv2.imwrite('lena_resize_400_400.png', resized)Copy the code

Original image:



Zoomed image:

Or specify a scaling factorfx,fy

Set dsize to None, and specify FX fy

import cv2 import numpy as np img = cv2.imread('lena1.jpg') height,width,channel = img.shape # Resized = cv2.resize(img, None, fx=1.5, fy=2) cv2.imwrite('lena_resize_fx_fy.jpg', resized)Copy the code

The running results are as follows:



Or specify the output image and pass in the size of the output image:

Import cv2 import numpy as NP img = cv2.imread('lena1.jpg') height,width,channel = img.shape # Zeros ((100, 100, 3), dtype='uint8') # Dsize =(dst.shape[1], dst.shape[0]), fx=1.5, fy=2) cv2.imwrite('lena_resize_from_dst.jpg', DST)Copy the code

The running results are as follows:

See the Opencv-resize documentation for more detailed instructions

We can also wrap it as a function for ease of use

def resize(image, width = None, height = None, inter = cv2.INTER_AREA):
    dim = None
    (h, w) = image.shape[:2]

    if width is None and height is None:
        return image

    if width is None:
        r = height / float(h)
        dim = (int(w * r), height)

    if height is None:
        r = width / float(w)
        dim = (width, int(h * r))

    if width and height:
        dim = (width, height)

    resized = cv2.resize(image, dim, interpolation = inter)
    return resized
Copy the code

The resolution is enlarged from 5 * 5 to 1000 * 1000, and different interpolation algorithms are selected. The corresponding demonstration effect is as follows:

Import cv2 import numpy as NP from matplotlib import pyplot as PLT img = Uint8 (np.random. Randint (0,255,size=(5,5))) height,width= img.shape 1000) plt.subplot(231) plt.title("SRC Image") plt.imshow(img,cmap='seismic') plt.subplot(232) resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_NEAREST) plt.title("INTER_NEAREST") plt.imshow(resized,cmap='seismic') plt.subplot(233) resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_LINEAR) plt.title("INTER_LINEAR") plt.imshow(resized,cmap='seismic') plt.subplot(234) resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_AREA) plt.title("INTER_AREA") plt.imshow(resized,cmap='seismic') plt.subplot(235) resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_CUBIC) plt.title("INTER_CUBIC") plt.imshow(resized,cmap='seismic') plt.subplot(236) resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_LANCZOS4) plt.title("INTER_LANCZOS4") plt.imshow(resized,cmap='seismic') plt.show()Copy the code



Use wrapAffine for scaling

Mathematical principles

The transformation matrix M of the expansion transformation of the image is



Among them,

fx: represents the focal length of the x axis (scaling factor)

fy: represents the focal length of the Y-axis (scaling factor)

The following formula can be obtained:

Concrete code demo

The source code:

Import numpy as np import cv2 img = cv2.imread('lena1.jpg') height,width,channel = img.shape # x axis focal length 1.5 x fx = 1.5 # y focal length 2 x fy = 2 # Declare the transformation matrix shifted 10 pixels to the right, Zoom down 30 pixels M = Np.float32 ([[fx, 0, 0], [0, fy, 0]]) # Re-sized 2D affine transform = cv2.warpaffine (img, M, (int(width*fx), int(height*fy))) cv2.imwrite('resize_raw.jpg', resized)Copy the code

Operation effect:

Original image:





weRandom module is used to generate a 5×5 random matrix.

Img = np.random. Randint (0,255,size=(5,5))Copy the code

The source code:

The affine matrix implements scaling fx,fy "import numpy as NP import cv2 from matplotlib import Pyplot as PLT # generates a random noise img = Uint8 (np.random. Randint (0,255,size=(5,5))) height,width = img.shape # x axis focal length 1.5 x fx = 1.5 y axis focal length 2 x fy = 2 Shift it 10 pixels to the right, Zoom down 30 pixels M = Np.float32 ([[fx, 0, 0], [0, fy, 0]]) # Re-sized 2D affine transform = cv2.warpaffine (img, M, (int(width*fx), Int (height*fy)) print(img) print(resized) # plt.subplot(121) plt.imshow(img, cmap="gray") plt.subplot(122) plt.imshow(resized,cmap="gray") plt.show()Copy the code

The original:

[[224 25 25 165 16] [37 170 114 16 101] [181 5 7 94 41] [206 167 23 133 115] [217 115 154 97 65]Copy the code

After the scale:

[[224 93 25 25 117 114 16] [131 109 88 70 83 80 59] [37 124 151 114 50 45 101] [109 95 78 61 57 61 71] [181 66 6 7 64 76 41] [194 123 62 15 80 101 78] [206 180 118 23 95 127 115] [212 165 123 89 106 106 90] [217 150 128 154 117 86 65] [109 75 64 77 58 43 33]Copy the code

For a more intuitive feeling, we can perform data visualization.

We use thematplotlibImage before and after resize.

Image rollovers

Flip using the flip function

Flip function prototype

flip(src, flipCode[, dst]) -> dst
Copy the code

Argument parsing

  • srcThe input image
  • flipCodeFlip the code
  • 1Flip Horizontally Horizontally (the second dimension of the image is column)
  • 0Vertically invert *Vertically *
  • - 1Horizontally and Vertically inverting Horizontally & Vertically

You can also encapsulate the following functions for ease of use

def flip(image, direction):
    if direction == "h":
        flipped = cv2.flip(image, 1)
    elif direction == "v":
        flipped = cv2.flip(image, 0)
    else:
        # both horizontally and vertically
        flipped = cv2.flip(image, -1)
Copy the code

Specific source code and effect display

Import numpy as np import cv2 from matplotlib import pyplot as PLT img = cv2.imread('lena1.jpg') def bgr2rbg(img): Flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip flip Flip_hv = cv2.flip(img, -1) plt.subplot(221) plt.title('SRC') plt.imshow(bgr2rbg(img)) plt.subplot(222) plt.title('Horizontally') plt.imshow(bgr2rbg(flip_h)) plt.subplot(223) plt.title('Vertically') plt.imshow(bgr2rbg(flip_v)) plt.subplot(224) plt.title('Horizontally & Vertically') plt.imshow(bgr2rbg(flip_hv)) plt.show()Copy the code

Use numpy’s index for flipping

Using the nDARray index in NUMpy, we can easily achieve image flipping.

# flip horizontal flip_h = img [: : : 1] # flip vertical flip_v = img] [: : - 1 # levels at the same time flip vertical flip_hv = img [: : 1, : : 1]Copy the code

Specific source code and effect display

Import cv2 import numpy as NP from matplotlib import pyplot as PLT img = Flip_h = img[:,::-1] # flip flip_v = img[::-1] # flip flip_v = img[::-1] # Flip_hv = img[::-1, ::-1] def bgr2rbg(img): Return img[:,:,::-1] plt.subplot(221) plt.title('SRC') plt.imshow(bgr2rbg(img)) plt.subplot(222)  plt.title('Horizontally') plt.imshow(bgr2rbg(flip_h)) plt.subplot(223) plt.title('Vertically') plt.imshow(bgr2rbg(flip_v)) plt.subplot(224) plt.title('Horizontally & Vertically') plt.imshow(bgr2rbg(flip_hv)) plt.show() 12345678910111213141516171819202122232425262728293031323334353637383940414243Copy the code

Use wrapAffine for rollover

Mathematical principles of image flipping

Note: width represents the width of the image; Height represents the height of the image

The transformation matrix of the horizontal flip



The vertically flipped transformation matrix



Flip horizontally and vertically simultaneously

Specific source code and effect display

Import cv2 import numpy as NP from matplotlib import pyplot as PLT img = cv2.imread('lena1.jpg') Float32 ([[-1, 0, width], [0, 1, 0]]) flip_h = cv2.warpAffine(img, M1, 1, 0) Float = np.float32([[1, 0, 0], [0, -1, height]]) flip_v = cv2.warpAffine(img, M2, (width, 0, 0)) Float ([[-1, 0, width], [0, -1, height]]) flip_hv = cv2.warpAffine(img, M3, (width, 0, -1, height) height)) def bgr2rbg(img): Return img[:,:,::-1] plt.subplot(221) plt.title('SRC') plt.imshow(bgr2rbg(img)) plt.subplot(222)  plt.title('Horizontally') plt.imshow(bgr2rbg(flip_h)) plt.subplot(223) plt.title('Vertically') plt.imshow(bgr2rbg(flip_v)) plt.subplot(224) plt.title('Horizontally & Vertically') plt.imshow(bgr2rbg(flip_hv)) plt.show()Copy the code

Resource portal

  1. Pay attention to [be a gentle program ape] public account
  2. In [do a tender program ape] public account background reply [Python information] [2020 autumn recruit] can get the corresponding surprise oh!

“❤️ thank you.”

  1. Click “like” to support it, so that more people can see this content.
  2. Share your thoughts with me in the comments section, and record your thought process in the comments section.
  • Excel/Word/CSV with Python (160 +)
  • The average programmer likes to browse 40 websites, so many years, I will not hide private, personally strongly recommend (230+)
  • Image Encryption and Restoration Based on Chaotic Logistic Encryption Algorithm
  • Draw dynamic fireworks in Python
  • Error: AttributeError: ‘Module’ object has no attribute ‘XXXXX’
  • How to parse XML and PDF easily in Python (300 +)