Home>

python - about the optimum angle of rotation matrix

Purpose: The yellow part of the upper image is rotated by Affine transformation to convert it vertically as shown in the lower image.
Rotate from -90 degrees to 90 degrees to output the optimum angle.

There are two patterns that I thought about myself,

1. Enclose the teeth with a rectangle and add up the pixels other than the teeth in it, and the smallest one is the optimum.
2. The pixel of the tooth part is counted for each column, and the one with the largest number is optimized.

When I actually saw these two, I felt that they were not optimal.
I would like to know how people perceive that they are in portrait orientation. Please teach us the idea of ​​calculating the optimum angle.

Attachments (3 npy files, ipynb files in tooth_array.zip)

``````# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
#Rotate image array with affine transformation
def rotate_affine (src, theta):
# Get the size of the original image
h, w = src.shape [0], src.shape [1]
# Array generation for output image (all elements are 0)
dst = np.zeros ((h, w))
Convert to # degree or raradian
#Affine transformation
for y in range (0, h):
for x in range (0, w):
xi = int ((x --int (w/2)) * np.cos (rd)-(y --int (h/2)) * np.sin (rd) + int (w/2))
yi = int ((x --int (w/2)) * np.sin (rd) + (y --int (h/2)) * np.cos (rd) + int (h/2))
# Substitute pixel values ​​in the output image array if the converted coordinates are not out of range
if yi<h ―― 1 and xi<w ―― 1 and yi>0 and xi>0:
dst [y] [x] = src [yi] [xi]
return dst
def pattern1 (src):
sum_cnt = 1000000
best_theta = 0
for theta in range (-90,90 + 1):
dst = rotate_affine (src, theta)
vertical_info = np.count_nonzero (dst == 255, axis = 0)
side_info = np.count_nonzero (dst == 255, axis = 1)
min_flag = False
for i, num in enumerate (vertical_info):
if num! = 0 and min_flag == False:
min_flag = True
min_x = i
if num == 0 and min_flag == True:
max_x = i-1
min_flag = False
for i, num in enumerate (side_info):
if num! = 0 and min_flag == False:
min_flag = True
min_y = i
if num == 0 and min_flag == True:
max_y = i-1
min_flag = False
tooth_co_info = [min_y, min_x, max_y, max_x]
ex_dst = dst [tooth_co_info [0]: tooth_co_info [2] + 1, tooth_co_info [1]: tooth_co_info [3] +1]
print ("theta, sum: {}, {}" .format (theta, np.count_nonzero (ex_dst == 0, axis = 0) .sum ()))
if np.count_nonzero (ex_dst == 0, axis = 0) .sum ()<sum_cnt:
sum_cnt = np.count_nonzero (ex_dst == 0, axis = 0) .sum ()
best_theta = theta
print ("best")
print (best_theta, sum_cnt)

def pattern2 (src):
max_cnt = 0
best_theta = 0
for theta in range (-90,90 + 1):
dst = rotate_affine (src, theta)
print ("theta, count: {}, {}" .format (theta, np.count_nonzero (dst == 255, axis = 0) .max ()))
if np.count_nonzero (dst == 255, axis = 0) .max ()>max_cnt:
max_cnt = np.count_nonzero (dst == 255, axis = 0) .max ()
best_theta = theta
print ("best")
print ("theta, count: {}, {}". format (theta, max_cnt))
if __name__ =='__main__':
src = np.zeros ((int ((w ** 2 + h ** 2) ** (1/2)), int ((w ** 2 + h ** 2) ** (1/2)) ))
dx, dy = int ((w ** 2 + h ** 2) ** (1/2)/2) --int (w/2), int ((w ** 2 + h ** 2) ** (1/2)/2) --int (h/2)
for y in range (0, h): # translation
for x in range (0, w):
src [y + dy] [x + dx] = bi_mask [y] [x]
theta = 0
dst = rotate_affine (src, theta)
plt.imshow (dst)
plt.show ()
pattern1 (src) # switching required
# pattern2 (src)``````

I tried to make it clean as a whole by making full use of scipy.

The idea is to first align the center of gravity of the tooth shape on a flat surface, and then shape the tooth to find a balanced rotation position.

Balance is defined as the minimum moment of inertia with respect to the central axis when the entire tooth is viewed as a rigid body with a constant weight density and standing vertically.

The first center of gravity, translation, rotation are all`scipy.ndimage`I can't find only the moment calculation, so I made it myself, and to find the minimum moment`scipy.optimize`I used.

The tooth image was created by capturing the first image of the questioner (so the pixel size is different from the original). It is assumed that the tooth image does not protrude outside the frame due to rotation.

Below is a diagram of the code and execution results. It came out that a rotation of about -23.36 degrees is optimal. Compared to the questioner's result, it has turned slightly to the right and looks very well balanced.

* Initially, the left and right rotation moments were used in opposite directions, but it turned out that using the moment of inertia gives a more stable shape. In the case of a rotary moment, even if it is stretched horizontally, it will be stable if it is symmetrical, but if it is a moment of inertia, it will be advantageous to make the width as small as possible. The principle is the same as a figure skating player folding his arms and spinning at high speed.

``````import numpy as np
from scipy import ndimage, optimize
import matplotlib.pyplot as plt
import cv2
_, img = cv2.threshold (img, 100, 1, cv2.THRESH_BINARY)
# Translate and center the center of gravity
center = ndimage.center_of_mass (img)
img = ndimage.shift (img, np.array (img.shape)/2-np.array (center))
# Moment of inertia with respect to the central axis when the figure is erected
# mass: number of pixels on the vertical axis, radius: pixel distance to the left and right from the central axis
# Moment of inertia = Σ mass * radius ^ 2
def inertia (img):
mass = img.sum (axis = 0)
radius = np.abs (np.arange (-len (mass) // 2, len (mass) // 2 + 1))
if len (mass)! = len (radius):
# Rotate and measure moment of inertia = minimization function
def rotated_inertia (degree, img):
return inertia (ndimage.rotate (img, degree, reshape = False))
# Obtain the minimum moment of inertia when rotating -90 to 90 degrees by finding the minimum value of the minimization function.
res = optimize.minimize_scalar (
rotated_inertia, bounds = [-90,90], args = (img), method ='Bounded')
# Find the rotation angle at the minimum value
print (res.x)
# Actually rotate at the minimum value
img = ndimage.rotate (img, res.x, reshape = False)
plt.imshow (img)
plt.show ()``````

Result