UIImage: Zmiana rozmiaru, następnie Crop

głosy
184

Byłem walnąć moją twarz w tym jeden dla dosłownie dni teraz i chociaż ciągle czuję, że jestem na skraju objawienia, ja po prostu nie może osiągnąć swój cel.

Myślałam, z wyprzedzeniem w koncepcyjnych etapach mojego projektu, że byłoby to trywialna sprawa aby pobrać obraz z kamery iPhone lub biblioteki, skalować go do określonej wysokości, z zastosowaniem podobnej funkcji w aspekcie Wypełnij opcję UIImageView (w całości w kodzie), a następnie przyciąć wyłączyć wszystko, co nie pasuje w upłynął CGRect.

Pierwsze zdjęcie z aparatu lub biblioteki oryginalnej, była trywialna. Jestem w szoku, jak trudno pozostałe dwa kroki okazały się.

Załączony rysunek przedstawia to, co próbuję osiągnąć. Czy ktoś mógłby być na tyle uprzejmy, żeby trzymać rękę? Każdy przykładowy kod znalazłem do tej pory wydaje się rozbić obraz do góry nogami, wyglądają jak gówno, rysować poza granicami, lub inaczej po prostu nie działać poprawnie.

Utwórz 02/03/2009 o 21:42
źródło użytkownik
W innych językach...                            


16 odpowiedzi

głosy
244

Musiałem to samo - w moim przypadku, aby wybrać wymiar, który pasuje raz przeskalowany, a następnie wyciąć każdy koniec dopasować resztę szerokości. (. Pracuję w krajobrazie, więc może nie zauważyłem żadnych uchybień w trybie portretowym) Oto mój kod - jest częścią categeory na UIImage. wielkość docelowa w moim kodu jest zawsze ustawiona na pełnym ekranie urządzenia.

@implementation UIImage (Extras)

#pragma mark -
#pragma mark Scale and crop image

- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
{
    UIImage *sourceImage = self;
    UIImage *newImage = nil;    
    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    CGFloat targetWidth = targetSize.width;
    CGFloat targetHeight = targetSize.height;
    CGFloat scaleFactor = 0.0;
    CGFloat scaledWidth = targetWidth;
    CGFloat scaledHeight = targetHeight;
    CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

    if (CGSizeEqualToSize(imageSize, targetSize) == NO) 
    {
        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;

        if (widthFactor > heightFactor) 
        {
            scaleFactor = widthFactor; // scale to fit height
        }
        else
        {
            scaleFactor = heightFactor; // scale to fit width
        }

        scaledWidth  = width * scaleFactor;
        scaledHeight = height * scaleFactor;

        // center the image
        if (widthFactor > heightFactor)
        {
            thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; 
        }
        else
        {
            if (widthFactor < heightFactor)
            {
                thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
            }
        }
    }   

    UIGraphicsBeginImageContext(targetSize); // this will crop

    CGRect thumbnailRect = CGRectZero;
    thumbnailRect.origin = thumbnailPoint;
    thumbnailRect.size.width  = scaledWidth;
    thumbnailRect.size.height = scaledHeight;

    [sourceImage drawInRect:thumbnailRect];

    newImage = UIGraphicsGetImageFromCurrentImageContext();

    if(newImage == nil)
    {
        NSLog(@"could not scale image");
    }

    //pop the context to get back to the default
    UIGraphicsEndImageContext();

    return newImage;
}
Odpowiedział 03/03/2009 o 07:55
źródło użytkownik

głosy
77

Starszy post zawiera kod dla metody do zmiany rozmiaru UIImage. Odpowiedni fragment brzmi następująco:

