ios - एक UIImage फसल




objective-c cocoa-touch (15)

मेरे पास कुछ कोड है जो एक छवि का आकार बदलता है, इसलिए मैं छवि के केंद्र का एक स्केल किया गया हिस्सा प्राप्त कर सकता हूं - मैं इसका उपयोग UIImage लेने के लिए करता हूं और एक UIImage छोटे, वर्ग का प्रतिनिधित्व देता हूं, जैसा कि एल्बम दृश्य में देखा गया है फोटो ऐप का। (मुझे पता है कि मैं UIImageView उपयोग कर सकता हूं और एक ही परिणाम प्राप्त करने के लिए फसल मोड समायोजित कर सकता हूं, लेकिन इन छवियों को कभी-कभी UIWebViews में प्रदर्शित किया जाता UIWebViews )।

मैंने इस कोड में कुछ क्रैश नोटिस करना शुरू कर दिया है और मैं थोड़ी सी स्टंप हूं। मेरे पास दो अलग-अलग सिद्धांत हैं और मैं सोच रहा हूं कि या तो आधार पर है या नहीं।

सिद्धांत 1) मैं अपने लक्ष्य आकार के एक ऑफस्क्रीन छवि संदर्भ में ड्राइंग करके फसल प्राप्त करता हूं। चूंकि मैं छवि का केंद्र भाग चाहता हूं, इसलिए मैंने drawInRect तर्क को मेरे छवि संदर्भ की सीमाओं से बड़ा कुछ ऐसा करने के लिए पारित किया है। मैं उम्मीद कर रहा था कि यह कोशेर था, लेकिन क्या मैं इसके बजाय अन्य स्मृति को आकर्षित करने का प्रयास कर रहा हूं कि मुझे छूना नहीं चाहिए?

सिद्धांत 2) मैं पृष्ठभूमि पृष्ठभूमि में यह सब कर रहा हूँ। मुझे पता है कि UIKit के भाग हैं जो मुख्य धागे तक सीमित हैं। मैं मान रहा था कि एक ऑफस्क्रीन व्यू पर ड्राइंग इन में से एक नहीं था। क्या मै गलत हु?

(ओह, मैं NSImage's drawInRect:fromRect:operation:fraction: को कैसे याद करता NSImage's drawInRect:fromRect:operation:fraction: विधि।)


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

स्विफ्ट 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

इस पुल समारोह की सहायता से CGRectMake -> CGRect ( इस जवाब को क्रेडिट @rob mayoff द्वारा उत्तर दिया गया है):

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

उपयोग है:

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

आउटपुट:


@Arne का उत्तर का पालन करें। मैं बस श्रेणी समारोह में फिक्सिंग। इसे 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;
}

आईओएस 9.2 एसडीके पर, मैं UIView से 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;
}

थोड़ा अजीब लग रहा है लेकिन महान काम करता है और छवि अभिविन्यास को ध्यान में रखता है:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)

मैं अन्य समाधानों से संतुष्ट नहीं था क्योंकि वे या तो कई बार आकर्षित करते हैं (आवश्यक से अधिक शक्ति का उपयोग कर) या उन्मुखीकरण के साथ समस्याएं हैं। यहां एक यूआईएममेज * छवि से एक स्केल्ड स्क्वायर फसल इमेज के लिए उपयोग किया गया है।

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

यहां कोई भी जवाब सभी पैमाने और रोटेशन मुद्दों को 100% सही तरीके से संभालता है। यहां तक ​​कि आईओएस 7/8 के रूप में अद्यतित सब कुछ का एक संश्लेषण है। यह 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;
}

यहां मेरा यूआईएममेज फसल कार्यान्वयन है जो इमेजेंटेशन प्रॉपर्टी का पालन करता है। सभी उन्मुखताओं का पूरी तरह से परीक्षण किया गया था।

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

स्विफ्ट एक्सटेंशन

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

हेड अप: ये सभी उत्तर एक CGImage छवि ऑब्जेक्ट मानते हैं।

image.CGImage शून्य वापस आ सकता है, अगर CIImage द्वारा समर्थित किया जाता है, तो यह मामला होगा यदि आपने इस छवि को CIFilter का उपयोग करके बनाया है।

उस स्थिति में, आपको छवि को नए संदर्भ में खींचना पड़ सकता है, और उस छवि को वापस ( धीमा ) वापस कर देना पड़ सकता है।

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

awolf के जवाब का स्विफ्ट संस्करण, जो मेरे लिए काम करता था:

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

स्विफ्ट 2.0 अपडेट ( CIImage संगतता)

मैक्सिम के जवाब का विस्तार करना लेकिन काम करता है अगर आपकी छवि CIImage आधारित है, साथ ही साथ।

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

Noodles उत्तर के आधार पर एक अद्यतन स्विफ्ट 3 संस्करण यहां दिया गया है

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
}

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

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

मैंने देखा है कि अधिकांश प्रतिक्रियाओं (x, y) के लिए केवल (0, 0) की स्थिति से संबंधित है। ठीक है कि यह एक मामला है, लेकिन मैं अपने फसल ऑपरेशन को केंद्रित करना चाहता हूं। डब्ल्यूटीएफ टिप्पणी के बाद लाइन को समझने में मुझे कुछ समय लगा।

आइए पोर्ट्रेट अभिविन्यास के साथ कैप्चर की गई छवि का केस लें:

  1. मूल छवि ऊंचाई इसकी चौड़ाई से अधिक है (वू, अब तक कोई आश्चर्य नहीं!)
  2. छवि है कि CGImageCreateWithImageInRect विधि अपनी दुनिया में कल्पना करता है वास्तव में एक चित्र नहीं है लेकिन एक परिदृश्य है (यही कारण है कि यदि आप छवि WithCGImage कन्स्ट्रक्टर में अभिविन्यास तर्क का उपयोग नहीं करते हैं, तो यह 180 घुमाएगा)।
  3. तो, आपको कल्पना करना चाहिए कि यह एक परिदृश्य है, (0, 0) स्थिति छवि के शीर्ष दाएं कोने में स्थित है।

उम्मीद है कि यह समझ में आता है! यदि ऐसा नहीं होता है, तो अलग-अलग मानों को आजमाएं, आप देखेंगे कि जब आपकी फसल के लिए सही एक्स, वाई, चौड़ाई और ऊंचाई चुनने की बात आती है तो तर्क उलटा होता है।







image-manipulation