8

InstagramのようにiPhoneで画像フィルター機能を試してみたいです。imagePickerControllerを使用して、カメラロールから写真を取得します。imagePickerControllerによる画像の戻りが、メモリを節約するために削減されたことを理解しています。また、元の画像をUIImageに読み込むのは賢明ではありません。しかし、どうすれば画像を処理して元のピクセルとして保存できますか?開発デバイスとしてiPhone4Sを使用しています。

カメラロールの元の写真は3264*2448です。

UIImagePickerControllerOriginalImageによって返される画像は1920*1440です。

UIImagePickerControllerEditedImageによって返される画像は640*640です。

imageViewOld(UIImagePickerControllerCropRect [80,216,1280,1280]を使用してUIImagePickerControllerOriginalImageによって返される画像をトリミングする)は1280*1224です。

imageViewNew(ダブルサイズのUIImagePickerControllerCropRect [80,216,2560,2560]を使用して、UIImagePickerControllerOriginalImageによって返される画像をトリミングします)は1840*1224です。

同じ写真をインスタグラムで確認すると1280*1280です

私の質問は次のとおりです。

  1. UIImagePickerControllerOriginalImageが「元の」写真を返さないのはなぜですか?なぜ1920*1440に減らしたのですか?
  2. UIImagePickerControllerEditedImageが1280*1280までに画像を返さないのはなぜですか?UIImagePickerControllerCropRectが示すように、
    1280 * 1280平方でカットされていますか?

  3. 元の写真を2448*2448の画像に正方形にカットするにはどうすればよいですか?

前もって感謝します。以下は私のコードです:

  - (void)imagePickerController:(UIImagePickerController *)picker  didFinishPickingMediaWithInfo:(NSDictionary *)info
  {

   NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
   if ([mediaType isEqualToString:@"public.image"])
   {

    UIImage *imageEdited = [info objectForKey:UIImagePickerControllerEditedImage];
    UIImage *imagePicked = [info objectForKey:UIImagePickerControllerOriginalImage];

    CGRect cropRect;
    cropRect = [[info valueForKey:@"UIImagePickerControllerCropRect"] CGRectValue];

    NSLog(@"Original width = %f height= %f ",imagePicked.size.width, imagePicked.size.height);
    //Original width = 1440.000000 height= 1920.000000

    NSLog(@"imageEdited width = %f height = %f",imageEdited.size.width, imageEdited.size.height);
    //imageEdited width = 640.000000 height = 640.000000

    NSLog(@"corpRect %f %f %f %f", cropRect.origin.x, cropRect.origin.y , cropRect.size.width, cropRect.size.height);
    //corpRect 80.000000 216.000000 1280.000000 1280.000000

    CGRect rectNew = CGRectMake(cropRect.origin.x, cropRect.origin.y , cropRect.size.width*2, cropRect.size.height*2);

    CGRect rectOld = CGRectMake(cropRect.origin.x, cropRect.origin.y , cropRect.size.width, cropRect.size.height);

    CGImageRef imageRefNew = CGImageCreateWithImageInRect([imagePicked CGImage], rectNew);
    CGImageRef imageRefOld = CGImageCreateWithImageInRect([imagePicked CGImage], rectOld);

    UIImageView *imageViewNew = [[UIImageView alloc] initWithImage:[UIImage imageWithCGImage:imageRefNew]];
    CGImageRelease(imageRefNew);

    UIImageView *imageViewOld = [[UIImageView alloc] initWithImage:[UIImage imageWithCGImage:imageRefOld]];
    CGImageRelease(imageRefOld);


    NSLog(@"imageViewNew width = %f height = %f",imageViewNew.image.size.width, imageViewNew.image.size.height);
    //imageViewNew width = 1840.000000 height = 1224.000000

    NSLog(@"imageViewOld width = %f height = %f",imageViewOld.image.size.width, imageViewOld.image.size.height);
    //imageViewOld width = 1280.000000 height = 1224.000000

     UIImageWriteToSavedPhotosAlbum(imageEdited, nil, nil, NULL);

     UIImageWriteToSavedPhotosAlbum([imageViewNew.image imageRotatedByDegrees:90.0], nil, nil, NULL);
     UIImageWriteToSavedPhotosAlbum([imageViewOld.image imageRotatedByDegrees:90.0], nil, nil, NULL);


    //assign the image to an UIImage Control
    self.imageV.contentMode = UIViewContentModeScaleAspectFit;
    self.imageV.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.width);
    self.imageV.image = imageEdited;


  }

  [self dismissModalViewControllerAnimated:YES];

 }
4

1 に答える 1

12

あなたが観察したように、UIImagePickerControllerは、時には640x640、時には320x320(デバイスに依存)の縮小された編集済み画像を返します。

