214

画像のサイズを変更して、画像の中心の拡大縮小されたチャンクを取得できるコードをいくつか持っています。これを使用して、画像UIImageの小さな正方形の表現を取得して返します。写真アプリ。UIImageView( を使用してクロップ モードを調整しても同じ結果が得られることはわかっていますが、これらの画像は で表示されることがありますUIWebViews)。

このコードでいくつかのクラッシュに気付き始めたので、少し困惑しています。私には 2 つの異なる説がありますが、どちらが基地内にあるのか疑問に思っています。

理論 1) ターゲット サイズのオフスクリーン イメージ コンテキストに描画することで、クロッピングを実現します。画像の中央部分が必要なので、CGRect渡された引数をdrawInRect画像コンテキストの境界よりも大きいものに設定します。これがコーシャであることを願っていましたが、触れてはいけない他の記憶を引きずり込もうとしているのですか?

理論 2) 私はこれらすべてをバックグラウンド スレッドで実行しています。メインスレッドに制限されている UIKit の部分があることは知っています。私は、オフスクリーンビューへの描画がこれらの1つではないことを想定/望んでいました. 私が間違っている?

(ああ、方法が恋しいNSImage's drawInRect:fromRect:operation:fraction:。)

4

25 に答える 25

237

2014 年 5 月 28 日の更新: 私がこれを書いたのは、iOS 3 かそこらがホットな新しいものだったときでした。これを行うためのより良い方法が、おそらく組み込みであると確信しています。多くの人が言及しているように、この方法は回転を考慮していません。いくつかの追加の回答を読んで、この質問への回答がすべての人にとって役立つように、賛成票を広めてください。

元の応答:

同じ質問への回答をコピーして別の場所に貼り付けます。

これを行う単純なクラス メソッドはありませんが、必要な結果を得るために使用できる関数がありますCGImageCreateWithImageInRect(CGImageRef, CGRect)

これを使用した短い例を次に示します。

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);
于 2009-04-03T04:31:12.997 に答える
91

同じスケールと向きを維持しながら Retina イメージをトリミングするには、UIImage カテゴリで次のメソッドを使用します (iOS 4.0 以降)。

- (UIImage *)crop:(CGRect)rect {
    if (self.scale > 1.0f) {
        rect = CGRectMake(rect.origin.x * self.scale,
                          rect.origin.y * self.scale,
                          rect.size.width * self.scale,
                          rect.size.height * self.scale);
    }

    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}
于 2011-12-09T10:10:16.260 に答える
66

UIImage カテゴリを作成して、必要な場所で使用できます。HitScans の応答とその下のコメントに基づいています。

@implementation UIImage (Crop)

- (UIImage *)crop:(CGRect)rect {

    rect = CGRectMake(rect.origin.x*self.scale, 
                      rect.origin.y*self.scale, 
                      rect.size.width*self.scale, 
                      rect.size.height*self.scale);       

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef 
                                          scale:self.scale 
                                    orientation:self.imageOrientation]; 
    CGImageRelease(imageRef);
    return result;
}

@end

次のように使用できます。

UIImage *imageToCrop = <yourImageToCrop>;
CGRect cropRect = <areaYouWantToCrop>;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];
于 2011-10-09T15:03:54.563 に答える
59

スウィフト 3 バージョン

func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{
    
    let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)!
    let cropped:UIImage = UIImage(cgImage:imageRef)
    return cropped
}


let imageTop:UIImage  = UIImage(named:"one.jpg")! // add validation

ここに画像の説明を入力

このブリッジ機能の助けを借りてCGRectMake-> (この回答CGRectのクレジットは によって回答されました):@rob mayoff

 func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
    return CGRect(x: x, y: y, width: width, height: height)
}

使用法は次のとおりです。

if var image:UIImage  = UIImage(named:"one.jpg"){
   let  croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake(
        image.size.width/4,
        0,
        image.size.width/2,
        image.size.height)
    )
}

出力:

ここに画像の説明を入力

