UIImage: Změna velikosti, poté Crop

hlasů
184

Byl jsem mlátit můj obličej do této jedné doslova dny teď, a přestože mám pocit neustále, že jsem přímo na okraji zjevení, já prostě nemůže dosáhnout svého cíle.

Myslel jsem si, před časem v koncepčních fázích mého návrhu, že by bylo triviální záležitost chytit obraz z iPhone fotoaparát nebo v knihovně, zmenšenou na specifikované výšky pomocí funkce rovnající se Aspect Fill možnost UIImageView (zcela v kódu), a pak oříznout off nic, co by se nevejdou do prošel CGRect.

Získání původního obrazu z kamery nebo knihovny, byla triviální. Jsem šokován, jak obtížné další dva kroky se ukázaly být.

Přiložený obrázek ukazuje, co se snažím dosáhnout. Mohl by někdo prosím tak laskav a držet za ruku? Každý příklad kódu jsem našel tak daleko vypadá, že rozbít obraz, být vzhůru nohama, vypadají jako hovno, čerpat mimo hřiště, nebo jinak prostě nebude fungovat správně.

Položena 02/03/2009 v 21:42
zdroj uživatelem
V jiných jazycích...                            


16 odpovědí

hlasů
244

Potřeboval jsem to samé - v mém případě, vybrat rozměr, který se hodí jednou měřítkem, a pak oříznout každý konec, aby se vešly zbytek na šířku. (. Já pracuji v krajině, takže nemusí mít všiml nedostatků v režimu na výšku) Zde je můj kód - je to součást categeory na UIImage. Velikost cílového v mém kódu je vždy nastavena na plnou velikost obrazovky zařízení.

@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;
}
Odpovězeno 03/03/2009 v 07:55
zdroj uživatelem

hlasů
77

Starší příspěvek obsahuje kód pro metodu změnit velikost UIImage. Příslušná část je následující:

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

   return newImage;
}

Co se týče oříznutí jde, jsem přesvědčen, že pokud se změní metodu použít jinou velikost pro škálování než pro kontext, by měly být váš výsledný obraz oříznut na hranice kontextu.

Odpovězeno 02/03/2009 v 23:32
zdroj uživatelem

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

Navrhuji tohle. Není to krása? ;)

Odpovězeno 23/11/2010 v 00:09
zdroj uživatelem

hlasů
15

Tam je velký kus kódu ve vztahu k změně velikosti obrázků + několik dalších operací. I obešel tento jeden, když se snaží přijít ou, jak změnit velikost obrázků ... http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

Odpovězeno 18/01/2010 v 15:58
zdroj uživatelem

hlasů
7

Tady máš. Tenhle je perfektní ;-)

EDIT: viz níže poznámka - „To nebude fungovat s určitými obrazy, se nezdaří: CGContextSetInterpolationQuality: neplatný kontext Chyba 0x0“

// 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;
}
Odpovězeno 28/06/2011 v 19:40
zdroj uživatelem

hlasů
6

Jedná se o verzi Jane prodeje odpověď v Swift. Na zdraví!

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
}
Odpovězeno 24/12/2015 v 13:15
zdroj uživatelem

hlasů
2

I převede Sama Wirch příručce pro rychlý a fungovalo to dobře pro mě, i když tam je nějaký velmi mírný „squishing“ ve výsledném obraze, že jsem nemohl vyřešit.

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
}

Pokud se někdo chce Objective C verzi, je to na svých internetových stránkách.

Odpovězeno 16/08/2015 v 22:00
zdroj uživatelem

hlasů
2

Xamarin.iOS verze pro přijatou odpověď o tom, jak změnit velikost a oříznout UIImage (Aspect výplně) je nižší než

    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;
    }
Odpovězeno 09/09/2014 v 08:29
zdroj uživatelem

hlasů
2

Upravil jsem Brad Larsonovo kód. Bude aspekt vyplnit obraz v daném obdélníku.

-(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;
}
Odpovězeno 09/01/2012 v 21:02
zdroj uživatelem

hlasů
1

Zjistil jsem, že Swift 3 odeslal Evgenii Kanvets není jednotně upravit obrázek.

Tady je můj Swift 4 verzi funkce, která nemá přebít obrázku:

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
} 
Odpovězeno 19/06/2018 v 00:58
zdroj uživatelem

hlasů
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];

pak můžete pořizovat snímky na snímek tímto

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);
Odpovězeno 23/11/2012 v 10:31
zdroj uživatelem

hlasů
1

Následující jednoduchý kód pracoval pro mě.

[imageView setContentMode:UIViewContentModeScaleAspectFill];
[imageView setClipsToBounds:YES];
Odpovězeno 27/06/2012 v 09:07
zdroj uživatelem

hlasů
0

Zde je Swift 3 verze Sama Wirch příručky k prudkých odeslal William 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
    }

}
Odpovězeno 04/09/2017 v 12:38
zdroj uživatelem

hlasů
0

Tato otázka se zdá být dán k odpočinku, ale ve svém úsilí o nalezení řešení, které bych mohl snadněji pochopit (a psané v Swift) jsem přišel na to (také vyslán na: Jak oříznout UIImage )


chci být schopen oříznout z oblasti na základě poměru stran, a stupnice pro velikosti založené na vnějším ohraničujícím míře. Tady je moje variace:

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

Existuje pár věcí, které jsem našel matoucí, jednotlivé zájmy oříznutí a změnu velikosti. Oříznutí je nakládáno s původem v obdélníku, který předáte do drawInRect a škálování je prováděn velikost porce. V mém případě jsem potřeboval, aby se týkají velikosti střihových obdélníku o zdroje, podle mého výstupu obdélníku se stejným poměrem stran. Měřítko je pak výstup / vstup, a to je třeba aplikovat na drawRect (předán drawInRect).

Jedna námitka je, že tento přístup skutečně předpokládá, že obraz, který kreslíte, je větší než kontextu obrazu. I nebyly testovány, ale myslím, že můžete použít tento kód pro zpracování ořezávání / zvětšování, ale výslovně definování parametr měřítka, aby parametr výše uvedené stupnice. Ve výchozím nastavení UIKit použije multiplikátor na základě rozlišení obrazovky.

Na závěr je třeba poznamenat, že tento přístup je UIKit vyšší úrovni než CoreGraphics / Quartz a Core Image přístupů, a zdá se, zvládnout problémy orientace obrazu. Stojí také za zmínku, že to je docela rychlý, druhý ImageIO, v souladu s tímto příspěvek zde: http://nshipster.com/image-resizing/

Odpovězeno 28/05/2015 v 04:58
zdroj uživatelem

hlasů
0

Swift verze:

    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
}
Odpovězeno 24/02/2015 v 07:32
zdroj uživatelem

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

Níže uvedený příklad práce: Left image - (původ image); Pravý obraz se stupnicí x2

zadejte popis obrázku zde

Chcete-li měřítko obrazu, ale zachovat jeho rám (proporce), volání metody takto:

[yourImage imageScale:2.0f cropForSize:CGSizeZero];
Odpovězeno 12/02/2015 v 15:30
zdroj uživatelem

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