将图像缩放到像素级别

时间:2010-04-06 10:54:53

标签: algorithm graphics language-agnostic zoom

对于一个艺术项目,我要做的其中一件事就是将图像放大到特定的像素。我一直在揉着下巴,并希望得到一些关于如何进行的建议。

以下是输入参数:

Screen:
sw - screen width
sh - screen height

Image:
iw - image width
ih - image height

Pixel:
px - x position of pixel in image
py - y position of pixel in image

Zoom:
zf - zoom factor (0.0 to 1.0)

Background colour:
bc - background colour to use when screen and image aspect ratios are different

输出:

The zoomed image (no anti-aliasing)
The screen position/dimensions of the pixel we are zooming to.

When zf is 0 the image must fit the screen with correct aspect ratio.
When zf is 1 the selected pixel fits the screen with correct aspect ratio.

我的一个想法是使用类似povray的东西,并将相机移向大图像纹理或某些库(例如pygame)进行缩放。有人用简单的伪代码想到更聪明的东西吗?

为了使其更简单,您可以使图像和屏幕具有相同的宽高比。我可以忍受。

我会根据需要更新更多信息。

更新

将已接受的答案转换为PHP

Image Pixel Zoom on GitHub

3 个答案:

答案 0 :(得分:5)

如果原始图像的颜色值以数组

给出
image[x][y]

然后缩放图像的颜色值为

image[x+zf*(px-x)][y+zf*(py-y)]

关于窗口大小/图像大小 - 图像的初始准备应该注意:将图像缩放到不再适合窗口的点,并用您喜欢的背景颜色填充剩余的像素。

在python中,您可以执行类似

的操作
def naivezoom(im, px, py, zf, bg):
    out = Image.new(im.mode, im.size)        
    pix = out.load()
    iw, ih = im.size
    for x in range(iw):
        for y in range(ih):
            xorg = x + zf*(px - x)
            yorg = y + zf*(py - y)
            if xorg >= 0 and xorg < iw and yorg >= 0 and yorg < ih:
                pix[x,y] = im.getpixel( (xorg , yorg) )
            else:
                pix[x,y] = bg
    return out
设置

im = Image.open("filename.ext")

来自

的对象
import Image

编辑: 鉴于stackoverflow徽标,您将获得

alt text http://i40.tinypic.com/i1cwg7.jpg

对于zf = 0.3,在点25,6附近

alt text http://i41.tinypic.com/24g5848.png

对于zf = 0.96,大约在同一点

使用以下代码

获得图像
#!/bin/env python
from Tkinter import *
import Image
import ImageTk

def naivezoom(im, p, zf, bg):
    out = Image.new(im.mode, im.size)
    pix = out.load()
    iw, ih = im.size
    for x in range(iw):
        for y in range(ih):
            xorg = x + zf*(p[0] - x)
            yorg = y + zf*(p[1] - y)
            if xorg >= 0 and xorg < iw and yorg >= 0 and yorg < ih:
                pix[x,y] = im.getpixel( (xorg , yorg) )
            else:
                pix[x,y] = bg
    return out

class NaiveTkZoom:
    def __init__(self, parent=None):
        root = Tk()
        self.im = Image.open('logo.jpg')
        self.zf = 0.0
        self.deltazf = 0.02
        self.p = ( 0.1*self.im.size[0],0.1*self.im.size[1])
        self.bg = 255
        canvas = Canvas(root, width=self.im.size[0]+20 , height=self.im.size[1]+20)
        canvas.pack()
        root.bind('<Key>', self.onKey)
        self.canvas = canvas
        self.photo = ImageTk.PhotoImage(self.im)
        self.item = self.canvas.create_image(10, 10, anchor=NW, image=self.photo)
    def onKey(self, event):
        if event.char == "+":
            if self.zf < 1:
                self.zf += self.deltazf
        elif event.char == "-":
            if self.zf > 0:
                self.zf -= self.deltazf
        self.out = naivezoom(self.im, self.p, self.zf, self.bg)
        self.photo = ImageTk.PhotoImage(self.out)
        self.canvas.delete(self.item)
        self.item = self.canvas.create_image(10, 10, anchor=NW, image=self.photo)
        print self.p, self.zf

