การครอบตัด UIImage


209

ฉันมีรหัสที่ปรับขนาดรูปภาพเพื่อให้ได้สัดส่วนที่เป็นจุดศูนย์กลางของภาพ - ฉันใช้สิ่งนี้เพื่อถ่ายUIImageและคืนค่าการแสดงรูปภาพสี่เหลี่ยมจัตุรัสขนาดเล็กคล้ายกับสิ่งที่เห็นในมุมมองอัลบั้มของ แอพรูปถ่าย (ฉันรู้ว่าฉันสามารถใช้UIImageViewและปรับโหมดการครอบตัดเพื่อให้ได้ผลลัพธ์เดียวกัน แต่บางครั้งภาพเหล่านี้จะปรากฏในUIWebViews)

ฉันเริ่มสังเกตเห็นความผิดพลาดบางอย่างในรหัสนี้และฉันก็นิ่งงันเล็กน้อย ฉันมีสองทฤษฎีที่แตกต่างกันและฉันสงสัยว่าถ้าทั้งสองอยู่บนฐาน

ทฤษฎี 1) ฉันประสบความสำเร็จในการปลูกพืชโดยการวาดลงในบริบทภาพนอกจอที่ขนาดเป้าหมายของฉัน เนื่องจากฉันต้องการส่วนตรงกลางของภาพฉันจึงตั้งค่าCGRectอาร์กิวเมนต์ที่ส่งผ่านไปdrawInRectยังสิ่งที่ใหญ่กว่าขอบเขตของบริบทรูปภาพของฉัน ฉันหวังว่านี่คือ Kosher แต่ฉันพยายามที่จะดึงความทรงจำอื่น ๆ ที่ฉันไม่ควรแตะต้องมาก่อนหรือไม่?

ทฤษฎี 2) ฉันทำทั้งหมดนี้ในกระทู้พื้นหลัง ฉันรู้ว่ามีบางส่วนของ UIKit ที่ จำกัด เฉพาะเธรดหลัก ฉันกำลังสมมติ / หวังว่าการวาดไปยังมุมมองหน้าจอไม่ใช่หนึ่งในนั้น ฉันผิดหรือเปล่า?

(โอ้ฉันคิดถึงNSImage's drawInRect:fromRect:operation:fraction:วิธีการ)


หากคุณกำลังพยายามวิเคราะห์ความผิดพลาดคุณควรใช้แอพภายใต้ตัวดีบั๊กเกอร์และจดบันทึกสิ่งที่เกิดขึ้นเมื่อเกิดข้อขัดข้อง คุณยังไม่ได้ระบุว่ามีข้อผิดพลาดเกิดขึ้นหรือคุณได้รับ EXC_BAD_ACCESS เนื่องจากตัวชี้ที่ห้อยอยู่ เมื่อคุณรู้แล้วคุณสามารถเริ่มสร้างทฤษฎี
benzado

ยุติธรรมพอสมควร ฉันไม่ได้ทำซ้ำสิ่งนี้ภายใต้ตัวดีบักแม้ว่าฉันจะมีข้อความ EXC_BAD_ACCESS ในบันทึกข้อผิดพลาด เมื่อฉันโพสต์สิ่งนี้ฉันทำงานภายใต้สมมติฐานที่ว่าฉันทำผิดพลาดโง่ ๆ ในการนำไปใช้ของฉันและใครบางคนจะกระโดดข้ามมัน (เช่นลืมเส้นทางที่ถูกตัด)
Jablair

สำหรับสิ่งที่คุ้มค่าและคำตอบที่ดีด้านล่างแม้จะมีการสำรวจที่ดีของเทคนิคและผลการดำเนินงานดังกล่าวใน NSHipster: nshipster.com/image-resizing คนเจ้าระเบียบในตัวฉันต้องการใช้ CIImage แต่นักปฏิบัตินิยมเลือกบริบทของ UIKit / ภาพ
Chris Conover

คำตอบ:


236

อัปเดต 2014-05-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);

13
เมื่อimageOrientationมีการตั้งค่าเป็นอะไร แต่ขึ้นCGImageอยู่กับการหมุนด้วยความเคารพUIImageและสี่เหลี่ยมผืนผ้าการปลูกพืชจะผิด คุณสามารถหมุนกรอบการครอบตัดได้ตามต้องการ แต่การสร้างใหม่UIImageจะมีimageOrientationขึ้นเพื่อให้การครอบตัดCGImageถูกหมุนภายใน
โซล

