简体   繁体   中英

Map Object to Circle in Python, OpenCV

Given a numpy array that contains a grey scale image of a segmented object. The numpy array has dimensions (32,32) . The background of this object is coded as zeros, the object itself has a number between (0,255]. Example (with dimensions (7,7) ):

# Input
> np.array([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,1,2,3,0,0],[0,0,2,2,2,0,0],[0,0,1,2,3,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]], dtype=np.uint8)

array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 2, 3, 0, 0],
       [0, 0, 2, 2, 2, 0, 0],
       [0, 0, 1, 2, 3, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]], dtype=uint8)

The object in the numpy array can have an arbitrary shape. I'd like to stretch (distort) the object such that it becomes a circle, regardless of its previous shape. The circle should fill the entire 32x32 array. The values should be linearly interpolated. Example (simplified, without linear interpolation):

# Desired output = Circle with linearly interpolated values
array([[0, 0, 0, 2, 0, 0, 0],
       [0, 1, 1, 2, 3, 3, 0],
       [0, 1, 1, 2, 3, 3, 0],
       [2, 2, 2, 2, 2, 2, 2],
       [0, 1, 2, 2, 3, 3, 0],
       [0, 1, 2, 2, 3, 3, 0],
       [0, 0, 0, 2, 0, 0, 0]], dtype=uint8)

How would I do that. Is there maybe an OpenCV function to distort objects to circles?

Lets divide your problem in 3 steps:

  1. Resize your image to 32x32
  2. Remap your matrix from a square to circle
  3. Trim edges and resize if necessary

Resize your image to 32x32

You can use the cv2.resize() to make the job for you. It will do all the necessary interpolation to make the image the size you want

square = np.array([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,1,2,3,0,0],[0,0,2,2,2,0,0],[0,0,1,2,3,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]], dtype=np.uint8)
resized_image = cv2.resize(square, (32, 32)) 

调整大小

Remap your matrix from a square to circle

Mathematically speaking there are many ways to create a transformation from a square matrix to a circle. There is a very interesting topic about this here . Basically, you have to define a function to go from a set of points [x, y] to another set of points [u, v] , one possible solution mentioned by Ch Fong is:

u = x √(x² + y² - x²y²) / √(x² + y²)

v = y √(x² + y² - x²y²) / √(x² + y²)

You can use this equation with cv2.remap() function to obtain the transformation you want. However there is a tool called squircle that do all the work for you, By reading their doc: you can make a simple code to make this transformation:

Import with:

from squircle import to_circle 
from PIL import Image

Call the transformation after opening the image:

circle = to_circle(square)

重新映射

Trim edges and resize if necessary

Now, you just have to trim all zeros from your matrix, and resize it again so all 32x32 matrix have content inside it. A solution you can use is described here where circle is your remapped image. After you cut all initial and final lines and columns that are all zeros, you have to resize it again to 32x32.

coords = np.argwhere(circle)
x_min, y_min = coords.min(axis=0)
x_max, y_max = coords.max(axis=0)
b = cropped = circle[x_min:x_max+1, y_min:y_max+1]
resized_cropped_image = cv2.resize(cropped, (32, 32)) 

修剪

Complete examples


Example 1 - Using your array:

你的例子


Example 2 - Using another image:

我的例子

You can find a Jupyter Notebook for this example in my Github page. The full code can be seen below

import numpy as np
import cv2
import matplotlib.pyplot as plt
from squircle import to_circle, to_square
from PIL import Image

square = np.asarray(Image.open('Picture.png').convert('L'))
#square = np.array([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,1,2,3,0,0],[0,0,2,2,2,0,0],[0,0,1,2,3,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]], dtype=np.uint8)
resized_image = cv2.resize(square, (32, 32)) 

circle = to_circle(resized_image)

coords = np.argwhere(circle)
x_min, y_min = coords.min(axis=0)
x_max, y_max = coords.max(axis=0)
b = cropped = circle[x_min:x_max+1, y_min:y_max+1]
resized_cropped_image = cv2.resize(cropped, (32, 32)) 


fig, axs = plt.subplots(2, 2, figsize=(10,10))

axs[0, 0].imshow(square)
axs[0, 0].set_title('Original')

axs[0, 1].imshow(resized_image)
axs[0, 1].set_title('Resized to 32x32')

axs[1, 0].imshow(circle)
axs[1, 0].set_title('Remapped to cicle')

axs[1, 1].imshow(resized_cropped_image)
axs[1, 1].set_title('Trimmed')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM