web-dev-qa-db-fra.com

Comment recadrer le UIImage?

Je développe une application dans laquelle je traite l'image à l'aide de ses pixels, mais cela prend beaucoup de temps. Par conséquent, je souhaite recadrer UIImage (seule la partie centrale de l’image, c’est-à-dire supprimer/rogner la partie entourée de l’image) .J'ai le code de développement,

- (NSInteger) processImage1: (UIImage*) image
{

 CGFloat width = image.size.width;
 CGFloat height = image.size.height;
 struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel));
 if (pixels != nil)
 {
  // Create a new bitmap
  CGContextRef context = CGBitmapContextCreate(
              (void*) pixels,
              image.size.width,
              image.size.height,
              8,
              image.size.width * 4,
              CGImageGetColorSpace(image.CGImage),
              kCGImageAlphaPremultipliedLast
              );
  if (context != NULL)
  {
   // Draw the image in the bitmap
   CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage);
   NSUInteger numberOfPixels = image.size.width * image.size.height;

   NSMutableArray *numberOfPixelsArray = [[[NSMutableArray alloc] initWithCapacity:numberOfPixelsArray] autorelease];
}

Comment je prends (recadrage extérieur bordé) la partie centrale de UIImage ?????????

38
Rajendra Bhole

Essayez quelque chose comme ça:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
image = [UIImage imageWithCGImage:imageRef]; 
CGImageRelease(imageRef);

Remarque: cropRect est un rectangle plus petit avec la partie centrale de l'image ...

78
mihir mehta

Je cherchais un moyen d'obtenir un recadrage rectangulaire arbitraire (c'est-à-dire une sous-image) d'un UIImage. 

La plupart des solutions que j'ai essayées ne fonctionnent pas si l'orientation de l'image est autre chose que UIImageOrientationUp. 

Par exemple:

http://www.Hive05.com/2008/11/crop-an-image-using-the-iphone-sdk/

Généralement, si vous utilisez votre appareil photo iPhone, vous aurez d'autres orientations, comme UIImageOrientationLeft, et vous n'obtiendrez pas un recadrage correct avec ce qui précède. Cela est dû à l'utilisation de CGImageRef/CGContextDrawImage, qui diffère dans le système de coordonnées en ce qui concerne UIImage. 

Le code ci-dessous utilise des méthodes UI * (pas de CGImageRef) et je l’ai testé avec des images orientées haut/bas/gauche/droite et il semble fonctionner très bien. 


// get sub image
- (UIImage*) getSubImageFrom: (UIImage*) img WithRect: (CGRect) rect {

    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // translated rectangle for drawing sub image 
    CGRect drawRect = CGRectMake(-rect.Origin.x, -rect.Origin.y, img.size.width, img.size.height);

    // clip to the bounds of the image context
    // not strictly necessary as it will get clipped anyway?
    CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));

    // draw image
    [img drawInRect:drawRect];

    // grab image
    UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return subImage;
}
39
M-V

Cela serait finalement plus rapide, avec beaucoup moins de création d’images à partir d’atlas Sprite, si vous pouviez définir non seulement l’image pour un UIImageView, mais également le décalage en haut à gauche à afficher dans ce UIImage. Peut-être que c'est possible Cela éliminerait certainement beaucoup d'efforts!

En attendant, j'ai créé ces fonctions utiles dans une classe d'utilitaires que j'utilise dans mes applications. Il crée un UIImage à partir d'une partie d'un autre UIImage, avec des options permettant de faire pivoter, de redimensionner et d'inverser en utilisant les valeurs UIImageOrientation standard à spécifier. La mise à l'échelle en pixels est conservée par rapport à l'image d'origine.

Mon application crée beaucoup de UIImages lors de l'initialisation, ce qui prend nécessairement du temps. Mais certaines images ne sont pas nécessaires jusqu'à ce qu'un certain onglet est sélectionné. Pour donner l’apparence d’une charge plus rapide, je pouvais les créer dans un thread séparé créé au démarrage, puis attendre que cela soit fait lorsque cet onglet est sélectionné.

Ce code est également disponible sur Méthode la plus efficace pour dessiner une partie d’une image sous iOS

+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture {
    return [ChordCalcController imageByCropping:imageToCrop toRect:aperture withOrientation:UIImageOrientationUp];
}

