Thread: Reducing size of image pixel data to display in window

  1. #1
    Registered User
    Join Date
    Jun 2009
    Posts
    101

    Reducing size of image pixel data to display in window

    I need help figuring out how to reduce the size of an image and display it in a window. Here's what I have so far:

    Code:
    char *buffer;
    int x,y;
    int image_width = 1920;
    int image_height = 1080;
    int preview_width = 304;
    int preview_height = 176;
    int num_channels = 3; //may be 4 if alpha channel is present
    int pixeldata;
    int pixelout[4]; //may hold 3 or 4 8-bit pixel values
    
    float calc_width = ((float)img_width/(float)preview_width);
    float calc_height = ((float)img_height/(float)preview_height);
    
    float pixelcounter = 0;
    
    for(y=0; y<preview_height; y++){
    	for(x=0; x<preview_width; x++){
    
    		memcpy(&pixeldata,(buffer+(int)pixelcounter),num_channels);
    		pixelout[0] = (pixeldata&0x000000FF);		//red
    		pixelout[1] = (pixeldata&0x0000FF00)>>8;	//green
    		pixelout[2] = (pixeldata&0x00FF0000)>>16;	//blue
    		
    		//assign bytes to NSBitmapImageRep object
    		[nsBitmapImageRepObj setPixel:pixelout atX:x y:y];
    
    		//skip bytes to reduce width
    		pixelcounter += (int)calc_width*3;
    	}
    	//skip bytes to reduce height
    	pixelcounter = ((y*(int)calc_height)*(img_width*num_channels));
    }
    The problem I have is that the preview image gets cut off because the width is divided by an integer and not a float. If I divide it by the float value of calc_width, there is no guarantee that the next read into the buffer will be on the three pixel RGB boundary. The only way to guarantee this is to divide the image by a factor of 3. This works, but then accuracy is compromised.

    What I need to do is divide the image width using the float value, then "snap" the result to the nearest red pixel. Making the result a multiple of 3 doesn't work because it just converts calc_width back to an integer.

    Sorry if this isn't the best explanation, but hopefully someone will be able to figure out what I'm trying to do!
    Last edited by synthetix; 11-16-2009 at 04:22 AM.

  2. #2
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    I just explained this to someone at work last week who was doing a very similar thing with graphics.
    Integer divisions lose information (the remainder) but integer multiplications don't (as long as they don't overflow).
    One trick then is to perform multiplications before the divisions.
    In your case (and the one I described to someone else) your multiplications are being done through successive additions. Those are your additions to pixelcounter.

    Instead of performing multiplications via successive additions, you can just perform a multiplication:
    E.g. Replace this line
    Code:
    memcpy(&pixeldata,(buffer+(int)pixelcounter),num_channels);
    with just this
    Code:
    memcpy(&pixeldata, (buffer +
        (y*preview_height/img_height)*(img_width*num_channels) +
        (x*preview_width/img_width)*3), num_channels);
    and remove the no-longer-necessary code dealing with pixelcounter.
    Now this will generate correct results and you can reapply the optimisation techniques you know such as loop-hoisting to get a faster solution from there.

    It can be done without the divisions inside the loops if you do some research, but don't trying doing that before you know how to get it to work at all.
    So yeah this is also a lesson in "get it working first, then get it fast".

    Note that in a final solution I would recommend getting rid of the memcpy and just accessing the three bytes consecutive from the address calculated. This removes endian issues and removes one cause of the slowdown.

    Did you know that you can get a much better image by averaging groups of pixels instead of just skipping them? You can also perform bilinear filtering or similar to improve the image quality. There's also http://en.wikipedia.org/wiki/Lanczos_resampling if you get really enthusiastic!
    Last edited by iMalc; 11-16-2009 at 12:47 AM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  3. #3
    Registered User
    Join Date
    Jun 2009
    Posts
    101
    Awesome. That totally worked! Here's my new loop:

    Code:
    for(y=0; y<preview_height; y++){
    	for(x=0; x<preview_width; x++){
    			
    		memcpy(&pixeldata, buffer+(
    			((y*img_height/preview_height) * (img_width*num_channels)) +
    			( x*img_width/preview_width)   *  num_channels), num_channels);
    						 
    		pixelout[0] = (pixeldata&0x000000FF);		//red
    		pixelout[1] = (pixeldata&0x0000FF00)>>8;	//green
    		pixelout[2] = (pixeldata&0x00FF0000)>>16;	//blue
    		
    		//populate bitmap
    		[nsBitmapImageRepObj setPixel:pixelout atX:x y:y];	
    	}
    }
    The code is much cleaner, too. Thanks!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. xor linked list
    By adramalech in forum C Programming
    Replies: 23
    Last Post: 10-14-2008, 10:13 AM
  2. How do I display data in the Right Window in MFC.
    By Jasonc in forum Windows Programming
    Replies: 2
    Last Post: 09-07-2002, 06:31 AM
  3. OpenGL and Windows
    By sean345 in forum Game Programming
    Replies: 5
    Last Post: 06-24-2002, 10:14 PM
  4. gcc problem
    By bjdea1 in forum Linux Programming
    Replies: 13
    Last Post: 04-29-2002, 06:51 PM
  5. opengl code not working
    By Unregistered in forum Windows Programming
    Replies: 4
    Last Post: 02-14-2002, 10:01 PM