あなたの質問:

元の写真を2448*2448の画像に正方形にカットするにはどうすればよいですか?

これを行うには、最初にを使用して、情報辞書UIImagePickerControllerCropRectのキーを使用して取得した元の画像から新しい画像を作成する必要があります。UIImagePickerControllerOriginalImageQuartz Coreメソッドを使用するとCGImageCreateWithImageInRect、渡された四角形で囲まれたピクセルのみを含む新しい画像を作成できます。この場合、作物は直立します。これが正しく機能するためには、向きを考慮する必要があります。次に、画像を希望のサイズに拡大縮小するだけです。クロップレクトは、カメラやフォトライブラリから出てくるのではなく、正しい方向に向けられた後の元の画像に対して相対的であることに注意することが重要です。これが、Quartzメソッドを使用して新しい画像などを作成するときに、方向に一致するようにクロップレクを変換する必要がある理由です。

上記のコードを使用して、クロップレクに基づいて元の画像から1280x1280の画像を作成するように設定しました。ここにはまだいくつかのエッジケースがあります。つまり、クロップレクが負の値になる場合があることを考慮に入れると(コードは正方形のクロップレクを想定しています)、対処されていません。

  1. まず、入力画像の向きとサイズを考慮して、クロップレクを変換します。このtransformCGRectForUIImageOrientation関数はNiftyBeanからのものです
  2. 変換されたトリミング長方形にトリミングされる画像を作成します。
  3. 画像を希望のサイズに拡大縮小(および回転)します。つまり、1280x1280です。
  4. 正しい縮尺と向きでCGImageからUIImageを作成します。

変更を加えたコードは次のとおりです。UPDATE欠落しているケースを処理するために、この下に新しいコードが追加されました。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:@"public.image"])
{

    UIImage *imageEdited = [info objectForKey:UIImagePickerControllerEditedImage];
    UIImage *imagePicked = [info objectForKey:UIImagePickerControllerOriginalImage];

    CGRect cropRect;
    cropRect = [[info valueForKey:@"UIImagePickerControllerCropRect"] CGRectValue];

    NSLog(@"Original width = %f height= %f ",imagePicked.size.width, imagePicked.size.height);
    //Original width = 1440.000000 height= 1920.000000

    NSLog(@"imageEdited width = %f height = %f",imageEdited.size.width, imageEdited.size.height);
    //imageEdited width = 640.000000 height = 640.000000

    NSLog(@"corpRect %@", NSStringFromCGRect(cropRect));
    //corpRect 80.000000 216.000000 1280.000000 1280.000000

    CGSize finalSize = CGSizeMake(1280,1280); 
    CGImageRef imagePickedRef = imagePicked.CGImage;

    CGRect transformedRect = transformCGRectForUIImageOrientation(cropRect, imagePicked.imageOrientation, imagePicked.size);
    CGImageRef cropRectImage = CGImageCreateWithImageInRect(imagePickedRef, transformedRect);
    CGColorSpaceRef colorspace = CGImageGetColorSpace(imagePickedRef);
    CGContextRef context = CGBitmapContextCreate(NULL, 
                                                 finalSize.width, 
                                                 finalSize.height,
                                                 CGImageGetBitsPerComponent(imagePickedRef),
                                                 CGImageGetBytesPerRow(imagePickedRef),
                                                 colorspace,
                                                 CGImageGetAlphaInfo(imagePickedRef));
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh); //Give the context a hint that we want high quality during the scale
    CGContextDrawImage(context, CGRectMake(0, 0, finalSize.width, finalSize.height), cropRectImage);
    CGImageRelease(cropRectImage);

    CGImageRef instaImage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);

    //assign the image to an UIImage Control
    UIImage *image = [UIImage imageWithCGImage:instaImage scale:imagePicked.scale orientation:imagePicked.imageOrientation];
    self.imageView.contentMode = UIViewContentModeScaleAspectFit;
    self.imageView.image = image;
    CGImageRelease(instaImage);

    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}

[self dismissModalViewControllerAnimated:YES];

}

CGRect transformCGRectForUIImageOrientation(CGRect source, UIImageOrientation orientation, CGSize imageSize) {
switch (orientation) {
    case UIImageOrientationLeft: { // EXIF #8
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI_2);
        return CGRectApplyAffineTransform(source, txCompound);
    }
    case UIImageOrientationDown: { // EXIF #3
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI);
        return CGRectApplyAffineTransform(source, txCompound);
    }
    case UIImageOrientationRight: { // EXIF #6
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(0.0, imageSize.width);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI + M_PI_2);
        return CGRectApplyAffineTransform(source, txCompound);
    }
    case UIImageOrientationUp: // EXIF #1 - do nothing
    default: // EXIF 2,4,5,7 - ignore
        return source;
}
}

