Przycinanie obrazu UIImage

209

Mam kod, który zmienia rozmiar obrazu, dzięki czemu mogę uzyskać przeskalowaną część środka obrazu - używam tego, aby zrobić UIImagei zwrócić małą, kwadratową reprezentację obrazu, podobną do tego, co widać w widoku albumu aplikacja Zdjęcia. (Wiem, że mogłem użyć a UIImageViewi dostosować tryb przycinania, aby uzyskać te same wyniki, ale te obrazy są czasami wyświetlane w UIWebViews).

Zacząłem zauważać awarie w tym kodzie i jestem trochę zakłopotany. Mam dwie różne teorie i zastanawiam się, czy któraś z nich jest oparta.

Teoria 1) Kadrowanie osiągam, rysując w kontekście obrazu poza ekranem mojego docelowego rozmiaru. Ponieważ chcę środkowej części obrazu, ustawiłem CGRectargument przekazywany drawInRectna coś, co jest większe niż granice mojego kontekstu obrazu. Miałem nadzieję, że to koszerne, ale czy zamiast tego próbuję przywołać inne wspomnienia, których nie powinienem dotykać?

Teoria 2) Robię to wszystko w tle. Wiem, że istnieją części UIKit, które są ograniczone do głównego wątku. Zakładałem / miałem nadzieję, że rysowanie w widoku poza ekranem nie było jednym z nich. Czy się mylę?

(Och, jak tęsknię za NSImage's drawInRect:fromRect:operation:fraction:metodą.)

Jablair
źródło
Jeśli próbujesz zdiagnozować awarię, powinieneś uruchomić aplikację pod debuggerem i zanotować, co się stanie, gdy ulegnie awarii. Nie zidentyfikowałeś nawet, czy zgłaszany jest wyjątek lub otrzymujesz EXC_BAD_ACCESS z powodu wiszącego wskaźnika. Kiedy już to wiesz, możesz zacząć tworzyć teorie.
benzado
Słusznie. Nie zrobiłem tego ponownie w debuggerze, chociaż w dzienniku awarii mam komunikaty EXC_BAD_ACCESS. Kiedy to opublikowałem, pracowałem przy założeniu, że popełniłem głupią pomyłkę w mojej implementacji i ktoś na nią wskoczy (jak zapomnienie ścieżki przycinającej).
Jablair
Niezależnie od tego, co jest warte i dobre odpowiedzi poniżej, na NSHipster znajduje się dobre badanie technik i ich wydajności: nshipster.com/image-resizing . Purysta chciał we mnie użyć CIImage, ale pragmatysta wybrał kontekst UIKit / image.
Chris Conover

Odpowiedzi:

236

Aktualizacja 2014-05-28: Napisałem to, gdy iOS 3 lub coś takiego było nowością, jestem pewien, że istnieją lepsze sposoby, aby to zrobić, być może wbudowane. Jak wspomniało wiele osób, ta metoda nie uwzględnia rotacji; przeczytaj kilka dodatkowych odpowiedzi i rozpowszechnij miłość, aby odpowiedzi na to pytanie były pomocne dla wszystkich.

Oryginalna odpowiedź:

Skopiuję / wkleję swoją odpowiedź na to samo pytanie w innym miejscu:

Nie ma prostej metody klasy, aby to zrobić, ale istnieje funkcja, której można użyć, aby uzyskać pożądane wyniki: CGImageCreateWithImageInRect(CGImageRef, CGRect)pomoże ci.

Oto krótki przykład korzystania z niego:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);
HitScan
źródło
13
Gdy parametr imageOrientationjest ustawiony na cokolwiek innego niż w górę, CGImageobraca się względem UIImageprostokąta przycinania i będzie nieprawidłowy. Możesz odpowiednio obrócić prostokąt przycinania, ale świeżo utworzony UIImagebędzie miał imageOrientationgórę, dzięki czemu przycięte CGImagezostaną obrócone w nim.
zoul
25
Chcę podkreślić, że na wyświetlaczu siatkówki należy podwoić szerokość i wysokość uprawy, używając tej metody. Po prostu coś, na co wpadłem.
petrocket
21
aby działało w dowolnej orientacji, użyj:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis
3
Powinieneś dodać wsparcie dla Retina !!
Mário Carvalho
1
@NicosKaralis, który obróci go tylko we właściwej orientacji. Jednak obszar, który przycinasz z obrazu, nadal będzie nieprawidłowy.
Tim Bodeit,
90