25
ฉันต้องการชี้ให้เห็นว่าบนจอแสดงผลเรตินาคุณต้องเพิ่มความกว้างและความสูง CropRect เป็นสองเท่าโดยใช้วิธีนี้ บางสิ่งที่ฉันเจอ
petrocket

21
เพื่อให้สามารถใช้งานได้ในทุกทิศทาง:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis

3
คุณควรเพิ่มการสนับสนุนสำหรับ Retina !!
Mário Carvalho

1
@NicosKaralis ที่จะหมุนไปในทิศทางที่ถูกต้องเท่านั้น อย่างไรก็ตามพื้นที่ที่คุณกำลังครอบตัดภาพจะยังคงผิด
Tim Bodeit

90

ในการครอบตัดภาพเรตินาในขณะที่ยังคงขนาดและการวางแนวเดียวกันให้ใช้วิธีการต่อไปนี้ในหมวดหมู่ 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;
}

14
คุณสามารถฆ่าเงื่อนไขได้ที่นี่และคูณกันด้วยself.scaleไม่ใช่หรือ?
Michael Mior

2
คุณพูดถูกไมเคิล แต่ฉันคิดว่าวิธีแก้ปัญหาข้างต้นนั้นเร็วขึ้นเล็กน้อยสำหรับอุปกรณ์ที่ไม่ใช่จอประสาทตาเพราะจะทำเพียงแค่ตรวจสอบครั้งเดียวแทนที่จะเป็นสี่คูณรวมถึงการมอบหมาย ในอุปกรณ์เรติน่ามันเป็นเพียงการตรวจสอบบูลีนเดียวเกินความจำเป็นดังนั้นจึงเป็นคำถามของความชอบส่วนตัวหรือเป้าหมายที่เหมาะสม
CGee

2
ใช้CGImageRelease(imageRef);กับ ARC ได้หรือไม่
CGee

3
@Dschee ใช่ ดูstackoverflow.com/questions/7800174/…
Klaas

3
ที่จริงแล้วการคูณ 4 อาจเร็วกว่าการประเมินหนึ่งเงื่อนไข :) การคูณสามารถเพิ่มประสิทธิภาพได้ดีในซีพียูสมัยใหม่ในขณะที่เงื่อนไขสามารถคาดการณ์ได้เท่านั้น แต่คุณพูดถูกมันเป็นเรื่องของความชอบ ฉันชอบรหัสที่สั้นกว่า / ง่ายกว่าเนื่องจากความสามารถในการอ่านเพิ่มขึ้น
marsbear

65

คุณสามารถสร้างหมวดหมู่ 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];

3
ไม่ควร[[UIScreen mainScreen] scale]จะเป็นเพียงแค่self.scale? สเกลของภาพอาจไม่เหมือนกับสเกลของหน้าจอ
Michael Mior

สวัสดีฉันลองตอบคุณแล้วมันก็ช่วยให้ฉันNo visible @interface for 'UIImage' declares the selector 'crop'แม้ว่าฉันจะใส่ไฟล์หมวดหมู่. h และ. m ในโครงการของฉันและนำเข้าไฟล์. h ในชั้นเรียนที่ฉันใช้หมวดหมู่อยู่ ความคิดใด ๆ
อาลี

ซ่อมมัน. ฉันพลาดการใส่หัวข้อวิธีการในไฟล์ UIImage + Crop.h
อาลี

ฉันต้องการที่จะครอบตัดภาพในรูปทรงกลม เพื่อที่เราจะได้เห็นเพียงเส้นทางวงกลมและเส้นทางอื่น ๆ ยังคงโปร่งใส
Alfa

กว่าที่คุณสามารถใช้ CGImageCreateWithMask หรือ CGImageCreateWithMaskingColors แทน CGImageCreateWithImageInRect
Vilém Kurz

54

รุ่น Swift 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)
    )
}

เอาท์พุท:

ป้อนคำอธิบายรูปภาพที่นี่


ไม่แน่ใจว่าทำไมมันถึงลงคะแนนฉันคิดว่ามันเป็นคำตอบที่ดี สิ่งเดียวที่ฉันคิดได้ก็คือมันไม่ได้รองรับการปรับขนาดดังนั้นมันจะไม่ทำงานหากคุณมีภาพ @ 2x / @ 3x และชื่อฟังก์ชั่นของคุณไม่ตรงกัน
William T.