于 2014-09-13T15:05:01.397 に答える
49

これが、imageOrientationプロパティに従うUIImageクロップの実装です。すべての向きが徹底的にテストされました。

inline double rad(double deg)
{
    return deg / 180.0 * M_PI;
}

UIImage* UIImageCrop(UIImage* img, CGRect rect)
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}
于 2013-02-05T16:27:46.740 に答える
36

ここでの回答はどれも、スケールと回転の問題をすべて 100% 正しく処理していません。iOS7/8 の時点で最新の、これまでに述べたすべての内容をまとめたものを次に示します。UIImage のカテゴリにメソッドとして含めることを意図しています。

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}
于 2014-08-13T18:32:52.133 に答える
10
CGSize size = [originalImage size];
int padding = 20;
int pictureSize = 300;
int startCroppingPosition = 100;
if (size.height > size.width) {
    pictureSize = size.width - (2.0 * padding);
    startCroppingPosition = (size.height - pictureSize) / 2.0; 
} else {
    pictureSize = size.height - (2.0 * padding);
    startCroppingPosition = (size.width - pictureSize) / 2.0;
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that 
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation];
[m_photoView setImage:newImage];
CGImageRelease(imageRef);

私が見た応答のほとんどは、(x, y) に対して (0, 0) の位置のみを扱っています。これは 1 つのケースですが、トリミング操作を中央に配置したいと思います。理解するのに時間がかかったのは、WTF コメントに続く行です。

縦向きでキャプチャされた画像の場合を考えてみましょう。

  1. 元の画像の高さが幅よりも高くなっています (これまでのところ驚くことではありません!)
  2. CGImageCreateWithImageInRect メソッドが独自の世界で想像する画像は、実際には縦長ではなく横長です (これが、imageWithCGImage コンストラクターで方向引数を使用しない場合、180 回転したものとして表示される理由でもあります)。
  3. したがって、(0, 0) の位置が画像の右上隅にある風景だと想像する必要があります。

それが理にかなっていることを願っています!そうでない場合は、別の値を試してみてください。cropRect の正しい x、y、幅、および高さを選択する際に、ロジックが逆になっていることがわかります。

于 2011-03-29T03:30:57.130 に答える
7

スウィフト拡張

extension UIImage {
    func crop(var rect: CGRect) -> UIImage {
        rect.origin.x*=self.scale
        rect.origin.y*=self.scale
        rect.size.width*=self.scale
        rect.size.height*=self.scale

        let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
        let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
        return image
    }
}
于 2015-05-22T18:47:57.307 に答える
4

精度、ピクセルスケーリングの観点から、 SwiftでUIImage をトリミングするための最良のソリューション...:

private func squareCropImageToSideLength(let sourceImage: UIImage,
    let sideLength: CGFloat) -> UIImage {
        // input size comes from image
        let inputSize: CGSize = sourceImage.size

        // round up side length to avoid fractional output size
        let sideLength: CGFloat = ceil(sideLength)

        // output size has sideLength for both dimensions
        let outputSize: CGSize = CGSizeMake(sideLength, sideLength)

        // calculate scale so that smaller dimension fits sideLength
        let scale: CGFloat = max(sideLength / inputSize.width,
            sideLength / inputSize.height)

        // scaling the image with this scale results in this output size
        let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale,
            inputSize.height * scale)

        // determine point in center of "canvas"
        let center: CGPoint = CGPointMake(outputSize.width/2.0,
            outputSize.height/2.0)

        // calculate drawing rect relative to output Size
        let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0,
            center.y - scaledInputSize.height/2.0,
            scaledInputSize.width,
            scaledInputSize.height)

        // begin a new bitmap context, scale 0 takes display scale
        UIGraphicsBeginImageContextWithOptions(outputSize, true, 0)

        // optional: set the interpolation quality.
        // For this you need to grab the underlying CGContext
        let ctx: CGContextRef = UIGraphicsGetCurrentContext()
        CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)

        // draw the source image into the calculated rect
        sourceImage.drawInRect(outputRect)

        // create new image from bitmap context
        let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()

        // clean up
        UIGraphicsEndImageContext()

        // pass back new image
        return outImage
}

