views:

12189

answers:

2

Objective: take a UIImage, crop out a square in the middle, change size of square to 320x320 pixels, slice up the image into 16 80x80 images, save the 16 images in an array.

Here's my code:

CGImageRef originalImage, resizedImage, finalImage, tmp;
float imgWidth, imgHeight, diff;
UIImage *squareImage, *playImage;
NSMutableArray *tileImgArray;
int r, c;

originalImage = [image CGImage];

imgWidth = image.size.width;
imgHeight = image.size.height;
diff = fabs(imgWidth - imgHeight);

if(imgWidth > imgHeight){
 resizedImage = CGImageCreateWithImageInRect(originalImage, CGRectMake(floor(diff/2), 0, imgHeight, imgHeight));
}else{
 resizedImage = CGImageCreateWithImageInRect(originalImage, CGRectMake(0, floor(diff/2), imgWidth, imgWidth));
}
CGImageRelease(originalImage);

squareImage = [UIImage imageWithCGImage:resizedImage];  
if(squareImage.size.width != squareImage.size.height){
 NSLog(@"image cutout error!");
 //*code to return to main menu of app, irrelevant here
}else{
 float newDim = squareImage.size.width;
 if(newDim != 320.0){
  CGSize finalSize = CGSizeMake(320.0, 320.0);
  UIGraphicsBeginImageContext(finalSize);
  [squareImage drawInRect:CGRectMake(0, 0, finalSize.width, finalSize.height)];
  playImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
 }else{
  playImage = squareImage;
 }
}

finalImage = [playImage CGImage];
tileImgArray = [NSMutableArray arrayWithCapacity:0];
for(int i = 0; i < 16; i++){
 r = i/4;
 c = i%4;
 //*
 tmp = CGImageCreateWithImageInRect(finalImage, CGRectMake(c*tileSize, r*tileSize, tileSize, tileSize));
 [tileImgArray addObject:[UIImage imageWithCGImage:tmp]];
}

The code works correctly when the original (the variable image) has its smaller dimension either bigger or smaller than 320 pixels. When it's exactly 320, the resulting 80x80 images are almost entirely black, some with a few pixels at the edges that may (I can't really tell) be from the original image.

I tested by displaying the full image both directly:

[UIImage imageWithCGImage:finalImage];

And indirectly:

[UIImage imageWithCGImage:CGImageCreateWithImageInRect(finalImage, CGRectMake(0, 0, 320, 320))];

In both cases, the display worked. The problems only arise when I attempt to slice out some part of the image.

+3  A: 

After some more experimentation, I found the following solution (I still don't know why it didn't work as originally written, though.) But anyway, the slicing works after the resize code is put in place even when resizing is unnecessary:

if(newDim != 320.0){
            CGSize finalSize = CGSizeMake(320.0, 320.0);
            UIGraphicsBeginImageContext(finalSize);
            [squareImage drawInRect:CGRectMake(0, 0, finalSize.width, finalSize.height)];
            playImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
}else{
            CGSize finalSize = CGSizeMake(320.0, 320.0);
            UIGraphicsBeginImageContext(finalSize);
            [squareImage drawInRect:CGRectMake(0, 0, finalSize.width, finalSize.height)];
            playImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
}

Anyone has any clue WHY this is going on?

P.S. Yes, if/else is no longer required here. Removing it before I knew it was going to work would be stupid, though.

executor21
+1  A: 

Just out of curiosity, why did you make your mutable array with bound of 0 when you know you're going to put 16 things in it?

Well, aside from that, I've tried the basic techniques you used for resizing and slicing (I did not need to crop, because I'm working with images that are already square) and I'm unable to reproduce your problem in the simulator. You might want to try breaking your code into three separate functions (crop to square, resize, and slice into pieces) and then test the three separately so you can figure out which of the three steps is causing the problems (ie. input images that you've manipulated in a normal graphics program instead of using objective c and then inspect what you get back out!).

I'll attach my versions of the resize and slice functions below, which will hopefully be helpful. It was nice to have your versions to look at, since I didn't have to find all the methods by myself for once. :)

Just as a note, the two dimensional array mentioned is my own class built out of NSMutableArrays, but you could easily implement your own version or use a flat NSMutableArray instead. ;)

// cut the given image into a grid of equally sized smaller images
// this assumes that the image can be equally divided in the requested increments
// the images will be stored in the return array in [row][column] order
+ (TwoDimensionalArray *) chopImageIntoGrid : (UIImage *) originalImage : (int) numberOfRows : (int) numberOfColumns
{   
// figure out the size of our tiles
int tileWidth = originalImage.size.width / numberOfColumns;
int tileHeight = originalImage.size.height / numberOfRows;

// create our return array
TwoDimensionalArray * toReturn = [[TwoDimensionalArray alloc] initWithBounds : numberOfRows 
                    : numberOfColumns];

// get a CGI image version of our image
CGImageRef cgVersionOfOriginal = [originalImage CGImage];

// loop to chop up each row
for(int row = 0; row < numberOfRows ; row++){
 // loop to chop up each individual piece by column
 for (int column = 0; column < numberOfColumns; column++)
 {
  CGImageRef tempImage = 
    CGImageCreateWithImageInRect(cgVersionOfOriginal, 
            CGRectMake(column * tileWidth, 
              row * tileHeight, 
              tileWidth, 
              tileHeight));
  [toReturn setObjectAt : row : column : [UIImage imageWithCGImage:tempImage]];
 }
}

// now return the set of images we created
return [toReturn autorelease];
}

// this method resizes an image to the requested dimentions
// be a bit careful when using this method, since the resize will not respect
// the proportions of the image
+ (UIImage *) resize : (UIImage *) originalImage : (int) newWidth : (int) newHeight
{   
// translate the image to the new size
CGSize newSize = CGSizeMake(newWidth, newHeight); // the new size we want the image to be
UIGraphicsBeginImageContext(newSize); // downside: this can't go on a background thread, I'm told
[originalImage drawInRect : CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); // get our new image
UIGraphicsEndImageContext();

// return our brand new image
return newImage;
}

Eva Schiffer

Eva Schiffer