4
คุณต้องการguardบรรทัดแรกในกรณีที่UIImage.CGImageผลตอบแทนnilและทำไมใช้varเมื่อletทำงานอย่างสมบูรณ์
NoodleOfDeath

สิ่งนี้ถูกต้องอย่างไรก็ตามอ่านเอกสารcropping(to:)อย่างระมัดระวังมาก ผลลัพธ์ที่CGImageได้มีการอ้างอิงที่แข็งแกร่งไปยังส่วนที่ใหญ่กว่าCGImageซึ่งมันถูกครอบตัด ดังนั้นหากคุณต้องการที่จะดูเฉพาะภาพที่ถูกครอบตัดและไม่ต้องการต้นฉบับให้ดูที่วิธีแก้ปัญหาอื่น
jsadler

สิ่งนี้ไม่ทำงานกับรูปภาพที่มีแท็กการวางแนว พื้นที่เพาะปลูกไม่หมุนอย่างถูกต้อง
Max

1
@ Max เป็นเพราะ cgImage ไม่มีการรองรับการวางแนว หาก UIImage มีอยู่rightแล้ว cgImage ควรหมุน ccw 90 องศาดังนั้นจึงควรหมุนส่วนครอบตัดด้วยเช่นกัน
MK Yung

49

นี่คือการดำเนินการครอบตัด UII ของฉันซึ่งเป็นไปตามคุณสมบัติ imageOrientation ทุกทิศทางถูกทดสอบอย่างละเอียด

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;
}

7
คำเตือนimplicit declaration of function 'rad' is invalid in c99อาจถูกลบออกโดยแทนที่: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster

อ๊ะขอโทษ เพิ่มฟังก์ชันยูทิลิตี้ rad () ลงในข้อมูลโค้ดต้นทาง
Sergii Rudchenko

contains undefined reference for architecture armv7ฉันไม่มีห้องสมุดหรือไม่ นำเข้า CoreGraphics
Bob Spryn

1
@SergiiRudchenko "การวางแนวทั้งหมดได้รับการทดสอบอย่างถี่ถ้วน" - สิ่งนี้รวมถึงการวางแนวมิเรอร์หรือไม่?
Tim Bodeit

@BobSpryn ไม่คุณไม่ได้หายไปจากห้องสมุด ในขณะที่ฉันไม่สามารถอธิบายความหมายของข้อผิดพลาดได้ แต่การแทนที่ของ rad () เช่นเดียวกับ SoundBlaster ที่แนะนำให้แก้ไขข้อผิดพลาดนี้เช่นกัน
Tim Bodeit

39

มุ่งหน้าไป: คำตอบทั้งหมดนี้ถือว่าเป็น CGImageออบเจ็กต์ภาพที่แบ็กไว้

image.CGImage สามารถส่งคืนศูนย์ถ้า UIImageมีการสนับสนุนโดย a CIImageซึ่งจะเป็นกรณีถ้าคุณสร้างภาพนี้โดยใช้CIFilterซึ่งจะเป็นกรณีที่ถ้าคุณสร้างภาพนี้โดยใช้

ในกรณีนี้คุณอาจต้องวาดภาพในบริบทใหม่และส่งคืนรูปภาพนั้น ( ช้า )

UIImage* crop(UIImage *image, rect) {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]);
    [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

5
ตรงนี้เป็นสิ่งที่ฉันต้องการทำงานในทุกสถานการณ์ดังนั้นดีกว่าโซลูชันอื่น ๆ !
David Thompson

2
image.size ในบรรทัดแรกควรเป็น rect.size UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Brett

2
ขอบคุณเบรทแน่นอนอยู่ที่นั่น ฉันอัปเดตรหัสเพื่อรวมการแก้ไขนี้
colinta

ทำไม -rect.origin.x ไม่ใช่แค่ rect.origin.x?
Aggressor

2
เพราะเรากำลังครอบตัด กล่าวอีกนัยหนึ่ง arg 'rect' กำลังพูดว่า "ฉันต้องการให้มุมบนซ้ายของภาพใหม่เริ่มต้นที่จะพูดว่า [10, 10]" ในการทำเช่นนั้นเราวาดภาพเพื่อให้ [10, 10] เป็นจุดกำเนิดของภาพใหม่
colinta

35

ไม่มีคำตอบในที่นี้ที่จะจัดการกับปัญหาการหมุนและการหมุนได้อย่างถูกต้อง 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;
}

