Alors voici où je l'ai fait jusqu'à présent. J'utilise un UIImage capturé à partir de la caméra et je peux recadrer le carré central en paysage. Pour une raison quelconque, cela ne se traduit pas en mode portrait comme prévu. Je vais poster mon code et les journaux juste pour référence.
Code:
CGRect squareRect = CGRectMake(offsetX, offsetY, newWidth, newHeight);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], squareRect);
image = [UIImage imageWithCGImage:imageRef scale:1 orientation:image.imageOrientation];
Résultats du portrait (pas carré):
original image size: {1536, 2048}, with orientation: 3
squareRect: {{0, 256}, {1536, 1536}}
new image size: {1280, 1536}, with orientation: 3 <--- not expected
Résultats paysage (carré):
original image size: {2048, 1536}, with orientation: 1
squareRect: {{256, 0}, {1536, 1536}}
new image size: {1536, 1536}, with orientation: 1
Est-ce un bogue dans CGImageCreateWithImageInRect () ou est-ce que quelque chose me manque?
Je pense qu'ici serait la solution parfaite!
Ce n’est PAS une bonne idée de rogner la base d’image sur le toSize
's size. Cela aura l'air bizarre quand la résolution de l'image (taille) sera très grande.
Le code suivant recadrera l’image conformément au ratio de toSize
'[.
Amélioré à partir de la réponse de @BlackRider.
- (UIImage *)imageByCroppingImage:(UIImage *)image toSize:(CGSize)size
{
double newCropWidth, newCropHeight;
//=== To crop more efficently =====//
if(image.size.width < image.size.height){
if (image.size.width < size.width) {
newCropWidth = size.width;
}
else {
newCropWidth = image.size.width;
}
newCropHeight = (newCropWidth * size.height)/size.width;
} else {
if (image.size.height < size.height) {
newCropHeight = size.height;
}
else {
newCropHeight = image.size.height;
}
newCropWidth = (newCropHeight * size.width)/size.height;
}
//==============================//
double x = image.size.width/2.0 - newCropWidth/2.0;
double y = image.size.height/2.0 - newCropHeight/2.0;
CGRect cropRect = CGRectMake(x, y, newCropWidth, newCropHeight);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return cropped;
}
Cela fonctionne avec des orientations différentes. Les orientations portrait et paysage fonctionnent correctement.
- (UIImage *)imageByCroppingImage:(UIImage *)image toSize:(CGSize)size
{
// not equivalent to image.size (which depends on the imageOrientation)!
double refWidth = CGImageGetWidth(image.CGImage);
double refHeight = CGImageGetHeight(image.CGImage);
double x = (refWidth - size.width) / 2.0;
double y = (refHeight - size.height) / 2.0;
CGRect cropRect = CGRectMake(x, y, size.height, size.width);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef scale:0.0 orientation:self.imageOrientation];
CGImageRelease(imageRef);
return cropped;
}
Sur la base de la réponse vérifiée d'Elmundo et de sa version Swift d'Imbrue, voici la même solution qui calcule automatiquement la taille du centre de l'image (en tenant compte de l'orientation) et en prenant en compte les erreurs:
func cropImageToSquare(image: UIImage) -> UIImage? {
var imageHeight = image.size.height
var imageWidth = image.size.width
if imageHeight > imageWidth {
imageHeight = imageWidth
}
else {
imageWidth = imageHeight
}
let size = CGSize(width: imageWidth, height: imageHeight)
let refWidth : CGFloat = CGFloat(CGImageGetWidth(image.CGImage))
let refHeight : CGFloat = CGFloat(CGImageGetHeight(image.CGImage))
let x = (refWidth - size.width) / 2
let y = (refHeight - size.height) / 2
let cropRect = CGRectMake(x, y, size.height, size.width)
if let imageRef = CGImageCreateWithImageInRect(image.CGImage, cropRect) {
return UIImage(CGImage: imageRef, scale: 0, orientation: image.imageOrientation)
}
return nil
}
Swift 3 version
func cropImageToSquare(image: UIImage) -> UIImage? {
var imageHeight = image.size.height
var imageWidth = image.size.width
if imageHeight > imageWidth {
imageHeight = imageWidth
}
else {
imageWidth = imageHeight
}
let size = CGSize(width: imageWidth, height: imageHeight)
let refWidth : CGFloat = CGFloat(image.cgImage!.width)
let refHeight : CGFloat = CGFloat(image.cgImage!.height)
let x = (refWidth - size.width) / 2
let y = (refHeight - size.height) / 2
let cropRect = CGRect(x: x, y: y, width: size.height, height: size.width)
if let imageRef = image.cgImage!.cropping(to: cropRect) {
return UIImage(cgImage: imageRef, scale: 0, orientation: image.imageOrientation)
}
return nil
}
Essayez quelque chose comme ça:
- (UIImage *)imageByCroppingImage:(UIImage *)image toSize:(CGSize)size
{
double x = (image.size.width - size.width) / 2.0;
double y = (image.size.height - size.height) / 2.0;
CGRect cropRect = CGRectMake(x, y, size.height, size.width);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return cropped;
}
Comme vous le voyez, je ne spécifie pas d'orientation dans l'appel à UIImage imageWithCGImage:
. Je me demande si c'est le problème dans votre code.
Il existe la version Swift pour la solution vérifiée:
private func imageByCroppingImage(image : UIImage, size : CGSize) -> UIImage{
var refWidth : CGFloat = CGFloat(CGImageGetWidth(image.CGImage))
var refHeight : CGFloat = CGFloat(CGImageGetHeight(image.CGImage))
var x = (refWidth - size.width) / 2
var y = (refHeight - size.height) / 2
let cropRect = CGRectMake(x, y, size.height, size.width)
let imageRef = CGImageCreateWithImageInRect(image.CGImage, cropRect)
let cropped : UIImage = UIImage(CGImage: imageRef, scale: 0, orientation: image.imageOrientation)!
return cropped
}
amélioré de la réponse de @ Elmundo
+(UIImage *)getCenterMaxSquareImageByCroppingImage:(UIImage *)image withOrientation:(UIImageOrientation)imageOrientation
{
CGSize centerSquareSize;
double oriImgWid = CGImageGetWidth(image.CGImage);
double oriImgHgt = CGImageGetHeight(image.CGImage);
NSLog(@"oriImgWid==[%.1f], oriImgHgt==[%.1f]", oriImgWid, oriImgHgt);
if(oriImgHgt <= oriImgWid) {
centerSquareSize.width = oriImgHgt;
centerSquareSize.height = oriImgHgt;
}else {
centerSquareSize.width = oriImgWid;
centerSquareSize.height = oriImgWid;
}
NSLog(@"squareWid==[%.1f], squareHgt==[%.1f]", centerSquareSize.width, centerSquareSize.height);
double x = (oriImgWid - centerSquareSize.width) / 2.0;
double y = (oriImgHgt - centerSquareSize.height) / 2.0;
NSLog(@"x==[%.1f], x==[%.1f]", x, y);
CGRect cropRect = CGRectMake(x, y, centerSquareSize.height, centerSquareSize.width);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef scale:0.0 orientation:imageOrientation];
CGImageRelease(imageRef);
return cropped;
}
Swift 2 en utilisant UIImage Extension
extension UIImage
{
func imageByCroppingImage(size : CGSize) -> UIImage
{
let refWidth : CGFloat = CGFloat(CGImageGetWidth(self.CGImage))
let refHeight : CGFloat = CGFloat(CGImageGetHeight(self.CGImage))
let x = (refWidth - size.width) / 2
let y = (refHeight - size.height) / 2
let cropRect = CGRectMake(x, y, size.width, size.height)
let imageRef = CGImageCreateWithImageInRect(self.CGImage, cropRect)
let cropped : UIImage = UIImage(CGImage: imageRef!, scale: 0, orientation: self.imageOrientation)
return cropped
}
}
Swift 3
let refWidth : CGFloat = CGFloat(image.cgImage!.width)
let refHeight : CGFloat = CGFloat(image.cgImage!.height)
let x = (refWidth - size.width) / 2
let y = (refHeight - size.height) / 2
let cropRect = CGRect(x: x, y: y, width: size.width, height: size.height)
let imageRef = image.cgImage!.cropping(to: cropRect)
let cropped : UIImage = UIImage(cgImage: imageRef!, scale: 0, orientation: image.imageOrientation)
return cropped
C'est une vieille question, mais aucune des réponses n'est vraiment correcte (même la réponse acceptée). La chose importante à comprendre est que UIImageOrientation
(image.imageOrientation
) est en fait correct, mais sa définition deUPet notre définition deUPsont différentes. Pour nous,UPest le haut de l'appareil (où se trouve le bouton d'alimentation). Pour UIImageOrientation
,UPest le côté opposé des boutons de réglage du volume. Ainsi, si l'appareil prend une photo avec les commandes de volume abaissées, la valeur est UIImageOrientationUp
. Si vous prenez une photo en mode portrait (avec le bouton d'accueil enfoncé), la valeur est UIImageOrientationLeft
.
Ainsi, vous pouvez calculer le centre en portrait, puis appliquer la transformation suivante à l'image de sorte que le recadrage se trouve au bon endroit.
- (UIImage *)cropImage:(UIImage*)image toRect:(CGRect)rect {
CGFloat (^rad)(CGFloat) = ^CGFloat(CGFloat deg) {
return deg / 180.0f * (CGFloat) M_PI;
};
// determine the orientation of the image and apply a transformation to the crop rectangle to shift it to the correct position
CGAffineTransform rectTransform;
switch (image.imageOrientation) {
case UIImageOrientationLeft:
rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -image.size.height);
break;
case UIImageOrientationRight:
rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -image.size.width, 0);
break;
case UIImageOrientationDown:
rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -image.size.width, -image.size.height);
break;
default:
rectTransform = CGAffineTransformIdentity;
};
// adjust the transformation scale based on the image scale
rectTransform = CGAffineTransformScale(rectTransform, image.scale, image.scale);
// apply the transformation to the rect to create a new, shifted rect
CGRect transformedCropSquare = CGRectApplyAffineTransform(rect, rectTransform);
// use the rect to crop the image
CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, transformedCropSquare);
// create a new UIImage and set the scale and orientation appropriately
UIImage *result = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation];
// memory cleanup
CGImageRelease(imageRef);
return result;
}
Ce code déplace le carré de recadrage afin qu'il soit dans la position relative correcte.
il y a déjà beaucoup de solution, mais je veux poster le mien:
extension UIImage {
var cgImageWidth: Int { return cgImage?.width ?? 0 }
var cgImageheight: Int { return cgImage?.height ?? 0 }
var blance: CGFloat { return min(size.width, size.height)}
var blanceSize: CGSize { return CGSize(width: blance, height: blance) }
var blanceRect: CGRect { return CGRect(Origin: .zero, size: blanceSize) }
var roundedImage: UIImage? {
UIGraphicsBeginImageContextWithOptions(blanceSize, false, scale)
defer { UIGraphicsEndImageContext() }
guard let cgImage = cgImage?.cropping(to: CGRect(Origin: CGPoint(x: max(0, CGFloat(cgImageWidth) - blance)/2.0, y: max(0, CGFloat(cgImageheight) - blance)/2.0), size: blanceSize)) else { return nil }
UIBezierPath(ovalIn: blanceRect).addClip()
UIImage(cgImage: cgImage, scale: 1.0, orientation: self.imageOrientation).draw(in: blanceRect)
return UIGraphicsGetImageFromCurrentImageContext()
}
}
Ceci est basé sur la réponse de @ Alonzo Swift 3 ci-dessus, avec un code simplifié et rationalisé pour le rendre plus compréhensible et concis. Il se sauve également si l'image est déjà carrée.
extension UIImage {
func square() -> UIImage? {
if size.width == size.height {
return self
}
let cropWidth = min(size.width, size.height)
let cropRect = CGRect(
x: (size.width - cropWidth) * scale / 2.0,
y: (size.height - cropWidth) * scale / 2.0,
width: cropWidth * scale,
height: cropWidth * scale
)
guard let imageRef = cgImage?.cropping(to: cropRect) else {
return nil
}
return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
}
}
qu'en est-il de l'aide de UIImageView
+ (UIImage *)laodSquareImage:(UIImage *)image withDiameter:(CGFloat)diameter
{
CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;
imageView.image = image;
CALayer *layer = imageView.layer;
layer.masksToBounds = YES;
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
[layer renderInContext:context];
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return roundedImage;
}
Et vous pouvez également faire le cercle de l'image en ajoutant juste une ligne.
layer.cornerRadius =MAX( imageView.frame.size.height,imageView.frame.size.width)/2;
Solution basée sur l'extension Swift 2. Improvisé de @Geoffrey et @Nirav Dangi. Notez que nous définissons newCropWidth et newCropHeight sur la largeur ou la hauteur de l'image lorsqu'elles sont inférieures à la largeur ou à la hauteur donnée.
extension UIImage {
func imageByCroppingImage(size: CGSize) -> UIImage {
let newCropWidth, newCropHeight : CGFloat;
if(self.size.width < self.size.height) {
if (self.size.width < size.width) {
newCropWidth = self.size.width;
}
else {
newCropWidth = size.width;
}
newCropHeight = (newCropWidth * size.height)/size.width;
} else {
if (self.size.height < size.height) {
newCropHeight = self.size.height;
}
else {
newCropHeight = size.height;
}
newCropWidth = (newCropHeight * size.width)/size.height;
}
let x = self.size.width / 2 - newCropWidth / 2;
let y = self.size.height / 2 - newCropHeight / 2;
let cropRect = CGRectMake(x, y, newCropWidth, newCropHeight);
let imageRef = CGImageCreateWithImageInRect(self.CGImage, cropRect);
let croppedImage : UIImage = UIImage(CGImage: imageRef!, scale: 0, orientation: self.imageOrientation);
return croppedImage;
}
}
j'ai affaire à ce qui suit
extension UIImage {
var cgImageWidth: Int { return cgImage?.width ?? 0 }
var cgImageheight: Int { return cgImage?.height ?? 0 }
var blance: CGFloat { return min(size.width, size.height)}
var blanceSize: CGSize { return CGSize(width: blance, height: blance) }
var blanceRect: CGRect { return CGRect(Origin: .zero, size: blanceSize) }
var roundedImage: UIImage? {
UIGraphicsBeginImageContextWithOptions(blanceSize, false, scale)
defer { UIGraphicsEndImageContext() }
guard let cgImage = cgImage?.cropping(to: CGRect(Origin: CGPoint(x: max(0, CGFloat(cgImageWidth) - blance)/2.0, y: max(0, CGFloat(cgImageheight) - blance)/2.0), size: blanceSize)) else { return nil }
UIBezierPath(ovalIn: blanceRect).addClip()
UIImage(cgImage: cgImage, scale: 1.0, orientation: self.imageOrientation).draw(in: blanceRect)
return UIGraphicsGetImageFromCurrentImageContext()
}
}
Xamarin:
UIImage ImageByCroppingImage(UIImage image, CGSize size)
{
double newCropWidth, newCropHeight;
if (image.Size.Width < image.Size.Height)
{
newCropWidth = image.Size.Width < size.Width ? size.Width : image.Size.Width;
newCropHeight = (newCropWidth * size.Height) / size.Width;
}
else
{
newCropHeight = image.Size.Height < size.Height ? size.Height : image.Size.Height;
newCropWidth = (newCropHeight * size.Width) / size.Height;
}
double x = image.Size.Width / 2.0 - newCropWidth / 2.0;
double y = image.Size.Height / 2.0 - newCropHeight / 2.0;
CGRect cropRect = new CGRect(x, y, newCropWidth, newCropHeight);
var imageRef = image.CGImage.WithImageInRect(cropRect);
var cropped = new UIImage(imageRef);
return cropped;
}