Aby przyciąć obrazy siatkówki przy zachowaniu tej samej skali i orientacji, użyj następującej metody w kategorii UIImage (iOS 4.0 i wyżej):

- (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;
}
Arne
źródło
14
Możesz po prostu zabić warunek tutaj i zawsze wielokrotny self.scale, nie?
Michael Mior,
2
Masz rację, Michael, ale myślę, że powyższe rozwiązanie jest nieco szybsze na urządzeniach innych niż siatkówka, ponieważ wykonuje tylko jedno sprawdzenie zamiast czterech multiplikacji plus przypisanie. Na urządzeniach z siatkówką jest to tylko jeden czek boolowski więcej niż to konieczne, więc jest to kwestia osobistych preferencji lub dopasowania do celu.
CGee
2
Czy korzystanie CGImageRelease(imageRef);z ARC jest prawidłowe ?
CGee
3
@Dschee Tak. Zobacz stackoverflow.com/questions/7800174/…
Klaas
3
W rzeczywistości 4 mnożenia mogą być szybsze niż ocena jednego warunku :) Mnożenie można całkiem dobrze zoptymalizować na nowoczesnych procesorach, podczas gdy warunki można tylko przewidzieć. Ale masz rację, to kwestia preferencji. Wolę krótszy / prostszy kod ze względu na zwiększoną czytelność.
marsbear,
65

Możesz stworzyć kategorię UIImage i używać jej tam, gdzie potrzebujesz. Na podstawie odpowiedzi HitScans i komentarzy poniżej.

@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

Możesz użyć tego w ten sposób:

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

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

UIImage *croppedImage = [imageToCrop crop:cropRect];
Vilém Kurz
źródło
3
Nie powinno [[UIScreen mainScreen] scale]tak być self.scale? Skala obrazu może nie być taka sama jak skala ekranu.
Michael Mior
Cześć, próbowałem twojej odpowiedzi i daje mi to, No visible @interface for 'UIImage' declares the selector 'crop'mimo że umieściłem pliki kategorii .h i .m w moim projekcie i zaimportowałem .h do klasy, której używam kategorii. Dowolny pomysł?
Ali,
Naprawione. Przegapiłem umieszczenie nagłówka metody w pliku UIImage + Crop.h.
Ali,
Chcę przyciąć obraz w okrągłym kształcie. Abyśmy widzieli tylko okrągłą ścieżkę, a inna ścieżka pozostaje przezroczysta.
Alfa,
Niż możesz użyć CGImageCreateWithMask lub CGImageCreateWithMaskingColors zamiast CGImageCreateWithImageInRect
Vilém Kurz
54

Wersja 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

wprowadź opis zdjęcia tutaj

za pomocą tej funkcji mostka CGRectMake-> CGRect(na tę odpowiedź odpowiedział:) @rob mayoff:

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

Wykorzystanie jest:

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

Wynik:

wprowadź opis zdjęcia tutaj

Maxim Shoustin
źródło
Nie jestem pewien, dlaczego zostało to przegłosowane, pomyślałem, że to dobra odpowiedź. Jedyne, co mogę wymyślić, to to, że nie obsługuje skalowania, więc nie będzie działać, jeśli masz obrazy @ 2x / @ 3x, a nazwy funkcji się nie zgadzają.
William T.
4
Musisz przejść guarddo pierwszego wiersza na wypadek UIImage.CGImagepowrotu nili dlaczego używać, vargdy letdziała idealnie.
NoodleOfDeath
Jest to prawidłowe, JEDNAK, cropping(to:)bardzo uważnie czytaj dokumentację . Wynikowy CGImagema silne odniesienie do większego, CGImagez którego został przycięty. Jeśli więc chcesz trzymać się tylko przyciętego obrazu i nie potrzebujesz oryginału, spójrz na inne rozwiązania.
jsadler,
To nie działa z obrazami ze znacznikami orientacji. Obszar kadrowania nie jest odpowiednio obracany.
Max
1
@Max. Dzieje się tak, ponieważ cgImage nie obsługuje orientacji. Jeśli ma to UIImage, rightcgImage powinien zostać obrócony o 90 stopni w kierunku przeciwnym do kierunku jazdy, dlatego też należy obrócić prostokąt przycinania
MK Yung,
49