+ (UIImage*)imageWithImage:(UIImage*)image 
               scaledToSize:(CGSize)newSize;
{
   UIGraphicsBeginImageContext( newSize );
   [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
   UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();

   return newImage;
}

Jeśli chodzi o kadrowanie idzie, wierzę, że jeśli zmieni sposób korzystania z innego rozmiaru dla skalowania niż dla kontekstu, Twój obraz wynikowy powinien być przycięty do granic kontekstu.

Odpowiedział 02/03/2009 o 23:32
źródło użytkownik

głosy
18
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize {
    //If scaleFactor is not touched, no scaling will occur      
    CGFloat scaleFactor = 1.0;

    //Deciding which factor to use to scale the image (factor = targetSize / imageSize)
    if (image.size.width > targetSize.width || image.size.height > targetSize.height)
        if (!((scaleFactor = (targetSize.width / image.size.width)) > (targetSize.height / image.size.height))) //scale to fit width, or
            scaleFactor = targetSize.height / image.size.height; // scale to fit heigth.

    UIGraphicsBeginImageContext(targetSize); 

    //Creating the rect where the scaled image is drawn in
    CGRect rect = CGRectMake((targetSize.width - image.size.width * scaleFactor) / 2,
                             (targetSize.height -  image.size.height * scaleFactor) / 2,
                             image.size.width * scaleFactor, image.size.height * scaleFactor);

    //Draw the image into the rect
    [image drawInRect:rect];

    //Saving the image, ending image context
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return scaledImage;
}

Proponuję ten jeden. Czy ona nie jest piękna? ;)

Odpowiedział 23/11/2010 o 00:09
źródło użytkownik

głosy
15

Jest to świetny kawałek kodu związane z skalowanie zdjęć + kilka innych operacji. I przyszedł ten, gdy próbuje dowiedzieć ou jak zmiana rozmiaru obrazów ... http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

Odpowiedział 18/01/2010 o 15:58
źródło użytkownik

głosy
7

Proszę bardzo. Ten jest idealny ;-)

EDIT: patrz niżej komentarzu - „nie działa z niektórymi obrazami, nie powiedzie się z: CGContextSetInterpolationQuality: error 0x0 nieprawidłowy kontekst”

// Resizes the image according to the given content mode, taking into account the image's orientation
- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode imageToScale:(UIImage*)imageToScale bounds:(CGSize)bounds interpolationQuality:(CGInterpolationQuality)quality {
    //Get the size we want to scale it to
    CGFloat horizontalRatio = bounds.width / imageToScale.size.width;
    CGFloat verticalRatio = bounds.height / imageToScale.size.height;
    CGFloat ratio;

    switch (contentMode) {
        case UIViewContentModeScaleAspectFill:
            ratio = MAX(horizontalRatio, verticalRatio);
            break;

        case UIViewContentModeScaleAspectFit:
            ratio = MIN(horizontalRatio, verticalRatio);
            break;

        default:
            [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode];
    }

    //...and here it is
    CGSize newSize = CGSizeMake(imageToScale.size.width * ratio, imageToScale.size.height * ratio);


    //start scaling it
    CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
    CGImageRef imageRef = imageToScale.CGImage;
    CGContextRef bitmap = CGBitmapContextCreate(NULL,
                                                newRect.size.width,
                                                newRect.size.height,
                                                CGImageGetBitsPerComponent(imageRef),
                                                0,
                                                CGImageGetColorSpace(imageRef),
                                                CGImageGetBitmapInfo(imageRef));

    CGContextSetInterpolationQuality(bitmap, quality);

    // Draw into the context; this scales the image
    CGContextDrawImage(bitmap, newRect, imageRef);

    // Get the resized image from the context and a UIImage
    CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

    // Clean up
    CGContextRelease(bitmap);
    CGImageRelease(newImageRef);

    return newImage;
}
Odpowiedział 28/06/2011 o 19:40
źródło użytkownik

głosy
6

To jest wersja odpowiedzi Jane sprzedaży w Swift. Twoje zdrowie!

public func resizeImage(image: UIImage, size: CGSize) -> UIImage? {
    var returnImage: UIImage?

    var scaleFactor: CGFloat = 1.0
    var scaledWidth = size.width
    var scaledHeight = size.height
    var thumbnailPoint = CGPointMake(0, 0)

    if !CGSizeEqualToSize(image.size, size) {
        let widthFactor = size.width / image.size.width
        let heightFactor = size.height / image.size.height

        if widthFactor > heightFactor {
            scaleFactor = widthFactor
        } else {
            scaleFactor = heightFactor
        }

        scaledWidth = image.size.width * scaleFactor
        scaledHeight = image.size.height * scaleFactor

        if widthFactor > heightFactor {
            thumbnailPoint.y = (size.height - scaledHeight) * 0.5
        } else if widthFactor < heightFactor {
            thumbnailPoint.x = (size.width - scaledWidth) * 0.5
        }
    }

    UIGraphicsBeginImageContextWithOptions(size, true, 0)

    var thumbnailRect = CGRectZero
    thumbnailRect.origin = thumbnailPoint
    thumbnailRect.size.width = scaledWidth
    thumbnailRect.size.height = scaledHeight

    image.drawInRect(thumbnailRect)
    returnImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return returnImage
}
Odpowiedział 24/12/2015 o 13:15
źródło użytkownik