この関数を呼び出すために使用される命令:

let image: UIImage = UIImage(named: "Image.jpg")!
let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320)
self.myUIImageView.image = squareImage

注: Objective-C で記述された最初のソース コードのインスピレーションは、「Cocoanetics」ブログで見つかりました。

于 2015-03-27T06:07:28.800 に答える
3

以下のコード スニペットが役立つ場合があります。

import UIKit

extension UIImage {
    func cropImage(toRect rect: CGRect) -> UIImage? {
        if let imageRef = self.cgImage?.cropping(to: rect) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }
}
于 2018-10-31T09:07:36.210 に答える
2

少し奇妙に見えますが、うまく機能し、画像の向きを考慮しています。

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)
于 2015-09-25T17:06:52.980 に答える
1

iOS9.2SDK では、以下のメソッドを使用してフレームを UIView から UIImage に変換します

-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
  CGSize cropSize = rect.size;
  CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width;
  CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height;
  cropSize = CGSizeMake(rect.size.width*widthScale, 
              rect.size.height*heightScale);
  CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale,
             rect.origin.y*heightScale);
  rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height);
  CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
  UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
  CGImageRelease(subImage);

  return croppedImage;
}
于 2016-01-29T02:14:54.343 に答える
1
- (UIImage *)getSubImage:(CGRect) rect{
    CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));

    UIGraphicsBeginImageContext(smallBounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, smallBounds, subImageRef);
    UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
    UIGraphicsEndImageContext();

    return smallImg;
}
于 2012-04-17T05:41:56.813 に答える
1
 (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    double ratio;
    double delta;
    CGPoint offset;

    //make a new square size, that is the resized imaged width
    CGSize sz = CGSizeMake(newSize.width, newSize.width);

    //figure out if the picture is landscape or portrait, then
    //calculate scale factor and offset
    if (image.size.width > image.size.height) {
        ratio = newSize.width / image.size.width;
        delta = (ratio*image.size.width - ratio*image.size.height);
        offset = CGPointMake(delta/2, 0);
    } else {
        ratio = newSize.width / image.size.height;
        delta = (ratio*image.size.height - ratio*image.size.width);
        offset = CGPointMake(0, delta/2);
    }

    //make the final clipping rect based on the calculated values
    CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                                 (ratio * image.size.width) + delta,
                                 (ratio * image.size.height) + delta);


    //start a new context, with scale factor 0.0 so retina displays get
    //high quality image
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
    } else {
        UIGraphicsBeginImageContext(sz);
    }
    UIRectClip(clipRect);
    [image drawInRect:clipRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}
于 2015-01-22T09:28:22.360 に答える
1

これは、 Noodlesの回答に基づいた更新されたSwift 3バージョンです

func cropping(to rect: CGRect) -> UIImage? {

    if let cgCrop = cgImage?.cropping(to: rect) {
        return UIImage(cgImage: cgCrop)
    }
    else if let ciCrop = ciImage?.cropping(to: rect) {
        return UIImage(ciImage: ciCrop)
    }

    return nil
}
于 2017-04-24T03:36:23.943 に答える
0

私は以下の方法を使用します。

  -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
  {
    CGSize cropSize = rect.size;
    CGFloat widthScale =  
    image.size.width/self.imageViewOriginal.bounds.size.width;
    CGFloat heightScale = 
    image.size.height/self.imageViewOriginal.bounds.size.height;
    cropSize = CGSizeMake(rect.size.width*widthScale,  
    rect.size.height*heightScale);
    CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
    rect.origin.y*heightScale);
    rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
    cropSize.height);
    CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
    UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
    CGImageRelease(subImage);
    return croppedImage;

}

于 2016-01-29T13:01:10.813 に答える
0

https://github.com/vvbogdan/BVCropPhotoを見てください。