更新私は残りのケースを処理するいくつかのメソッドを作成しました。手順は基本的に同じですが、いくつかの変更があります。

  1. 最初の変更は、コンテキストを正しく変換およびスケーリングして、着信画像の方向を処理することです。
  2. 2つ目は、から取得できる非正方形の作物をサポートすることですUIImagePickerController。このような場合、正方形の画像は選択した色で塗りつぶされます。

新しいコード

// CropRect is assumed to be in UIImageOrientationUp, as it is delivered this way from the UIImagePickerController when using AllowsImageEditing is on.
// The sourceImage can be in any orientation, the crop will be transformed to match
// The output image bounds define the final size of the image, the image will be scaled to fit,(AspectFit) the bounds, the fill color will be
// used for areas that are not covered by the scaled image. 
-(UIImage *)cropImage:(UIImage *)sourceImage cropRect:(CGRect)cropRect aspectFitBounds:(CGSize)finalImageSize fillColor:(UIColor *)fillColor {

CGImageRef sourceImageRef = sourceImage.CGImage;

//Since the crop rect is in UIImageOrientationUp we need to transform it to match the source image.
CGAffineTransform rectTransform = [self transformSize:sourceImage.size orientation:sourceImage.imageOrientation];
CGRect transformedRect = CGRectApplyAffineTransform(cropRect, rectTransform);

//Now we get just the region of the source image that we are interested in.
CGImageRef cropRectImage = CGImageCreateWithImageInRect(sourceImageRef, transformedRect);

//Figure out which dimension fits within our final size and calculate the aspect correct rect that will fit in our new bounds
CGFloat horizontalRatio = finalImageSize.width / CGImageGetWidth(cropRectImage);
CGFloat verticalRatio = finalImageSize.height / CGImageGetHeight(cropRectImage);
CGFloat ratio = MIN(horizontalRatio, verticalRatio); //Aspect Fit
CGSize aspectFitSize = CGSizeMake(CGImageGetWidth(cropRectImage) * ratio, CGImageGetHeight(cropRectImage) * ratio);


CGContextRef context = CGBitmapContextCreate(NULL,
                                             finalImageSize.width,
                                             finalImageSize.height,
                                             CGImageGetBitsPerComponent(cropRectImage),
                                             0,
                                             CGImageGetColorSpace(cropRectImage),
                                             CGImageGetBitmapInfo(cropRectImage));

if (context == NULL) {
    NSLog(@"NULL CONTEXT!");
}

//Fill with our background color
CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextFillRect(context, CGRectMake(0, 0, finalImageSize.width, finalImageSize.height));

//We need to rotate and transform the context based on the orientation of the source image.
CGAffineTransform contextTransform = [self transformSize:finalImageSize orientation:sourceImage.imageOrientation];
CGContextConcatCTM(context, contextTransform);

//Give the context a hint that we want high quality during the scale
CGContextSetInterpolationQuality(context, kCGInterpolationHigh); 

//Draw our image centered vertically and horizontally in our context.
CGContextDrawImage(context, CGRectMake((finalImageSize.width-aspectFitSize.width)/2, (finalImageSize.height-aspectFitSize.height)/2, aspectFitSize.width, aspectFitSize.height), cropRectImage);

//Start cleaning up..
CGImageRelease(cropRectImage);

CGImageRef finalImageRef = CGBitmapContextCreateImage(context);
UIImage *finalImage = [UIImage imageWithCGImage:finalImageRef];

CGContextRelease(context);
CGImageRelease(finalImageRef);
return finalImage;
}

//Creates a transform that will correctly rotate and translate for the passed orientation.
//Based on code from niftyBean.com
- (CGAffineTransform) transformSize:(CGSize)imageSize orientation:(UIImageOrientation)orientation {

CGAffineTransform transform = CGAffineTransformIdentity;
switch (orientation) {
    case UIImageOrientationLeft: { // EXIF #8
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI_2);
        transform = txCompound;
        break;
    }
    case UIImageOrientationDown: { // EXIF #3
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,M_PI);
        transform = txCompound;
        break;
    }
    case UIImageOrientationRight: { // EXIF #6
        CGAffineTransform txTranslate = CGAffineTransformMakeTranslation(0.0, imageSize.width);
        CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate,-M_PI_2);
        transform = txCompound;
        break;
    }
    case UIImageOrientationUp: // EXIF #1 - do nothing
    default: // EXIF 2,4,5,7 - ignore
        break;
}
return transform;

}
于 2012-06-28T02:40:42.477 に答える