głosy
2

I konwertowane przewodnik Sam Wirch do Swift i to działa dobrze dla mnie, chociaż istnieją pewne bardzo niewielkie „squishing” w ostateczny obraz, że nie mogę rozwiązać.

func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage {
    var ratio: CGFloat = 0
    var delta: CGFloat = 0
    var offset = CGPointZero
    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)
    }
    let clipRect = CGRectMake(-offset.x, -offset.y, (ratio * image.size.width) + delta, (ratio * image.size.height) + delta)
    UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
    UIRectClip(clipRect)
    image.drawInRect(clipRect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

Jeśli ktoś chce konkretna wersja C, to na jego stronie internetowej.

Odpowiedział 16/08/2015 o 22:00
źródło użytkownik

głosy
2

Wersja Xamarin.iOS dla zaakceptowanej odpowiedzi na temat zmiany rozmiaru, a następnie wyciąć UIImage (aspekt wypełnienia) wynosi poniżej

    public static UIImage ScaleAndCropImage(UIImage sourceImage, SizeF targetSize)
    {
        var imageSize = sourceImage.Size;
        UIImage newImage = null;
        var width = imageSize.Width;
        var height = imageSize.Height;
        var targetWidth = targetSize.Width;
        var targetHeight = targetSize.Height;
        var scaleFactor = 0.0f;
        var scaledWidth = targetWidth;
        var scaledHeight = targetHeight;
        var thumbnailPoint = PointF.Empty;
        if (imageSize != targetSize)
        {
            var widthFactor = targetWidth / width;
            var heightFactor = targetHeight / height;
            if (widthFactor > heightFactor)
            {
                scaleFactor = widthFactor;// scale to fit height
            }
            else
            {
                scaleFactor = heightFactor;// scale to fit width
            }
            scaledWidth = width * scaleFactor;
            scaledHeight = height * scaleFactor;
            // center the image
            if (widthFactor > heightFactor)
            {
                thumbnailPoint.Y = (targetHeight - scaledHeight) * 0.5f;
            }
            else
            {
                if (widthFactor < heightFactor)
                {
                    thumbnailPoint.X = (targetWidth - scaledWidth) * 0.5f;
                }
            }
        }
        UIGraphics.BeginImageContextWithOptions(targetSize, false, 0.0f);
        var thumbnailRect = new RectangleF(thumbnailPoint, new SizeF(scaledWidth, scaledHeight));
        sourceImage.Draw(thumbnailRect);
        newImage = UIGraphics.GetImageFromCurrentImageContext();
        if (newImage == null)
        {
            Console.WriteLine("could not scale image");
        }
        //pop the context to get back to the default
        UIGraphics.EndImageContext();

        return newImage;
    }
Odpowiedział 09/09/2014 o 08:29
źródło użytkownik

głosy
2

I zmodyfikowany kod Brad Larson. To aspekt wypełnić obraz w danym rect.

-(UIImage*) scaleAndCropToSize:(CGSize)newSize;
{
    float ratio = self.size.width / self.size.height;

    UIGraphicsBeginImageContext(newSize);

    if (ratio > 1) {
        CGFloat newWidth = ratio * newSize.width;
        CGFloat newHeight = newSize.height;
        CGFloat leftMargin = (newWidth - newHeight) / 2;
        [self drawInRect:CGRectMake(-leftMargin, 0, newWidth, newHeight)];
    }
    else {
        CGFloat newWidth = newSize.width;
        CGFloat newHeight = newSize.height / ratio;
        CGFloat topMargin = (newHeight - newWidth) / 2;
        [self drawInRect:CGRectMake(0, -topMargin, newSize.width, newSize.height/ratio)];
    }

    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}
Odpowiedział 09/01/2012 o 21:02
źródło użytkownik

głosy
1

Okazało się, że Swift 3 wysłane przez Evgenii Kanvets nie jednolicie skalowanie obrazu.

Oto mój Swift 4 wersja funkcji, które nie squish obraz:

static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {

    // This function returns a newImage, based on image
    // - image is scaled uniformaly to fit into a rect of size newSize
    // - if the newSize rect is of a different aspect ratio from the source image
    //     the new image is cropped to be in the center of the source image
    //     (the excess source image is removed)

    var ratio: CGFloat = 0
    var delta: CGFloat = 0
    var drawRect = CGRect()

    if newSize.width > newSize.height {

        ratio = newSize.width / image.size.width
        delta = (ratio * image.size.height) - newSize.height
        drawRect = CGRect(x: 0, y: -delta / 2, width: newSize.width, height: newSize.height + delta)

    } else {

        ratio = newSize.height / image.size.height
        delta = (ratio * image.size.width) - newSize.width
        drawRect = CGRect(x: -delta / 2, y: 0, width: newSize.width + delta, height: newSize.height)

    }

    UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
    image.draw(in: drawRect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
} 
Odpowiedział 19/06/2018 o 00:58
źródło użytkownik

głosy
1
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,ScreenWidth,ScreenHeigth)];
    [scrollView setBackgroundColor:[UIColor blackColor]];
    [scrollView setDelegate:self];
    [scrollView setShowsHorizontalScrollIndicator:NO];
    [scrollView setShowsVerticalScrollIndicator:NO];
    [scrollView setMaximumZoomScale:2.0];
    image=[image scaleToSize:CGSizeMake(ScreenWidth, ScreenHeigth)];
    imageView = [[UIImageView alloc] initWithImage:image];
    UIImageView* imageViewBk = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
    [self.view addSubview:imageViewBk];
    CGRect rect;
    rect.origin.x=0;
    rect.origin.y=0;
    rect.size.width = image.size.width;
    rect.size.height = image.size.height;

    [imageView setFrame:rect];

    [scrollView setContentSize:[imageView frame].size];
    [scrollView setMinimumZoomScale:[scrollView frame].size.width / [imageView frame].size.width];
    [scrollView setZoomScale:[scrollView minimumZoomScale]];
    [scrollView addSubview:imageView];

    [[self view] addSubview:scrollView];

