Hi,

to get more experience with pycuda and textures I just hacked a script together which rotates images. It should be able to handle all images that PIL can read and it shouldn't need anything but numpy and PIL. This is quite verbose so that it should be fairly easy to follow through, so maybe it would make a good contribution for the demo folder in pycuda. If you think so too Andreas, feel free to include it.

Thanks for your support!
Holger


#!/usr/bin/env python -tt
# encoding: utf-8
#
# Created by Holger Rapp on 2009-03-11.
# [email protected]
#

import pycuda.driver as cuda
import pycuda.compiler
import pycuda.autoinit
import numpy
from math import pi,cos,sin

_rotation_kernel_source = """
texture<float, 2> tex;

__global__ void copy_texture_kernel(
    const float resize_val, 
    const float alpha, 
    unsigned short oldiw, unsigned short oldih,
    unsigned short newiw, unsigned short newih,
    unsigned char* data) {

        // calculate pixel idx
        unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
        unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;
        
        // We might be outside the reachable pixels. Don't do anything
        if( (x >= newiw) || (y >= newih) )
            return;
        
        // calculate offset into destination array
        unsigned int didx = y * newiw + x;
        
        // calculate offset into source array (be aware of rotation and scaling)
        float xmiddle = (x-newiw/2.) / resize_val;
        float ymiddle = (y-newih/2.) / resize_val;
        float sx = ( xmiddle*cos(alpha)+ymiddle*sin(alpha) + oldiw/2.) ;
        float sy = ( -xmiddle*sin(alpha)+ymiddle*cos(alpha) + oldih/2.);
        
        if( (sx < 0) || (sx >= oldiw) || (sy < 0) || (sy >= oldih) ) { 
            data[didx] = 255; 
            return;
        }

        data[didx] = tex2D(tex, sx, sy);
    }
"""
mod_copy_texture=pycuda.compiler.SourceModule( _rotation_kernel_source )

copy_texture_func = mod_copy_texture.get_function("copy_texture_kernel")
texref = mod_copy_texture.get_texref("tex")

def rotate_image( a, resize = 1.5, angle = 20., interpolation = "linear", blocks = (16,16,1)  ):
    """
    Rotates the array. The new array has the new size and centers the
    picture in the middle.
    
    a             - array (2-dim)
    resize        - new_image w/old_image w
    angle         - degrees to rotate the image
    interpolation - "linear" or None
    blocks        - given to the kernel when run


    returns: a new array with dtype=uint8 containing the rotated image
    """
    angle = angle/180. *pi
    
    # Convert this image to float. Unsigned int texture gave 
    # strange results for me. This conversion is slow though :(
    a = a.astype("float32")

    # Calculate the dimensions of the new image
    calc_x = lambda (x,y): (x*a.shape[1]/2.*cos(angle)-y*a.shape[0]/2.*sin(angle))
    calc_y = lambda (x,y): (x*a.shape[1]/2.*sin(angle)+x*a.shape[0]/2.*cos(angle))

    xs = [ calc_x(p) for p in [ (-1.,-1.),(1.,-1.),(1.,1.),(-1.,1.) ] ]
    ys = [ calc_y(p) for p in [ (-1.,-1.),(1.,-1.),(1.,1.),(-1.,1.) ] ]

    new_image_dim = (
        int(numpy.ceil(max(ys)-min(ys))*resize),
        int(numpy.ceil(max(xs)-min(xs))*resize),
    )
    
    # Now generate the cuda texture
    cuda.matrix_to_texref(a, texref, order="C")
    
    # We could set the next if we wanted to address the image
    # in normalized coordinates ( 0 <= coordinate < 1.)
    # texref.set_flags(cuda.TRSF_NORMALIZED_COORDINATES)
    if interpolation == "linear":
        texref.set_filter_mode(cuda.filter_mode.LINEAR)

    # Calculate the gridsize. This is entirely given by the size of our image. 
    gridx = new_image_dim[1]/blocks[1] if \
            new_image_dim[1]%blocks[1]==1 else new_image_dim[1]/blocks[1] +1
    gridy = new_image_dim[0]/blocks[0] if \
            new_image_dim[0]%blocks[0]==0 else new_image_dim[0]/blocks[0] +1

    # Get the output image
    output = numpy.zeros(new_image_dim,dtype="uint8")
    
    # Call the kernel
    copy_texture_func(
        numpy.float32(resize), numpy.float32(angle),
        numpy.uint16(a.shape[1]), numpy.uint16(a.shape[0]),
        numpy.uint16(new_image_dim[1]), numpy.uint16(new_image_dim[0]),
            cuda.Out(output),texrefs=[texref],block=blocks,grid=(gridx,gridy))
    
    return output

if __name__ == '__main__':
    import Image
    import sys
    
    def main( ):
        if len(sys.argv) != 2:
            print "You should really read the source...\n\nUsage: rotate.py <Imagename>\n"
            sys.exit(-1)

        # Open, convert to grayscale, convert to numpy array
        img = Image.open(sys.argv[1]).convert("L")
        i = numpy.fromstring(img.tostring(),dtype="uint8").reshape(img.size[1],img.size[0])
        
        # Rotate & convert back to PIL Image
        irot = rotate_image(i)
        rotimg = Image.fromarray(irot,mode="L")

        # Save and display
        rotimg.save("rotated.png")
        rotimg.show()
    
    main()




----------------------------------------
Dipl.-Phys. Holger Rapp

Institut für Mess- und Regelungstechnik
Universität Karlsruhe (TH)
Engler-Bunte-Ring 21
76131 Karlsruhe, Germany
Geb. 40.32, Zi. 232, zweite Etage

Tel:  +49 (0)721 / 608-2341
Fax:  +49 (0)721 / 661874
Mail: [email protected]
Web:  www.mrt.uni-karlsruhe.de
----------------------------------------



_______________________________________________
PyCuda mailing list
[email protected]
http://tiker.net/mailman/listinfo/pycuda_tiker.net

Reply via email to