将图像环绕一圈 [英] Wrap image around a circle

查看:102
本文介绍了将图像环绕一圈的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在此示例中,我想要做的是将图像环绕在一个圆圈上,如下所示.

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

要包装图像,我只是简单地使用trig计算了x,y坐标. 问题是计算出的X和Y位置会四舍五入以使其为整数.这将导致在上面的包装图像中看到空白像素. x,y位置必须是整数,因为它们是列表中的位置.

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.

代码的输出是.

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

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?

推荐答案

在填充圈子时遇到了问题,因为从错误的角度来看这是很不现实的.

You are having problems filling up your circle because you are approaching this from the wrong way – quite literally.

在将从源映射到目标时,您需要填充您的 target ,并将每个翻译后的像素映射到图片.这样一来,您完全不会错过一个像素,并且同样,您将永远不会绘制(或查找)一个像素以上.

When mapping from a source to a target, you need to fill your target, and map each translated pixel from this into the source image. Then, there is no chance at all you miss a pixel, and, equally, you will never draw (nor lookup) a pixel more than once.

以下内容有些粗糙,仅作为概念示例.我首先编写了一些代码,从上到下绘制了一个实心圆.然后,我添加了更多代码以删除中心部分(并为内半径"添加了变量Ri).这将导致出现一个实心环,其中所有像素仅绘制一次:从上到下,从左到右.

The following is a bit rough-and-ready, it only serves as a concept example. I first wrote some code to draw a filled circle, top to bottom. Then I added some more code to remove the center part (and added a variable Ri, for "inner radius"). This leads to a solid ring, where all pixels are only drawn once: top to bottom, left to right.

如何精确绘制戒指实际上并不重要!起初我使用trig是因为我想重新使用角度钻头,但是也可以用毕达哥拉斯的方法甚至布雷森纳姆的绕圈程序来完成.您需要记住的是,您遍历 target 行和列,而不是 source .这提供了实际的xy坐标,您可以将其输入到重新映射过程中.

How you exactly draw the ring is not actually important! I used trig at first because I thought of re-using the angle bit, but it can be done with Pythagorus' as well, and even with Bresenham's circle routine. All you need to keep in mind is that you iterate over the target rows and columns, not the source. This provides actual x,y coordinates that you can feed into the remapping procedure.

完成上述工作后,我编写了trig函数,将的坐标转换为原始图像中的像素.为此,我创建了一个包含一些文本的测试图像:

With the above done and working, I wrote the trig functions to translate from the coordinates I would put a pixel at into the original image. For this, I created a test image containing some text:

也是一件好事,因为在第一次尝试中,我两次获得了文本(左一次,一次右一次)并进行了镜像-需要一些小的调整.还要注意背景网格.我添加了它以检查是否正确绘制了最上"和最下"线(最外圈和最内圈).

and a good thing that was, too, as in the first attempt I got the text twice (once left, once right) and mirrored – that needed a few minor tweaks. Also note the background grid. I added that to check if the 'top' and 'bottom' lines – the outermost and innermost circles – got drawn correctly.

使用此图像和RoRi分别以100和50运行我的代码,我得到以下结果:

Running my code with this image and Ro,Ri at 100 and 50, I get this result:

您可以看到,trig函数使它从最右边开始,顺时针移动,并使图像的顶部朝外.所有这些都可以进行微调,但是通过这种方式,它可以模仿您想要绘制图像的方向.

You can see that the trig functions make it start at the rightmost point, move clockwise, and have the top of the image pointing outwards. All can be trivially adjusted, but this way it mimics the orientation that you want your image drawn.

这是虹膜图像的结果,使用33作为内半径:

This is the result with your iris-image, using 33 for the inner radius:

这是一个很好的动画,显示了映射的稳定性:

and here is a nice animation, showing the stability of the mapping:

最后,我的代码是:

import math as m
from PIL import Image

Ro = 100.0
Ri = 50.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))]

# image = Image.open('0vWEI.png')
image = Image.open('this-is-a-test.png')
# data = image.convert('RGB')
pixels = image.load()
width, height = image.size

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


for i in range(int(Ro)):
    # outer_radius = Ro*m.cos(m.asin(i/Ro))
    outer_radius = m.sqrt(Ro*Ro - i*i)
    for j in range(-int(outer_radius),int(outer_radius)):
        if i < Ri:
            # inner_radius = Ri*m.cos(m.asin(i/Ri))
            inner_radius = m.sqrt(Ri*Ri - i*i)
        else:
            inner_radius = -1
        if j < -inner_radius or j > inner_radius:
            # this is the destination
            # solid:
            # cir[int(Ro-i)][int(Ro+j)] = (255,255,255)
            # cir[int(Ro+i)][int(Ro+j)] = (255,255,255)
            # textured:

            x = Ro+j
            y = Ro-i
            # calculate source
            angle = m.atan2(y-Ro,x-Ro)/2
            distance = m.sqrt((y-Ro)*(y-Ro) + (x-Ro)*(x-Ro))
            distance = m.floor((distance-Ri+1)*(height-1)/(Ro-Ri))
        #   if distance >= height:
        #       distance = height-1
            cir[int(y)][int(x)] = pixels[int(width*angle/m.pi) % width, height-distance-1]
            y = Ro+i
            # calculate source
            angle = m.atan2(y-Ro,x-Ro)/2
            distance = m.sqrt((y-Ro)*(y-Ro) + (x-Ro)*(x-Ro))
            distance = m.floor((distance-Ri+1)*(height-1)/(Ro-Ri))
        #   if distance >= height:
        #       distance = height-1
            cir[int(y)][int(x)] = pixels[int(width*angle/m.pi) % width, height-distance-1]

shom_im(cir)

注释掉的线条画出一个纯白色的环.请注意这里和那里的各种调整以获得最佳效果.例如,distance是从圆环的中心开始测量的,因此返回接近中心的较低值和返回圆的外部最大值.直接将其映射回目标图像将显示其顶部向内"的文本,指向内部孔.因此,我用height - distance - 1反转了此映射,其中-1是要使其再次从0映射到height.

The commented-out lines draw a solid white ring. Note the various tweaks here and there to get the best result. For instance, the distance is measured from the center of the ring, and so returns a low value for close to the center and the largest values for the outside of the circle. Mapping that directly back onto the target image would display the text with its top "inwards", pointing to the inner hole. So I inverted this mapping with height - distance - 1, where the -1 is to make it map from 0 to height again.

distance本身的计算中也有类似的解决方法.没有调整Ri+1height-1,将不会绘制最里面或最外面的行,这表明计算仅偏离了一个像素(这正是该网格的目的).

A similar fix is in the calculation of distance itself; without the tweaks Ri+1 and height-1 either the innermost or the outermost row would not get drawn, indicating that the calculation is just one pixel off (which was exactly the purpose of that grid).

这篇关于将图像环绕一圈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