Następnie można wykonywać zrzuty ekranu na obrazie przez to

float zoomScale = 1.0 / [scrollView zoomScale];
CGRect rect;
rect.origin.x = [scrollView contentOffset].x * zoomScale;
rect.origin.y = [scrollView contentOffset].y * zoomScale;
rect.size.width = [scrollView bounds].size.width * zoomScale;
rect.size.height = [scrollView bounds].size.height * zoomScale;

CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect);

UIImage *cropped = [UIImage imageWithCGImage:cr];

CGImageRelease(cr);
Odpowiedział 23/11/2012 o 10:31
źródło użytkownik

głosy
1

Poniższy prosty kod pracował dla mnie.

[imageView setContentMode:UIViewContentModeScaleAspectFill];
[imageView setClipsToBounds:YES];
Odpowiedział 27/06/2012 o 09:07
źródło użytkownik

głosy
0

Oto Swift 3 wersja przewodnika Sam Wirch do szybkich wysłane przez Williama T.

extension UIImage {

    static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {
        var ratio: CGFloat = 0
        var delta: CGFloat = 0
        var offset = CGPoint.zero

        if image.size.width > image.size.height {
            ratio = newSize.width / image.size.width
            delta = (ratio * image.size.width) - (ratio * image.size.height)
            offset = CGPoint(x: delta / 2, y: 0)
        } else {
            ratio = newSize.width / image.size.height
            delta = (ratio * image.size.height) - (ratio * image.size.width)
            offset = CGPoint(x: 0, y: delta / 2)
        }

        let clipRect = CGRect(x: -offset.x, y: -offset.y, width: (ratio * image.size.width) + delta, height: (ratio * image.size.height) + delta)
        UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
        UIRectClip(clipRect)
        image.draw(in: clipRect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }

}
Odpowiedział 04/09/2017 o 12:38
źródło użytkownik

głosy
0

To pytanie wydaje się, że zostały wprowadzone do odpoczynku, ale w moim dążeniu do rozwiązania, które mogłyby mi łatwiej zrozumieć (i napisany w SWIFT), ja przybyłem na ten (również wysłane do: ? Jak przyciąć UIImage )


I chce móc materiału od obszaru w oparciu o współczynniku kształtu i wagi do wielkości w oparciu o zewnętrzną obwiednią stopniu. Oto moja wariacja:

import AVFoundation
import ImageIO

class Image {

