将图像放大到像素级别

4
作为一个艺术项目,我将会对一张图片进行放大并定位到特定的像素点。我正在思考如何进行操作,希望能得到一些建议。
以下是输入参数:
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。 GitHub上的Image Pixel Zoom

您能否先澄清您是想要最终的图像还是透过一系列缩放级别制作动画呢? 如果您需要动画,您不仅需要描述最大和最小缩放级别(0和1),还需要说明相机移动的速度是线性的还是非线性的(线性的有点不自然,它会立即加速和停止)。 此外,由于相机所缩放到的像素通常不在中心,因此不仅存在缩放,还需要对相机进行平移以覆盖该像素;我们可以假设这是线性的,但确认会更好。 - Unreason
还有一点需要注意的是,算法会很简单,但我更倾向于使用现有的库来实现更聪明的调用。 - Unreason
对于给定的缩放因子,我只想要单个图像帧。之后,我将通过缩放因子0到1来创建动画。速度有点难以定义 - 我正在寻找一种“自然”的运动方式,当您从0到1缩放因子时,向像素移动具有固定步长(它不必是固定的,但这将是很好的)。此外,我不介意使用现有的库。 - zaf
3个回答

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

对于 zf = 0.3,在点 25,6 周围

alt text

对于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()

使用的库和逐像素的方法不是世界上最快的,但足以让你有足够的材料去尝试。

此外,上面的代码并不是非常干净。

编辑2(和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()

上述内容中有很多可以改进的地方。 :)

不错!感谢提供方程式和示例代码。我注意到一件事,就是如果选择的像素位于左上象限之类的位置,那么在放大时它会紧贴左上角。相反,当你放大时,我希望所选像素能够自动居中。 - zaf
更新了答案,这里没有任何问题,你确定你正确地应用了给定的公式吗? - Unreason
感谢更新的代码。我真的很喜欢+/-键触摸!好的 - 尝试在图像上放置一个带有红色(或任何颜色标记)像素的图像,然后将其缩放并且您会发现所选像素的左上角保持不变。我们想要的是像素中心本身在屏幕上。我正在使用一个1000x1000像素的图像,在300,300处有一个红色像素。 - zaf
再次更新了答案,之前缺少了翻译,缩放直接指向中心点。现在它在缩放的同时进行翻译。如果这对您有用,请告诉我,但仍然不完美;首先,我不确定缩放速率是否正确跟随翻译,其次,当缩放接近1时,像素本身并不居中,而是其中一个角落,所以我可能需要再次修改公式(现在没有时间)。 - Unreason
有一段时间了,但我已将您的代码转换为PHP https://github.com/ziqbal/ImagePixelZoom - zaf
显示剩余3条评论

0

编辑:艺术项目可以查看这个框架:Processing

我设计了一个用于一维图像的转换方法,您可以通过编写直接从原始图像到缩放图像的变换来实现您的约束:

由于您需要进行线性变换,因此它的形式为:

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来说,思路是类似的,但保持纵横比需要更多的努力。


有关一维的有趣想法。我会尝试理解你的方程式,看看能否理解它们。是的,我已经玩过processing,非常酷,如果我无法手动完成,可能会成为我使用的第一个工具。 - zaf

0
如果我理解你想做的事情是正确的话,你可以在图像处理程序(如Gimp)中打开图像,将缩放级别设置为1并截取屏幕。然后增加缩放级别并再次截取屏幕等等。然后使用mencoder从截图创建AVI文件。

你也可以编写一个脚本来使用增量缩放级别和固定裁剪运行imagemagic。将每个缩放后的图像保存为001.jpg、002.jpg等命名,并使用mencoder创建不同的电影。 - Ross
如果我无法掌握这些方程式,那么我将考虑自动化其他工具,但我知道在找到符合我要求的效果之前,我必须尝试几种不同的工具。 - zaf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接