- (UIImage *)croppedImage {
    CGFloat スケール = self.sourceImage.size.width / self.scrollView.contentSize.width;

    UIImage *finalImage = nil;
    CGRect targetFrame = CGRectMake((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * スケール、
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * スケール、
            self.cropSize.width * スケール、
            self.cropSize.height * スケール);

    CGImageRef contextImage = CGImageCreateWithImageInRect([[self imageWithRotation:self.sourceImage] CGImage], targetFrame);

    if (contextImage != NULL) {
        finalImage = [UIImage imageWithCGImage:contextImage
                                         スケール:self.sourceImage.scale
                                   向き:UIImageOrientationUp];

        CGImageRelease(contextImage);
    }

    最終画像を返します。
}


- (UIImage *)imageWithRotation:(UIImage *)画像 {


    if (image.imageOrientation == UIImageOrientationUp) 画像を返します。
    CGAffineTransform 変換 = CGAffineTransformIdentity;

    スイッチ (image.imageOrientation) {
        ケース UIImageOrientationDown:
        ケース UIImageOrientationDownMirrored:
            変換 = CGAffineTransformTranslate(変換、image.size.width、image.size.height);
            変換 = CGAffineTransformRotate(変換、M_PI);
            壊す;

        ケース UIImageOrientationLeft:
        ケース UIImageOrientationLeftMirrored:
            変換 = CGAffineTransformTranslate(変換、image.size.width、0);
            変換 = CGAffineTransformRotate(変換、M_PI_2);
            壊す;

        ケース UIImageOrientationRight:
        ケース UIImageOrientationRightMirrored:
            変換 = CGAffineTransformTranslate(変換、0、image.size.height);
            変換 = CGAffineTransformRotate(変換、-M_PI_2);
            壊す;
        ケース UIImageOrientationUp:
        ケース UIImageOrientationUpMirrored:
            壊す;
    }

    スイッチ (image.imageOrientation) {
        ケース UIImageOrientationUpMirrored:
        ケース UIImageOrientationDownMirrored:
            変換 = CGAffineTransformTranslate(変換、image.size.width、0);
            変換 = CGAffineTransformScale(変換、-1、1);
            壊す;

        ケース UIImageOrientationLeftMirrored:
        ケース UIImageOrientationRightMirrored:
            変換 = CGAffineTransformTranslate(変換、image.size.height、0);
            変換 = CGAffineTransformScale(変換、-1、1);
            壊す;
        ケース UIImageOrientationUp:
        ケース UIImageOrientationDown:
        ケース UIImageOrientationLeft:
        ケース UIImageOrientationRight:
            壊す;
    }

    // ここで、基礎となる CGImage を新しいコンテキストに描画し、変換を適用します
    // 上記で計算。
    CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent(image.CGImage), 0,
            CGImageGetColorSpace(image.CGImage)、
            CGImageGetBitmapInfo(image.CGImage));
    CGContextConcatCTM(ctx, 変換);
    スイッチ (image.imageOrientation) {
        ケース UIImageOrientationLeft:
        ケース UIImageOrientationLeftMirrored:
        ケース UIImageOrientationRight:
        ケース UIImageOrientationRightMirrored:
            // うーん...
            CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.height, image.size.width), image.CGImage);
            壊す;

        デフォルト:
            CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
            壊す;
    }

    // 次に、描画コンテキストから新しい UIImage を作成します
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    img を返します。

}
于 2016-10-28T07:33:59.367 に答える
0

他のソリューションには満足できませんでした。なぜなら、それらは何度も描画する (必要以上の電力を使用する) か、向きに問題があるからです。これは、UIImage * 画像からスケーリングされた正方形のcroppedImageに使用したものです。

CGFloat minimumSide = fminf(image.size.width, image.size.height);
CGFloat finalSquareSize = 600.;

//create new drawing context for right size
CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize);
CGFloat scalingRatio = 640.0/minimumSide;
UIGraphicsBeginImageContext(rect.size);

//draw
[image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)];

UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();
于 2014-01-10T10:28:47.477 に答える