// Draw a full image into a crop-sized area and offset to produce a cropped, rotated image
+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture withOrientation:(UIImageOrientation)orientation {

            // convert y coordinate to Origin bottom-left
    CGFloat orgY = aperture.Origin.y + aperture.size.height - imageToCrop.size.height,
            orgX = -aperture.Origin.x,
            scaleX = 1.0,
            scaleY = 1.0,
            rot = 0.0;
    CGSize size;

    switch (orientation) {
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            size = CGSizeMake(aperture.size.height, aperture.size.width);
            break;
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            size = aperture.size;
            break;
        default:
            assert(NO);
            return nil;
    }


    switch (orientation) {
        case UIImageOrientationRight:
            rot = 1.0 * M_PI / 2.0;
            orgY -= aperture.size.height;
            break;
        case UIImageOrientationRightMirrored:
            rot = 1.0 * M_PI / 2.0;
            scaleY = -1.0;
            break;
        case UIImageOrientationDown:
            scaleX = scaleY = -1.0;
            orgX -= aperture.size.width;
            orgY -= aperture.size.height;
            break;
        case UIImageOrientationDownMirrored:
            orgY -= aperture.size.height;
            scaleY = -1.0;
            break;
        case UIImageOrientationLeft:
            rot = 3.0 * M_PI / 2.0;
            orgX -= aperture.size.height;
            break;
        case UIImageOrientationLeftMirrored:
            rot = 3.0 * M_PI / 2.0;
            orgY -= aperture.size.height;
            orgX -= aperture.size.width;
            scaleY = -1.0;
            break;
        case UIImageOrientationUp:
            break;
        case UIImageOrientationUpMirrored:
            orgX -= aperture.size.width;
            scaleX = -1.0;
            break;
    }

    // set the draw rect to pan the image to the right spot
    CGRect drawRect = CGRectMake(orgX, orgY, imageToCrop.size.width, imageToCrop.size.height);

    // create a context for the new image
    UIGraphicsBeginImageContextWithOptions(size, NO, imageToCrop.scale);
    CGContextRef gc = UIGraphicsGetCurrentContext();

    // apply rotation and scaling
    CGContextRotateCTM(gc, rot);
    CGContextScaleCTM(gc, scaleX, scaleY);

    // draw the image to our clipped context using the offset rect
    CGContextDrawImage(gc, drawRect, imageToCrop.CGImage);

    // pull the image from our cropped context
    UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();

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

    // Note: this is autoreleased
    return cropped;
}
2
Scott Lahteine

Parce que j'en avais besoin tout à l'heure, voici le code de M-V dans Swift 4:

func imageWithImage(image: UIImage, croppedTo rect: CGRect) -> UIImage {

    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()

    let drawRect = CGRect(x: -rect.Origin.x, y: -rect.Origin.y, 
                          width: image.size.width, height: image.size.height)

    context?.clip(to: CGRect(x: 0, y: 0, 
                             width: rect.size.width, height: rect.size.height))

    image.draw(in: drawRect)

    let subImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()
    return subImage!
}
1
dasdom

Utiliser la fonction 

CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));

Voici un exemple de code utilisé dans un but différent, mais les clips sont corrects. 

- (UIImage *)aspectFillToSize:(CGSize)size
{
    CGFloat imgAspect = self.size.width / self.size.height;
    CGFloat sizeAspect = size.width/size.height;

    CGSize scaledSize;

        if (sizeAspect > imgAspect) { // increase width, crop height
            scaledSize = CGSizeMake(size.width, size.width / imgAspect);
        } else { // increase height, crop width
            scaledSize = CGSizeMake(size.height * imgAspect, size.height);
        }
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));
    [self drawInRect:CGRectMake(0.0f, 0.0f, scaledSize.width, scaledSize.height)];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
0
karim

Si vous voulez un portrait, rognez au centre de chaque photo.

Utilisez la solution @ M-V et remplacez cropRect.

CGFloat height = imageTaken.size.height;
CGFloat width = imageTaken.size.width;

CGFloat newWidth = height * 9 / 16;
CGFloat newX = abs((width - newWidth)) / 2;

CGRect cropRect = CGRectMake(newX,0, newWidth ,height);
0
benjaminhallock

Je souhaitais pouvoir recadrer à partir d'une région en fonction d'un rapport d'aspect et passer à une taille basée sur une limite extérieure. Voici ma variante:

import AVFoundation
import ImageIO

class Image {

    class func crop(image:UIImage, 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
    }
}

J'ai trouvé que certaines choses prêtaient à confusion, à savoir le recadrage et le redimensionnement. Le recadrage est géré avec l'origine du rect que vous passez à drawInRect et la mise à l'échelle est gérée par la partie taille. Dans mon cas, je devais établir un lien entre la taille du rectangle de découpe de la source et mon recto de sortie du même format. Le facteur d'échelle est alors sortie/entrée et doit être appliqué à drawRect (transmis à drawInRect).

Un inconvénient est que cette approche suppose effectivement que l'image que vous dessinez est plus grande que le contexte de l'image. Je n’ai pas testé cela, mais je pense que vous pouvez utiliser ce code pour gérer le recadrage/zoom, mais en définissant explicitement le paramètre scale comme étant le paramètre scale susmentionné. Par défaut, UIKit applique un multiplicateur basé sur la résolution de l'écran.

Enfin, il convient de noter que cette approche UIKit est supérieure aux approches CoreGraphics/Quartz et Core Image et semble traiter les problèmes d’orientation de l’image. Il est également intéressant de noter que c'est assez rapide, en second lieu à ImageIO, selon ce post ici: http://nshipster.com/image-resizing/

0
Chris Conover