Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rotate an image to get not-null pixels?

In the image I linked below, I need to get all the yellow/green pixels in this rotated rectangle and get rid of the blue background, so that the rectangle's axis are aligned with the x and y axis.

I'm using numpy but don't have a clue what I should do.

I uploaded the array in this drive in case anyone would like to work with the actual array

Original Image print

Thanks for the help in advance.

like image 294
Bernard Reznik Avatar asked Dec 18 '25 16:12

Bernard Reznik


2 Answers

I used the same image as user2640045, but different approach.

import numpy as np
import cv2

# load and convert image to grayscale
img = cv2.imread('image.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# binarize image
threshold, binarized_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# find the largest contour
contours, hierarchy = cv2.findContours(binarized_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
c = max(contours, key = cv2.contourArea)

# get size of the rotated rectangle
center, size, angle = cv2.minAreaRect(c)

# get size of the image
h, w, *_ = img.shape

# create a rotation matrix and rotate the image
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_img = cv2.warpAffine(img, M, (w, h))

# crop the image
pad_x = int((w - size[0]) / 2)
pad_y = int((h - size[1]) / 2)

cropped_img = rotated_img[pad_y : pad_y + int(size[1]), pad_x : pad_x + int(size[0]), :]

Result:

enter image description here

like image 55
dlt_w Avatar answered Dec 21 '25 05:12

dlt_w


I realize there is a allow_pickle=False option in numpys load method but I didn't feel comfortable with unpickling/using data from the internet so I used the small image. After removing the coordinate system and stuff I had

enter image description here

I define two helper methods. One to later rotate the image taken from an other stack overflow thread. See link below. And one to get a mask being one at a specified color and zero otherwise.

import numpy as np
import matplotlib.pyplot as plt
import sympy
import cv2
import functools

color = arr[150,50]

def similar_to_boundary_color(arr, color=tuple(color)):
    mask = functools.reduce(np.logical_and, [np.isclose(arr[:,:,i], color[i]) for i in range(4)])
    return mask

#https://stackoverflow.com/a/9042907/2640045
def rotate_image(image, angle):
    image_center = tuple(np.array(image.shape[1::-1]) / 2)
    rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
    result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR)
    return result

Next I calculate the angle to rotate about. I do that by finding the lowest pixel at width 50 and 300. I picked those since they are far enough from the boundary to not be effected by missing corners etc..

i,j = np.where(~similar_to_boundary_color(arr))

slope = (max(i[j == 50])-max(i[j == 300]))/(50-300)
angle = np.arctan(slope)
arr = rotate_image(arr, np.rad2deg(angle))
plt.imshow(arr)

roated image.

One way of doing the cropping is the following. You calculate the mid in height and width. Then you take two slices around the mid say 20 pixels in one direction and to until the mid in the other one. The biggest/smallest index where the pixel is white/background colored is a reasonable point to cut.

i,j = np.where(~(~similar_to_boundary_color(arr) & ~similar_to_boundary_color(arr, (0,0,0,0)))) 

imid, jmid = np.array(arr.shape)[:2]/2
imin = max(i[(i < imid) & (jmid - 10 < j) & (j < jmid + 10)])
imax = min(i[(i > imid) & (jmid - 10 < j) & (j < jmid + 10)])
jmax = min(j[(j > jmid) & (imid - 10 < i) & (i < imid + 10)])
jmin = max(j[(j < jmid) & (imid - 10 < i) & (i < imid + 10)])

arr = arr[imin:imax,jmin:jmax]
plt.imshow(arr)

and the result is: enter image description here

like image 25
user2640045 Avatar answered Dec 21 '25 05:12

user2640045



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!