开发者

Zoom image to pixel level

For an art project, one of the things I'll be doing is zooming in on an image to a particular pixel. I've been rubbing my chin and would love some advice on how to proceed.

Here are the input parameters:

Screen:
sw - screen width
sh - screen height

Image:
iw - image width
ih - image height

Pixel:
px - x position of pixel in imag开发者_运维百科e
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

Outputs:

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.

One idea I had was to use something like povray and move the camera towards a big image texture or some library (e.g. pygame) to do the zooming. Anyone think of something more clever with simple pseudo code?

To keep it more simple you can make the image and screen have the same aspect ratio. I can live with that.

I'll update with more info as its required.

UPDATE

Converted accepted answer to PHP

Image Pixel Zoom on GitHub


If color values of original image are given as array

image[x][y]

Then color values of zoomed image are

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

Regarding the windows size/image size - initial preparation of image should take care of that: zoom the image up to the point that it would not fit the window any more and fill the remaining pixels with your preferred background colour.

In python you can do something like

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

after you set

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

with objects from

import Image

EDIT: Given stackoverflow logo you will get

Zoom image to pixel level

for zf = 0.3, around point 25,6

Zoom image to pixel level

for zf = 0.96, around the same point

Images were obtained with following code

#!/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()

The libraries used and pixel by pixel approach are not the fastest in the world, but will give you enough material to play with.

Also the above code is not very clean.

EDIT2(and3, centered the formula): Here's another attempt, added translation, but I have a feeling this is not final either (don't have the time to check the formulas). Also the speed of the translation is constant, but that may lead to zooming to slow and showing background (if the point to which you are zooming is too close to the edge).
I've also added a point on the original image so that it is visible what happens with it without need to paint on original image.

#!/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()

There is quite a lot in the above that could be improved. :)


If I understand correctly what you want to do. You can open image in a graphics program (like Gimp) set zoom level at 1 and take a screenshot. Then increase zoom level and take screenshot again etc. Then use mencoder to create AVI from screenshots.


Edit : For art projects you can check this framework : Processing

I make it for 1D, you start by writing the direct transform from original image to zoomed image with your constraints :

As you want a linear transformation, it is in the form :

D( x ) = a x + b

You want :

for z = 0 : D( px ) = px D( px + 1 ) = px + 1

for z = 1 : D( px ) = 0 D( px + 1 ) = sw

This gives :

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

for z = 1 : a = sw , b = -sw . px , D( x ) = sw.x - sw.px

For all z, you use a linear combination of the two :

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

Now you write the inverse function to get the original coordinates from the output coordinates :

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

Which allows you to fill the output image from the input image. For each output pixel the value is OriginalPixel[ ID( xout ) ] ( And when ID( xout ) is not in [0..sw] you use the background value )

For 2D the idea is similar, but keeping the aspect ratio will need a little more effort.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