jackjameshoward jackjameshoward - 4 months ago 20
Python Question

Wrap image around a circle

What I'm trying to do in this example is wrap an image around a circle, like below.

original

wrapped

To wrap the image I simply calculated the x,y coordinates using trig.
The problem is the calculated X and Y positions are rounded to make them integers. This causes the blank pixels in seen the wrapped image above. The x,y positions have to be an integer because they are positions in lists.

I've done this again in the code following but without any images to make things easier to see. All I've done is create two arrays with binary values, one array is black the other white, then wrapped one onto the other.

The output of the code is.

example

import math as m
from PIL import Image # only used for showing output as image

width = 254.0
height = 24.0
Ro = 40.0

img = [[1 for x in range(int(width))] for y in range(int(height))]
cir = [[0 for x in range(int(Ro * 2))] for y in range(int(Ro * 2))]


def shom_im(img): # for showing data as image
list_image = [item for sublist in img for item in sublist]
new_image = Image.new("1", (len(img[0]), len(img)))
new_image.putdata(list_image)
new_image.show()

increment = m.radians(360 / width)
rad = Ro - 0.5
for i, row in enumerate(img):
hyp = rad - i
for j, column in enumerate(row):
alpha = j * increment
x = m.cos(alpha) * hyp + rad
y = m.sin(alpha) * hyp + rad
# put value from original image to its position in new image
cir[int(round(y))][int(round(x))] = img[i][j]


shom_im(cir)


I later found out about the Midpoint Circle Algorithm but I had worse result with that

midpoint

from PIL import Image # only used for showing output as image

width, height = 254, 24
ro = 40

img = [[(0, 0, 0, 1) for x in range(int(width))]
for y in range(int(height))]
cir = [[(0, 0, 0, 255) for x in range(int(ro * 2))] for y in range(int(ro * 2))]


def shom_im(img): # for showing data as image
list_image = [item for sublist in img for item in sublist]
new_image = Image.new("RGBA", (len(img[0]), len(img)))
new_image.putdata(list_image)
new_image.show()


def putpixel(x0, y0):
global cir
cir[y0][x0] = (255, 255, 255, 255)


def drawcircle(x0, y0, radius):
x = radius
y = 0
err = 0

while (x >= y):
putpixel(x0 + x, y0 + y)
putpixel(x0 + y, y0 + x)
putpixel(x0 - y, y0 + x)
putpixel(x0 - x, y0 + y)
putpixel(x0 - x, y0 - y)
putpixel(x0 - y, y0 - x)
putpixel(x0 + y, y0 - x)
putpixel(x0 + x, y0 - y)
y += 1
err += 1 + 2 * y
if (2 * (err - x) + 1 > 0):
x -= 1
err += 1 - 2 * x

for i, row in enumerate(img):
rad = ro - i
drawcircle(int(ro - 1), int(ro - 1), rad)

shom_im(cir)


Can anybody suggest a way to eliminate the blank pixels?

Fma Fma
Answer

I think what you need is a noise filter. There are many implementations from which I think Gaussian filter would give a good result. You can find a list of filters here. If it gets blurred too much: * keep your first calculated image * calculate filtered image * copy fixed pixels from filtered image to first calculated image

Here is a crude average filter written by hand:

cir_R = int(Ro*2) # outer circle 2*r
inner_r = int(Ro - 0.5 - len(img)) # inner circle r
for i in range(1, cir_R-1):
    for j in range(1, cir_R-1):
        if cir[i][j] == 0: # missing pixel
            dx = int(i-Ro)
            dy = int(j-Ro)
            pix_r2 = dx*dx + dy*dy # distance to center
            if pix_r2 <= Ro*Ro and pix_r2 >= inner_r*inner_r:
                cir[i][j] = (cir[i-1][j] + cir[i+1][j] + cir[i][j-1] +
                    cir[i][j+1])/4


shom_im(cir)

and the result:

enter image description here

This basically scans between two ranges checks for missing pixels and replaces them with average of 4 pixels adjacent to it. In this black white case it is all white.

Hope it helps!