1
เหตุใดคำตอบนี้จึงไม่ถูกเพิ่มขึ้นอีก ฉันลองคำตอบทั้งหมดข้างต้นและพบปัญหามากมาย (โดยเฉพาะการแปลงจาก UIImage เป็น CIImage และสูญเสียข้อมูลการแปลงที่เหมาะสม) นี่เป็นเพียงหนึ่งเดียวที่เหมาะกับฉัน!
ผู้รุกราน

1
ขอบคุณ @Aggressor ดีใจที่ได้ผลกับคุณ คำตอบของฉันเพิ่งโพสต์เมื่อเดือนที่แล้วในขณะที่คำตอบเหล่านี้มากมายอยู่ที่นี่เป็นเวลา 5 ปี ฉันเดาว่าจะอธิบายถึงการขาดคะแนนโหวตที่เปรียบเทียบได้
awolf

ฉันใช้ตัวเลือกนี้เพื่อปัดฟิลเตอร์ข้ามรูปภาพ (เช่นเดียวกับวิธี snapchat) และฉันได้รับปัญหาการปรับสเกลเล็กน้อย มีรหัสอะไรบ้างในคุณที่คุณสามารถแนะนำให้ฉันดูว่าอาจทำให้เกิดข้อผิดพลาดในการปรับขนาดนี้หรือไม่ ฉันโพสต์โซลูชันของฉันซึ่งใช้รหัสของคุณที่นี่: stackoverflow.com/questions/23319497/…
Aggressor

ฟังก์ชั่นที่มีประโยชน์มากขอบคุณ เคล็ดลับใด ๆ ที่ควรแก้ไขเพื่อครอบตัดโฟกัสไปที่ส่วนกลางของรูปภาพ
ส่วนตัว

นี่ควรเป็นคำตอบ
horseshoe7

17

awolfคำตอบของเวอร์ชัน Swift ซึ่งใช้ได้กับฉัน:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}

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);

คำตอบส่วนใหญ่ที่ฉันเห็นมีเพียงข้อตกลงกับตำแหน่ง (0, 0) สำหรับ (x, y) ตกลงนั่นเป็นกรณีหนึ่ง แต่ฉันต้องการให้การครอบตัดเป็นกึ่งกลาง สิ่งที่ฉันใช้เวลาสักครู่เพื่อคิดออกคือบรรทัดตามความคิดเห็น WTF

ลองพิจารณากรณีของภาพที่ถ่ายด้วยการวางแนวตั้ง:

  1. ความสูงของภาพดั้งเดิมสูงกว่าความกว้าง (Woo, ไม่แปลกใจเลย!)
  2. ภาพที่วิธี CGImageCreateWithImageInRect จินตนาการในโลกของตัวเองไม่ใช่ภาพเหมือนจริง แต่เป็นแนวนอน (นั่นคือเหตุผลว่าทำไมคุณไม่ใช้อาร์กิวเมนต์การวางแนวใน imageWithCGImage constructor มันจะปรากฏเป็น 180 หมุน)
  3. ดังนั้นคุณควรจินตนาการว่ามันเป็นแนวนอนตำแหน่ง (0, 0) ที่มุมขวาบนของภาพ

หวังว่ามันจะสมเหตุสมผล! หากไม่ลองใช้ค่าที่ต่างกันคุณจะเห็นว่าตรรกะกลับด้านเมื่อเลือก x, y, ความกว้างและความสูงที่เหมาะสมสำหรับ cropRect ของคุณ


8

swift3

extension UIImage {
    func crop(rect: CGRect) -> UIImage? {
        var scaledRect = rect
        scaledRect.origin.x *= scale
        scaledRect.origin.y *= scale
        scaledRect.size.width *= scale
        scaledRect.size.height *= scale
        guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else {
            return nil
        }
        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}

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
    }
}

4

ทางออกที่ดีที่สุดสำหรับการครอบตัด UIImage ในSwiftในแง่ของความแม่นยำการปรับขนาดพิกเซล ... :

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"


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
    }
}

2

ดูแปลกเล็กน้อย แต่ใช้งานได้ดีและคำนึงถึงการวางแนวภาพ:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)

1
แต่เห็นได้ชัดว่ามันแตกขนาด
Sulthan

2

สวิฟท์ 5:

extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        guard let cgImage = cgImage else { return nil }

        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        let context = UIGraphicsGetCurrentContext()

        context?.translateBy(x: 0.0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.draw(cgImage, in: CGRect(x: rect.minX, y: rect.minY, width: self.size.width, height: self.size.height), byTiling: false)


        let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return croppedImage
    }
}

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;
}

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;
}

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;
}