if __name__ == "__main__":
    NaiveTkZoom()
    mainloop()

使用的库和逐像素的方法并不是世界上最快的,但会为您提供足够的材料。

上面的代码也不是很干净。

EDIT2(和3,以公式为中心): 这是另一种尝试,增加了翻译,但我感觉这也不是最终的(没有时间检查公式)。此外,平移的速度是恒定的,但这可能会导致缩放变慢并显示背景(如果您缩放的点太靠近边缘)。
我还在原始图像上添加了一个点,以便可以看到它发生的情况,而无需在原始图像上进行绘制。

#!/bin/env python
from Tkinter import *
import Image
import ImageTk

def markImage(im, p, bg):
    pix = im.load()
    pix[ p[0], p[1] ] = bg

def naiveZoom(im, p, zf, bg):
    out = Image.new(im.mode, im.size)
    pix = out.load()
    iw, ih = im.size
    for x in range(iw):
        for y in range(ih):
            xorg = x + zf*(p[0]+0.5-x) + zf*(1-zf)*(p[0]-iw/2)
            yorg = y + zf*(p[1]+0.5-y) + zf*(1-zf)*(p[1]-ih/2)
            if xorg >= 0 and xorg < iw and yorg >= 0 and yorg < ih:
                pix[x,y] = im.getpixel( (xorg , yorg) )
            else:
                pix[x,y] = bg
    return out

class NaiveTkZoom:
    def __init__(self, parent=None):
        root = Tk()
        self.im = Image.open('py.jpg')
        self.zf = 0.0
        self.deltazf = 0.05
        self.p = (round(0.3*self.im.size[0]), round(0.3*self.im.size[1]) )
        self.bg = 255
        markImage(self.im, self.p, self.bg)
        canvas = Canvas(root, width=self.im.size[0]+20 , height=self.im.size[1]+20)
        canvas.pack()
        root.bind('<Key>', self.onKey)
        self.canvas = canvas
        self.photo = ImageTk.PhotoImage(self.im)
        self.item = self.canvas.create_image(10, 10, anchor=NW, image=self.photo)
        self.change = False
    def onKey(self, event):
        if event.char == "+":
            if self.zf < 1:
                self.zf += self.deltazf
                self.change = True
        elif event.char == "-":
            if self.zf > 0:
                self.zf -= self.deltazf
                self.change = True
        if self.change:
            self.out = naiveZoom(self.im, self.p, self.zf, self.bg)
            self.photo = ImageTk.PhotoImage(self.out)   
            self.canvas.delete(self.item)
            self.change = False
        self.item = self.canvas.create_image(10, 10, anchor=NW, image=self.photo)
        print self.p, self.zf

if __name__ == "__main__":
    NaiveTkZoom()
    mainloop()

上面有很多可以改进的东西。 :)

答案 1 :(得分:0)

修改:对于艺术项目,您可以查看此框架: 的 Processing

我为1D制作它,你首先将原始图像的直接变换写入带有约束的缩放图像:

如您所希望的那样,线性转换的形式为:

D(x)= a x + b

你想:

表示z = 0: D(px)= px D(px + 1)= px + 1

表示z = 1: D(px)= 0 D(px + 1)= sw

这给出了:

对于z = 0:a = 1,b = 0,D(x)= x

对于z = 1:a = sw,b = -sw。 px,D(x)= sw.x - sw.px

对于所有z,您使用两者的线性组合:

D(x)= z(sw.x - sw.px)+(1 - z)(x) D(x)=(z.sw + 1 - z).x - z.sw.px

现在编写反函数以从输出坐标获取原始坐标:

ID(xout)=(xout + z.sw.px)/(z.sw + 1 - z)

允许您从输入图像填充输出图像。对于每个输出像素,值为OriginalPixel [ID(xout)](当ID(xout)不在[0..sw]中时,使用背景值)

对于2D,这个想法是相似的,但保持纵横比需要更多的努力。

答案 2 :(得分:0)

如果我理解你想做什么。 您可以在图形程序(如Gimp)中打开图像,将缩放级别设置为1并截取屏幕截图。然后增加缩放级别并再次截取屏幕截图等。然后使用mencoder从截图中创建AVI。