Oto moja implementacja przycinania UIImage, która jest zgodna z właściwością imageOrientation. Wszystkie orientacje zostały dokładnie przetestowane.

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;
}
Sergii Rudchenko
źródło
7
Ostrzeżenie implicit declaration of function 'rad' is invalid in c99można usunąć, zastępując: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster
UPS przepraszam. Dodano funkcję narzędzia rad () do fragmentu kodu źródłowego.
Sergii Rudchenko
contains undefined reference for architecture armv7Czy brakuje mi biblioteki? CoreGraphics jest importowany.
Bob Spryn
1
@SergiiRudchenko „Wszystkie orientacje zostały dokładnie przetestowane”. - Czy obejmuje to lustrzane orientacje?
Tim Bodeit,
@BobSpryn Nie, nie brakuje Ci biblioteki. Chociaż nie potrafię wyjaśnić, co oznacza błąd, zastąpienie rad (), jak sugerował SoundBlaster, również naprawia ten błąd.
Tim Bodeit,
39

CGImageUwaga : wszystkie te odpowiedzi zakładają obiekt obrazu z cofnięciem.

image.CGImagemoże zwrócić zero, jeśli UIImagejest on wspierany przez a CIImage, co miałoby miejsce, gdybyś utworzył ten obraz za pomocą CIFilter.

W takim przypadku konieczne może być narysowanie obrazu w nowym kontekście i zwrócenie tego obrazu ( powoli ).

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;
}
colinta
źródło
5
Właśnie tego potrzebowałem, działa we wszystkich scenariuszach, dlatego jest lepszy niż inne rozwiązania!
David Thompson,
2
image.size w pierwszym wierszu powinien być rect.size UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Brett
2
Dzięki Brett, zdecydowanie tam. Zaktualizowałem kod, aby uwzględnić tę poprawkę.
colinta
Dlaczego to -rect.origin.x to nie tylko rect.origin.x?
Agresor
2
Ponieważ przycinamy; innymi słowy, „prosty” argument mówi „Chcę, aby lewy górny róg nowego obrazu zaczynał się od, powiedzmy, [10, 10]”. Aby to zrobić, narysujemy obraz, aby [10, 10] znajdował się u źródła nowego obrazu.
colinta
35

Żadna z odpowiedzi tutaj nie obsługuje wszystkich problemów ze skalą i rotacją w 100% poprawnie. Oto synteza wszystkiego, co powiedziane do tej pory, aktualne na iOS7 / 8. Ma być włączony jako metoda do kategorii w 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;
}
Wilk
źródło
1
Dlaczego ta odpowiedź nie jest bardziej doceniana? Wypróbowałem wszystkie powyższe odpowiedzi i napotkałem mnóstwo problemów (w szczególności konwersję z UIImage do CIImage i utratę odpowiednich danych transformacyjnych). TO JEDYNA JEDNA Z TYCH DZIAŁAŃ!
Agresor
1
Dzięki @Aggressor, cieszę się, że to działa dla Ciebie. Moja odpowiedź została opublikowana zaledwie miesiąc temu, podczas gdy wiele z tych odpowiedzi istnieje już od 5 lat. Zgaduję, że to tłumaczy brak porównywalnych głosów.
awolf
Używam tego do przesuwania filtrów między obrazami (jak to robi Snapchat) i dostaję niewielkie problemy ze skalowaniem. Czy w twoim kodzie jest coś, co możesz zasugerować, aby sprawdzić, co może być przyczyną tego błędu skalowania? Moje rozwiązanie, które wykorzystuje Twój kod, opublikowałem tutaj: stackoverflow.com/questions/23319497/...
Aggressor
bardzo przydatna funkcja, dziękuję. jakieś wskazówki, co należy zmodyfikować, aby skupić się na środkowej części obrazu?
prywatny
TO POWINNA BYĆ ODPOWIEDŹ.
podkowa7
17

Szybka wersja awolfodpowiedzi, która zadziałała dla mnie:

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
    }
}
Mark Leonard
źródło
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);

Większość odpowiedzi, które widziałem, dotyczy tylko pozycji (0, 0) dla (x, y). Ok, to jest jeden przypadek, ale chciałbym, aby moja operacja przycinania była wyśrodkowana. To, co zajęło mi trochę czasu, aby zrozumieć, to wiersz następujący po komentarzu WTF.

Weźmy przypadek zdjęcia wykonanego w orientacji pionowej:

  1. Oryginalna wysokość obrazu jest większa niż jego szerokość (Na razie, nie ma niespodzianki!)
  2. Obraz, który wyobraża sobie metoda CGImageCreateWithImageInRect we własnym świecie, nie jest tak naprawdę portretem, ale krajobrazem (dlatego też, jeśli nie użyjesz argumentu orientacji w konstruktorze imageWithCGImage, wyświetli się on jako 180 obrócony).
  3. Powinieneś więc sobie wyobrazić, że jest to krajobraz, a pozycja (0, 0) jest prawym górnym rogiem obrazu.

