I have to resize an album artwork form the file I get with this code:
for (NSString *format in [asset availableMetadataFormats]) {
for (AVMetadataItem *item in [asset metadataForFormat:format]) {
if ([[item commonKey] isEqualToString:#"title"]) {
//NSLog(#"name: %#", (NSString *)[item value]);
downloadedCell.nameLabel.text = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"artist"]) {
downloadedCell.artistLabel.text = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"albumName"]) {
//musicItem.strAlbumName = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"artwork"]) {
UIImage *img = nil;
if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
img = [UIImage imageWithData:[item.value copyWithZone:nil]];
}
else { // if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) {
NSData *data = [(NSDictionary *)[item value] objectForKey:#"data"];
img = [UIImage imageWithData:data];
}
// musicItem.imgArtwork = img;
UIImage *newImage = [self resizeImage:img width:70.0f height:70.0f];
downloadedCell.artworkImage.image = newImage;
}
When I apply this method:
- (UIImage *)resizeImage:(UIImage *)image width:(int)width height:(int)height {
//NSLog(#"resizing");
CGImageRef imageRef = [image CGImage];
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
//if (alphaInfo == kCGImageAlphaNone)
alphaInfo = kCGImageAlphaNoneSkipLast;
CGContextRef bitmap = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(imageRef),
4 * width, CGImageGetColorSpace(imageRef), alphaInfo);
CGContextDrawImage(bitmap, CGRectMake(0, 0, width, height), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage *result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap);
CGImageRelease(ref);
return result;
}
I ALWAYS get a noise image like you can see in the photo above.
http://postimage.org/image/jltpfza11/
How can I get a better resolution image?
If your view is 74x74, you should resize to twice that on retina displays. So, something like:
CGFloat imageSize = 74.0f * [[UIScreen mainScreen] scale];
UIImage *newImage = [self resizeImage:img width:imageSize height:imageSize];
Then you need to set the contentMode of your image view to something like UIViewContentModeScaleAspectFill.
Try specify explicitly the level of interpolation for the context using:
CGContextSetInterpolationQuality(bitmap, kCGInterpolationHigh);
Your resizeImage:width:height: method thus becomes:
-(UIImage *)resizeImage:(UIImage *)image width:(int)width height:(int)height {
//NSLog(#"resizing");
CGImageRef imageRef = [image CGImage];
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
//if (alphaInfo == kCGImageAlphaNone)
alphaInfo = kCGImageAlphaNoneSkipLast;
CGContextRef bitmap = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(imageRef),
4 * width, CGImageGetColorSpace(imageRef), alphaInfo);
CGContextSetInterpolationQuality(bitmap, kCGInterpolationHigh);
CGContextDrawImage(bitmap, CGRectMake(0, 0, width, height), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage *result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap);
CGImageRelease(ref);
return result;
}
Related
We have a process that takes high resolution source PNG/JPG images and creates renditions of these images in various lower resolution formats / cropped versions.
void ResizeAndSaveSourceImageFromFile(NSString *imagePath, NSInteger width, NSInteger height, NSString *destinationFolder, NSString *fileName, BOOL shouldCrop, NSInteger rotation, NSInteger cornerRadius, BOOL removeAlpha) {
NSString *outputFilePath = [NSString stringWithFormat:#"%#/%#", destinationFolder, fileName];
NSImage *sourceImage = [[NSImage alloc] initWithContentsOfFile:imagePath];
NSSize sourceSize = sourceImage.size;
float sourceAspect = sourceSize.width / sourceSize.height;
float desiredAspect = width / height;
float finalWidth = width;
float finalHeight = height;
if (shouldCrop == true) {
if (desiredAspect > sourceAspect) {
width = height * sourceAspect;
} else if (desiredAspect < sourceAspect) {
height = width / sourceAspect;
}
}
if (width < finalWidth) {
width = finalWidth;
height = width / sourceAspect;
}
if (height < finalHeight) {
height = finalHeight;
width = height * sourceAspect;
}
NSImage *resizedImage = ImageByScalingToSize(sourceImage, CGSizeMake(width, height));
if (shouldCrop == true) {
resizedImage = ImageByCroppingImage(resizedImage, CGSizeMake(finalWidth, finalHeight));
}
if (rotation != 0) {
resizedImage = ImageRotated(resizedImage, rotation);
}
if (cornerRadius != 0) {
resizedImage = ImageRounded(resizedImage, cornerRadius);
}
NSBitmapImageRep *imgRep = UnscaledBitmapImageRep(resizedImage, removeAlpha);
NSBitmapImageFileType type = NSPNGFileType;
if ([fileName rangeOfString:#".jpg"].location != NSNotFound) {
type = NSJPEGFileType;
}
NSData *imageData = [imgRep representationUsingType:type properties: #{}];
[imageData writeToFile:outputFilePath atomically:NO];
if ([outputFilePath rangeOfString:#"land-mdpi"].location != NSNotFound) {
[imageData writeToFile:[outputFilePath stringByReplacingOccurrencesOfString:#"land-mdpi" withString:#"tvdpi"] atomically:NO];
}
}
NSImage* ImageByScalingToSize(NSImage* sourceImage, NSSize newSize) {
if (! sourceImage.isValid) return nil;
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:newSize.width
pixelsHigh:newSize.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0
bitsPerPixel:0];
rep.size = newSize;
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:rep]];
[sourceImage drawInRect:NSMakeRect(0, 0, newSize.width, newSize.height) fromRect:NSZeroRect operation:NSCompositingOperationCopy fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
NSImage *newImage = [[NSImage alloc] initWithSize:newSize];
[newImage addRepresentation:rep];
return newImage;
}
NSBitmapImageRep* UnscaledBitmapImageRep(NSImage *image, BOOL removeAlpha) {
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:image.size.width
pixelsHigh:image.size.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:0
bitsPerPixel:0];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:
[NSGraphicsContext graphicsContextWithBitmapImageRep:rep]];
[image drawAtPoint:NSMakePoint(0, 0)
fromRect:NSZeroRect
operation:NSCompositingOperationSourceOver
fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
NSBitmapImageRep *imgRepFinal = rep;
if (removeAlpha == YES) {
NSImage *newImage = [[NSImage alloc] initWithSize:[rep size]];
[newImage addRepresentation:rep];
static int const kNumberOfBitsPerColour = 5;
NSRect imageRect = NSMakeRect(0.0, 0.0, newImage.size.width, newImage.size.height);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef tileGraphicsContext = CGBitmapContextCreate (NULL, imageRect.size.width, imageRect.size.height, kNumberOfBitsPerColour, 2 * imageRect.size.width, colorSpace, kCGBitmapByteOrder16Little | kCGImageAlphaNoneSkipFirst);
NSData *imageDataTIFF = [newImage TIFFRepresentation];
CGImageRef imageRef = [[NSBitmapImageRep imageRepWithData:imageDataTIFF] CGImage];
CGContextDrawImage(tileGraphicsContext, imageRect, imageRef);
// Create an NSImage from the tile graphics context
CGImageRef newImageRef = CGBitmapContextCreateImage(tileGraphicsContext);
NSImage *newNSImage = [[NSImage alloc] initWithCGImage:newImageRef size:imageRect.size];
// Clean up
CGImageRelease(newImageRef);
CGContextRelease(tileGraphicsContext);
CGColorSpaceRelease(colorSpace);
CGImageRef CGImage = [newNSImage CGImageForProposedRect:nil context:nil hints:nil];
imgRepFinal = [[NSBitmapImageRep alloc] initWithCGImage:CGImage];
}
return imgRepFinal;
}
NSImage* ImageByCroppingImage(NSImage* image, CGSize size) {
NSInteger trueWidth = image.representations[0].pixelsWide;
double refWidth = image.size.width;
double refHeight = image.size.height;
double scale = trueWidth / refWidth;
double x = (refWidth - size.width) / 2.0;
double y = (refHeight - size.height) / 2.0;
CGRect cropRect = CGRectMake(x * scale, y * scale, size.width * scale, size.height * scale);
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)[image TIFFRepresentation], NULL);
CGImageRef maskRef = CGImageSourceCreateImageAtIndex(source, 0, NULL);
CGImageRef imageRef = CGImageCreateWithImageInRect(maskRef, cropRect);
NSImage *cropped = [[NSImage alloc] initWithCGImage:imageRef size:size];
CGImageRelease(imageRef);
return cropped;
}
This process works well and gets the results we want. We can re-run these functions on hundreds of images and get the same output every time. We then commit these files in git repos.
HOWEVER, every time we update macOS to a new version (such as updating to High Sierra, Monterey, etc.) when we run these functions ALL of the images result in an output that is different and has different hashes so git treats these images as being changed even though the source images are identical.
FURTHER, JPG images seem to have a different output when run on an Intel mac vs. an Apple M1 mac.
We have checked the head of the output images using a command like:
od -bc banner.png | head
This results in the same head data in all cases even though the actual image data doesn't match after version changes.
We've also checked CGImageSourceCopyPropertiesAtIndex such as:
{
ColorModel = RGB;
Depth = 8;
HasAlpha = 1;
PixelHeight = 1080;
PixelWidth = 1920;
ProfileName = "Generic RGB Profile";
"{Exif}" = {
PixelXDimension = 1920;
PixelYDimension = 1080;
};
"{PNG}" = {
InterlaceType = 0;
};
}
Which do not show any differences between versions of macOS or Intel vs. M1.
We don't want the hash to keep changing on us and resulting in extra churn in git and hoping for feedback that may help in us getting consistent output in all cases.
Any tips are greatly appreciated.
I have path of the png file (Image). I would like to create new Image in the same folder where in the center of this Image will be Black rectangle with dimensions 300x100. Then I need to get the path of new created Image.
Can someone, please, help me with this issue?
I was playing with this code:
- (void) grayscale:(UIImage*) image {
CGContextRef ctx;
CGImageRef imageRef = [image CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);
// Now your rawData contains the image data in the RGBA8888 pixel format.
NSUInteger byteIndex = (bytesPerRow * 0) + 0 * bytesPerPixel;
for (int ii = 0 ; ii < width * height ; ++ii)
{
// Get color values to construct a UIColor
CGFloat red = (rawData[byteIndex] * 1.0) / 255.0;
CGFloat green = (rawData[byteIndex + 1] * 1.0) / 255.0;
CGFloat blue = (rawData[byteIndex + 2] * 1.0) / 255.0;
CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.0;
rawData[byteIndex] = (char) (red);
rawData[byteIndex+1] = (char) (green);
rawData[byteIndex+2] = (char) (blue);
byteIndex += 4;
}
ctx = CGBitmapContextCreate(rawData,
CGImageGetWidth( imageRef ),
CGImageGetHeight( imageRef ),
8,
CGImageGetBytesPerRow( imageRef ),
CGImageGetColorSpace( imageRef ),
kCGImageAlphaPremultipliedLast );
imageRef = CGBitmapContextCreateImage (ctx);
UIImage* rawImage = [UIImage imageWithCGImage:imageRef];
CGContextRelease(ctx);
self.workingImage = rawImage;
[self.imageView setImage:self.workingImage];
free(rawData);
}
But I didn't successed.
Yes you can draw image with another black layer on it. See below my code which will satisfy your requirement:
Note: Please add image "testImage.png" into your code then execute below code.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize strTemp3;
- (void)viewDidLoad {
[super viewDidLoad];
[self testImageWrite];
[self addNewImageFromPath];
}
- (UIImage *)imageToDraw
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 100), NO, [UIScreen mainScreen].scale);
UIImage *natureImage = [UIImage imageNamed:#"testImage"];
[natureImage drawInRect:CGRectMake(0, 0, 300, 100)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rectangle = CGRectMake(75, 25, 150, 50);
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0);
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, rectangle);
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultingImage;
}
- (NSString *)filePath
{
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"Image.png"];
}
- (void)testImageWrite
{
NSData *imageData = UIImagePNGRepresentation([self imageToDraw]);
NSError *writeError = nil;
BOOL success = [imageData writeToFile:[self filePath] options:0 error:&writeError];
if (!success || writeError != nil)
{
NSLog(#"Error Writing: %#",writeError.description);
}
}
-(void)addNewImageFromPath{
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 300, 100)];
imgView.image = [UIImage imageWithContentsOfFile:[self filePath]];
[self.view addSubview:imgView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
Output:
Actual Image:
New Image:
I want to manipulate image and shuffle colors. I'm trying to rotate 180 degress with pixels but failed. I don't want to use UIImageView rotate cause i won't just rotate images. I want to do them whatever i want.
EDIT : It was wrong operator. I dont know why i used % instead of / . Anyways i hope this code helps someone(it works).
- (IBAction)shuffleImage:(id)sender {
[self calculateRGBAsAndChangePixels:self.imageView.image atX:0 andY:0];
}
-(void)calculateRGBAsAndChangePixels:(UIImage*)image atX:(int)x andY:(int)y
{
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * image.size.width;
NSUInteger bitsPerComponent = 8;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, image.size.width, image.size.height, bitsPerComponent,bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, image.size.width, image.size.height}, image.CGImage);
UInt8* data = (UInt8*)CGBitmapContextGetData(bmContext);
const size_t bitmapByteCount = bytesPerRow * image.size.height;
NSMutableArray *reds = [[NSMutableArray alloc] init];
NSMutableArray *greens = [[NSMutableArray alloc] init];
NSMutableArray *blues = [[NSMutableArray alloc] init];
for (size_t i = 0; i < bitmapByteCount; i += 4)
{
[reds addObject:[NSNumber numberWithInt:data[i]]];
[greens addObject:[NSNumber numberWithInt:data[i+1]]];
[blues addObject:[NSNumber numberWithInt:data[i+2]]];
}
for (size_t i = 0; i < bitmapByteCount; i += 4)
{
data[i] = [[reds objectAtIndex:reds.count-i%4-1] integerValue];
data[i+1] = [[greens objectAtIndex:greens.count-i%4-1] integerValue];
data[i+2] = [[blues objectAtIndex:blues.count-i%4-1] integerValue];
}
CGImageRef newImage = CGBitmapContextCreateImage(bmContext);
UIImage *imageView = [[UIImage alloc] initWithCGImage:newImage];
self.imageView.image = imageView;
}
Assuming that you are wanting to make the image turn upside down (rotate it 180) and not mirror it, I found some relevant code on another question that may help you:
static inline double radians (double degrees) {return degrees * M_PI/180;}
UIImage* rotate(UIImage* src, UIImageOrientation orientation)
{
UIGraphicsBeginImageContext(src.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (orientation == UIImageOrientationRight) {
CGContextRotateCTM (context, radians(90));
} else if (orientation == UIImageOrientationLeft) {
CGContextRotateCTM (context, radians(-90));
} else if (orientation == UIImageOrientationDown) {
// NOTHING
} else if (orientation == UIImageOrientationUp) {
CGContextRotateCTM (context, radians(90));
}
[src drawAtPoint:CGPointMake(0, 0)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
If you're trying to mirror the image, this code example from this question maybe of help:
UIImage* sourceImage = [UIImage imageNamed:#"whatever.png"];
UIImage* flippedImage = [UIImage imageWithCGImage:sourceImage.CGImage
scale:sourceImage.scale
orientation:UIImageOrientationUpMirrored];
So you're looking to actually manipulate the raw pixel data. Check this out then:
Getting the pixel data from a CGImage object
It's for MacOS but should be relevant for iOS as well.
In the past we had two different ways of blurring UIImages, and both led to crashes for our users. The first way crashes when the user puts the device into background (GPU error, not allowed to do anything in background).
The second one crashes with ESC_BAD_ACCESS errors.
What's a better (crash-safe) way to do it?
Version 1:
- (UIImage *)blurred:(float)inputRadius {
// create our blurred image
CIContext *context = [CIContext contextWithOptions:nil];
CIImage *inputImage = [CIImage imageWithCGImage:self.CGImage];
// setting up Gaussian Blur (we could use one of many filters offered by Core Image)
CIFilter *filter = [CIFilter filterWithName:#"CIGaussianBlur"];
[filter setValue:inputImage forKey:kCIInputImageKey];
[filter setValue:[NSNumber numberWithFloat:inputRadius] forKey:#"inputRadius"];
CIImage *result = [filter valueForKey:kCIOutputImageKey];
CGImageRef cgImage = [context createCGImage:result fromRect:[inputImage extent]];
UIImage *blurredImage = [UIImage imageWithCGImage:cgImage];
CFRelease(cgImage);
return blurredImage;
}
Version 2:
- (UIImage*)blurredImage:(CGFloat)blurRadius {
if (blurRadius < 0.0) {
blurRadius = 0.0;
}
CGImageRef img = self.CGImage;
CGFloat inputImageScale = self.scale;
vImage_Buffer inBuffer, outBuffer;
vImage_Error error;
void *pixelBuffer;
CGDataProviderRef inProvider = CGImageGetDataProvider(img);
CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
inBuffer.width = CGImageGetWidth(img);
inBuffer.height = CGImageGetHeight(img);
inBuffer.rowBytes = CGImageGetBytesPerRow(img);
inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);
pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
outBuffer.data = pixelBuffer;
outBuffer.width = CGImageGetWidth(img);
outBuffer.height = CGImageGetHeight(img);
outBuffer.rowBytes = CGImageGetBytesPerRow(img);
CGFloat inputRadius = blurRadius * inputImageScale;
if (inputRadius - 2. < __FLT_EPSILON__)
inputRadius = 2.;
uint32_t radius = floor((inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5) / 2);
radius |= 1; // force radius to be odd so that the three box-blur methodology works.
// line of crash
error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
if (!error) {
error = vImageBoxConvolve_ARGB8888(&outBuffer, &inBuffer, NULL, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
}
if (error) {
return self;
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
outBuffer.width,
outBuffer.height,
8,
outBuffer.rowBytes,
colorSpace,
(CGBitmapInfo)kCGImageAlphaNoneSkipLast);
CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
CGContextRelease(ctx);
CGColorSpaceRelease(colorSpace);
free(pixelBuffer);
CFRelease(inBitmapData);
CGImageRelease(imageRef);
return returnImage;
}
This is driving me crazy because I can't get it to work. I have the following scenario:
I'm using an AVCaptureSession and an AVCaptureVideoPreviewLayer to create my own camera interface. The interface shows a rectangle. Below is the AVCaptureVideoPreviewLayer that fills the whole screen.
I want to the captured image to be cropped in a way, that the resulting image shows exactly the content seen in the rect on the display.
My setup looks like this:
_session = [[AVCaptureSession alloc] init];
AVCaptureSession *session = _session;
session.sessionPreset = AVCaptureSessionPresetPhoto;
AVCaptureDevice *camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (camera == nil) {
[self showImagePicker];
_isSetup = YES;
return;
}
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
captureVideoPreviewLayer.frame = self.liveCapturePlaceholderView.bounds;
[self.liveCapturePlaceholderView.layer addSublayer:captureVideoPreviewLayer];
NSError *error;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:camera error:&error];
if (error) {
HGAlertViewWrapper *av = [[HGAlertViewWrapper alloc] initWithTitle:kFailedConnectingToCameraAlertViewTitle message:kFailedConnectingToCameraAlertViewMessage cancelButtonTitle:kFailedConnectingToCameraAlertViewCancelButtonTitle otherButtonTitles:#[kFailedConnectingToCameraAlertViewRetryButtonTitle]];
[av showWithBlock:^(NSString *buttonTitle){
if ([buttonTitle isEqualToString:kFailedConnectingToCameraAlertViewCancelButtonTitle]) {
[self.delegate gloameCameraViewControllerDidCancel:self];
}
else {
[self setupAVSession];
}
}];
}
[session addInput:input];
NSDictionary *options = #{ AVVideoCodecKey : AVVideoCodecJPEG };
_stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
[_stillImageOutput setOutputSettings:options];
[session addOutput:_stillImageOutput];
[session startRunning];
_isSetup = YES;
I'm capturing the image like this:
[_stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
if (error) {
MWLogDebug(#"Error capturing image from camera. %#, %#", error, [error userInfo]);
_capturePreviewLayer.connection.enabled = YES;
}
else
{
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
CGRect cropRect = [self createCropRectForImage:image];
UIImage *croppedImage;// = [self cropImage:image toRect:cropRect];
UIGraphicsBeginImageContext(cropRect.size);
[image drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y)];
croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.capturedImage = croppedImage;
[_session stopRunning];
}
}];
In the createCropRectForImage: method I've tried various ways to calculate the rect to cut out of the image, but with no success so far.
- (CGRect)createCropRectForImage:(UIImage *)image
{
CGPoint maskTopLeftCorner = CGPointMake(self.maskRectView.frame.origin.x, self.maskRectView.frame.origin.y);
CGPoint maskBottomRightCorner = CGPointMake(self.maskRectView.frame.origin.x + self.maskRectView.frame.size.width, self.maskRectView.frame.origin.y + self.maskRectView.frame.size.height);
CGPoint maskTopLeftCornerInLayerCoords = [_capturePreviewLayer convertPoint:maskTopLeftCorner fromLayer:self.maskRectView.layer.superlayer];
CGPoint maskBottomRightCornerInLayerCoords = [_capturePreviewLayer convertPoint:maskBottomRightCorner fromLayer:self.maskRectView.layer.superlayer];
CGPoint maskTopLeftCornerInDeviceCoords = [_capturePreviewLayer captureDevicePointOfInterestForPoint:maskTopLeftCornerInLayerCoords];
CGPoint maskBottomRightCornerInDeviceCoords = [_capturePreviewLayer captureDevicePointOfInterestForPoint:maskBottomRightCornerInLayerCoords];
float x = maskTopLeftCornerInDeviceCoords.x * image.size.width;
float y = (1 - maskTopLeftCornerInDeviceCoords.y) * image.size.height;
float width = fabsf(maskTopLeftCornerInDeviceCoords.x - maskBottomRightCornerInDeviceCoords.x) * image.size.width;
float height = fabsf(maskTopLeftCornerInDeviceCoords.y - maskBottomRightCornerInDeviceCoords.y) * image.size.height;
return CGRectMake(x, y, width, height);
}
That is my current version but doesn't even get the proportions right. Could some one please help me!
I have also tried using this method to crop my image:
- (UIImage*)cropImage:(UIImage*)originalImage toRect:(CGRect)rect{
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], rect);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef);
CGContextRef bitmap = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo);
if (originalImage.imageOrientation == UIImageOrientationLeft) {
CGContextRotateCTM (bitmap, radians(90));
CGContextTranslateCTM (bitmap, 0, -rect.size.height);
} else if (originalImage.imageOrientation == UIImageOrientationRight) {
CGContextRotateCTM (bitmap, radians(-90));
CGContextTranslateCTM (bitmap, -rect.size.width, 0);
} else if (originalImage.imageOrientation == UIImageOrientationUp) {
// NOTHING
} else if (originalImage.imageOrientation == UIImageOrientationDown) {
CGContextTranslateCTM (bitmap, rect.size.width, rect.size.height);
CGContextRotateCTM (bitmap, radians(-180.));
}
CGContextDrawImage(bitmap, CGRectMake(0, 0, rect.size.width, rect.size.height), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage *resultImage=[UIImage imageWithCGImage:ref];
CGImageRelease(imageRef);
CGContextRelease(bitmap);
CGImageRelease(ref);
return resultImage;
}
Does anybody have the 'right combination' of methods to make this work? :)
In Swift 3:
private func cropToPreviewLayer(originalImage: UIImage) -> UIImage {
let outputRect = previewLayer.metadataOutputRectConverted(fromLayerRect: previewLayer.bounds)
var cgImage = originalImage.cgImage!
let width = CGFloat(cgImage.width)
let height = CGFloat(cgImage.height)
let cropRect = CGRect(x: outputRect.origin.x * width, y: outputRect.origin.y * height, width: outputRect.size.width * width, height: outputRect.size.height * height)
cgImage = cgImage.cropping(to: cropRect)!
let croppedUIImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: originalImage.imageOrientation)
return croppedUIImage
}
I've solved this problem by using metadataOutputRectOfInterestForRect function.
It works with any orientation.
[_stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error)
{
if (error)
{
[_delegate cameraView:self error:#"Take picture failed"];
}
else
{
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *takenImage = [UIImage imageWithData:jpegData];
CGRect outputRect = [_previewLayer metadataOutputRectOfInterestForRect:_previewLayer.bounds];
CGImageRef takenCGImage = takenImage.CGImage;
size_t width = CGImageGetWidth(takenCGImage);
size_t height = CGImageGetHeight(takenCGImage);
CGRect cropRect = CGRectMake(outputRect.origin.x * width, outputRect.origin.y * height, outputRect.size.width * width, outputRect.size.height * height);
CGImageRef cropCGImage = CGImageCreateWithImageInRect(takenCGImage, cropRect);
takenImage = [UIImage imageWithCGImage:cropCGImage scale:1 orientation:takenImage.imageOrientation];
CGImageRelease(cropCGImage);
}
}
];
The takenImage is still imageOrientation dependent image. You can delete orientation information for further image processing.
UIGraphicsBeginImageContext(takenImage.size);
[takenImage drawAtPoint:CGPointZero];
takenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
In Swift 4:
I prefer to never force-unwrap to avoid crashes, so I use optionals and guards in mine.
private func cropToPreviewLayer(originalImage: UIImage) -> UIImage? {
guard let cgImage = originalImage.cgImage else { return nil }
let outputRect = previewLayer.metadataOutputRectConverted(fromLayerRect: previewLayer.bounds)
let width = CGFloat(cgImage.width)
let height = CGFloat(cgImage.height)
let cropRect = CGRect(x: outputRect.origin.x * width, y: outputRect.origin.y * height, width: outputRect.size.width * width, height: outputRect.size.height * height)
if let croppedCGImage = cgImage.cropping(to: cropRect) {
return UIImage(cgImage: croppedCGImage, scale: 1.0, orientation: originalImage.imageOrientation)
}
return nil
}
AVMakeRectWithAspectRatioInsideRect
this api is from AVFoundation, it will return the crop region for the image given the crop size.