1

Swift 2.0 Update ( CIImageความเข้ากันได้)

การไม่ตอบคำถามของ Maximแต่จะใช้งานได้หากภาพของคุณเป็นไปCIImageตามนั้นเช่นกัน

public extension UIImage {
    func imageByCroppingToRect(rect: CGRect) -> UIImage? {
        if let image = CGImageCreateWithImageInRect(self.CGImage, rect) {
            return UIImage(CGImage: image)
        } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) {
            return UIImage(CIImage: image)
        }
       return nil
   }
}

1

ต่อไปนี้เป็นเวอร์ชั่น Swift 3 ที่ได้รับการปรับปรุงตามคำตอบของNoodles

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
}

1

ทำตามคำตอบของ @Arne ฉันเพิ่งแก้ไขไปยังฟังก์ชันหมวดหมู่ วางไว้ในหมวดหมู่ของ UIImage

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

    UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]);
    [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

0

ฉันไม่พอใจกับวิธีแก้ปัญหาอื่น ๆ เพราะพวกเขาวาดหลายครั้ง (ใช้พลังงานมากกว่าที่จำเป็น) หรือมีปัญหาเกี่ยวกับทิศทาง นี่คือสิ่งที่ฉันใช้สำหรับการครอบตัดรูปสี่เหลี่ยมจัตุรัสภาพจากภาพ UIImage *

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();

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;

}


0

ดูที่https://github.com/vvbogdan/BVCropPhoto

- (UIImage *) croppedImage {
    CGFloat scale = self.sourceImage.size.width / self.scrollView.contentSize.width;

    UIImage * finalImage = ไม่มี;
    CGRect targetFrame = CGRectMake ((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * สเกล
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * สเกล
            self.cropSize.width * scale,
            self.cropSize.height * scale);

    CGImageRef contextImage = CGImageCreateWithImageInRect ([[ภาพตัวเองด้วยการหมุน: self.sourceImage] CGImage], เป้าหมายเฟรม);

    if (contextImage! = NULL) {
        finalImage = [UIImage imageWithCGImage: contextImage
                                         ขนาด: self.sourceImage.scale
                                   ปฐมนิเทศ: UIImageOrientationUp];

        CGImageRelease (contextImage);
    }

    คืนสุดท้ายภาพ;
}


- (UIImage *) imageWithRotation: (UIImage *) ภาพ {


    ถ้า (image.imageOrientation == UIImageOrientationUp) ภาพกลับมา;
    CGAffineTransform transform = CGAffineTransformIdentity;

    สวิตช์ (image.imageOrientation) {
        กรณี UIImageOrientationDown:
        เคส UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (เปลี่ยน, image.size.width, image.size.height);
            transform = CGAffineTransformRotate (transform, M_PI);
            หยุดพัก;

        กรณี UIImageOrientation ซ้าย:
        เคส UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate (เปลี่ยน, image.size.width, 0);
            transform = CGAffineTransformRotate (แปลง, M_PI_2);
            หยุดพัก;

        กรณี UIImageOrientationRight:
        เคส UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, 0, image.size.height);
            transform = CGAffineTransformRotate (แปลง, -M_PI_2);
            หยุดพัก;
        กรณี UIImageOrientationUp:
        กรณี UIImageOrientationUpMirrored:
            หยุดพัก;
    }

    สวิตช์ (image.imageOrientation) {
        กรณี UIImageOrientationUpMirrored:
        เคส UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (เปลี่ยน, image.size.width, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            หยุดพัก;

        เคส UIImageOrientationLeftMirrored:
        เคส UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (เปลี่ยน, image.size.height, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            หยุดพัก;
        กรณี UIImageOrientationUp:
        กรณี UIImageOrientationDown:
        กรณี UIImageOrientation ซ้าย:
        กรณี UIImageOrientationRight:
            หยุดพัก;
    }

    // ตอนนี้เราวาด CGImage พื้นฐานในบริบทใหม่โดยใช้การแปลง
    // คำนวณข้างต้น
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage)
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, transform);
    สวิตช์ (image.imageOrientation) {
        กรณี UIImageOrientation ซ้าย:
        เคส UIImageOrientationLeftMirrored:
        กรณี UIImageOrientationRight:
        เคส UIImageOrientationRightMirrored:
            // Grr ...
            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;

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.