Mam nadzieję, że to ma sens! Jeśli nie, wypróbuj inne wartości, a zobaczysz, że logika jest odwrócona, jeśli chodzi o wybór właściwego x, y, szerokości i wysokości dla twojego cropRect.

Jordania
źródło
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)
    }
}
neoneye
źródło
7

Szybkie rozszerzenie

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
    }
}
Epicki bajt
źródło
4

Najlepsze rozwiązanie do przycinania obrazu UII w Swift pod względem precyzji, skalowania pikseli ...:

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
}

Instrukcje użyte do wywołania tej funkcji:

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

Uwaga: początkowa inspiracja kodu źródłowego napisana w Objective-C została znaleziona na blogu „Cocoanetics”.

King-Wizard
źródło
3

Poniższy fragment kodu może pomóc.

import UIKit

extension UIImage {
    func cropImage(toRect rect: CGRect) -> UIImage? {
        if let imageRef = self.cgImage?.cropping(to: rect) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }
}
luhuiya
źródło
2

Wygląda trochę dziwnie, ale działa świetnie i uwzględnia orientację obrazu:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)
MuniekMg
źródło
1
ale oczywiście łamie skalę
Sulthan
2

Swift 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
    }
}
huync
źródło
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;
}
Złoty
źródło
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;
}
Bhushan_pawar
źródło
1

W iOS9.2SDK używam poniższej metody do konwersji ramki z UIView na 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;
}
użytkownik5176302
źródło
1

Aktualizacja Swift 2.0 ( CIImagekompatybilność)

Rozszerzanie odpowiedzi Maxima, ale działa również, jeśli Twój obraz jest CIImageoparty.

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
   }
}
NoodleOfDeath
źródło
1

Oto zaktualizowana wersja Swift 3 oparta na odpowiedzi 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
}
voidref
źródło
1

Postępuj zgodnie z odpowiedzią @Arne. Właśnie ustawiam na funkcję kategorii. umieść go w kategorii 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;
}
Linh Nguyen
źródło
0

Nie byłem usatysfakcjonowany innymi rozwiązaniami, ponieważ albo rysują kilka razy (zużywają więcej energii niż to konieczne), albo mają problemy z orientacją. Oto, czego użyłem do skalowanego kwadratu przyciętego obrazu z obrazu 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();
Matthieu Rouif
źródło
0

Korzystam z metody poniżej.

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

}

użytkownik5176302
źródło
0

Spójrz na https://github.com/vvbogdan/BVCropPhoto

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

    UIImage * finalImage = zero;
    CGRect targetFrame = CGRectMake ((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * skala,
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * skala,
            skala self.cropSize.width *,
            skala self.cropSize.height *);

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

    if (contextImage! = NULL) {
        finalImage = [UIImage imageWithCGImage: contextImage
                                         Skala: self.sourceImage.scale
                                   orientacja: UIImageOrientationUp];

        CGImageRelease (contextImage);
    }

    zwróć końcowy obraz;
}


- (UIImage *) imageWithRotation: (UIImage *) image {


    if (image.imageOrientation == UIImageOrientationUp) zwraca obraz;
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (image.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, image.size.height);
            transform = CGAffineTransformRotate (transformacja, M_PI);
            przerwa;

        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformRotate (transformacja, M_PI_2);
            przerwa;

        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, 0, image.size.height);
            transform = CGAffineTransformRotate (transformacja -M_PI_2);
            przerwa;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            przerwa;
    }

    switch (image.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformScale (transformata, -1, 1);
            przerwa;

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.height, 0);
            transform = CGAffineTransformScale (transformata, -1, 1);
            przerwa;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            przerwa;
    }

    // Teraz rysujemy bazowy CGImage w nowym kontekście, stosując transformację
    // obliczone powyżej.
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage),
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, transform);
    switch (image.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr ...
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.height, image.size.width), image.CGImage);
            przerwa;

        domyślna:
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.width, image.size.height), image.CGImage);
            przerwa;
    }

    // A teraz po prostu tworzymy nowego UIImage na podstawie rysunku
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [UIImage imageWithCGImage: cgimg];
    CGContextRelease (ctx);
    CGImageRelease (cgimg);
    zwróć img;

}
Nowytech
źródło