    class func crop(image:UIImage, crop source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage {

        let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source)
        let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(origin: CGPointZero, size: outputExtent))

        let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen
        UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale)

        let scale = max(
            targetRect.size.width / sourceRect.size.width,
            targetRect.size.height / sourceRect.size.height)

        let drawRect = CGRect(origin: -sourceRect.origin * scale, size: image.size * scale)
        image.drawInRect(drawRect)

        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return scaledImage
    }
}

Jest kilka rzeczy, które znalazłem mylące, oddzielne obawy przycinanie i zmiana rozmiaru. Kadrowanie jest obsługiwane z pochodzenia rect, które przechodzą do drawInRect i skalowanie jest obsługiwane przez część wielkości. W moim przypadku musiałem dotyczą rozmiaru przycinania rect na źródła, do mojego wyjścia rect tej samej proporcji. Współczynnik skali jest następnie wejść / wyjść i to musi być zastosowana do drawRect (przekazywane drawInRect).

Jedno zastrzeżenie jest to, że takie podejście skutecznie zakłada, że ​​obraz rysowania jest większy niż w kontekście obrazu. Nie testowałem tego, ale myślę, że można użyć tego kodu do obsługi kadrowanie / powiększanie, ale wyraźne zdefiniowanie parametru skalę być parametr skala ww. Domyślnie UIKit zastosowanie mnożnika w oparciu o rozdzielczości ekranu.

Wreszcie, należy zauważyć, że takie podejście UIKit jest wyższy poziom niż CoreGraphics / Quartz oraz Core podejść obraz, a zdaje sobie sprawy orientacji obrazu. Warto też wspomnieć, że jest to dość szybko, po drugie, aby ImageIO, zgodnie z tym post tutaj: http://nshipster.com/image-resizing/

Odpowiedział 28/05/2015 o 04:58
źródło użytkownik

głosy
0

Swift w wersji:

    static func imageWithImage(image:UIImage, newSize:CGSize) ->UIImage {
    UIGraphicsBeginImageContextWithOptions(newSize, true, UIScreen.mainScreen().scale);
    image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))

    let newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();
    return newImage
}
Odpowiedział 24/02/2015 o 07:32
źródło użytkownik

głosy
0
- (UIImage*)imageScale:(CGFloat)scaleFactor cropForSize:(CGSize)targetSize
{
    targetSize = !targetSize.width?self.size:targetSize;
    UIGraphicsBeginImageContext(targetSize); // this will crop

    CGRect thumbnailRect = CGRectZero;

    thumbnailRect.size.width  = targetSize.width*scaleFactor;
    thumbnailRect.size.height = targetSize.height*scaleFactor;
    CGFloat xOffset = (targetSize.width- thumbnailRect.size.width)/2;
    CGFloat yOffset = (targetSize.height- thumbnailRect.size.height)/2;
    thumbnailRect.origin = CGPointMake(xOffset,yOffset);

    [self drawInRect:thumbnailRect];

    UIImage *newImage  = UIGraphicsGetImageFromCurrentImageContext();

    if(newImage == nil)
    {
        NSLog(@"could not scale image");
    }

    UIGraphicsEndImageContext();

    return newImage;
}

Poniżej na przykładzie pracy: lewy obrazu - (pochodzenia obrazu); Prawy obraz ze skalą x2

wprowadzić opis obrazu tutaj

Jeśli chcesz przeskalować obraz, ale zachowują swoją ramkę (proporcjach), wywołanie metody w ten sposób:

[yourImage imageScale:2.0f cropForSize:CGSizeZero];
Odpowiedział 12/02/2015 o 15:30
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more