Generate SHA1 (of Image) on iPhone? (Incorrect results) - iphone

I have looked everywhere and cannot find a solution to my problem. I have written a program for Android and Windows both that calculates the SHA1 hash of a given image. I confirmed these hashes with HashCalc that I downloaded. However when I try to do the same for iPhone I get a completely different hash. I believe it has something to do with converting the image to a byte array before I compute the hash. Here is my code:
- (void)imagePickerCotroller:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
[imageview setImage:image];
NSData* imageData = UIImageJPEGRepresentation(image, 1);
NSString *hash = makeSHA1(imageData);
[filepath setText: hash];
[self dismissModalViewControllerAnimated:YES];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissModalViewControllerAnimated:YES];
}
NSString* makeSHA1(NSData *data) {
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, data.length, digest);
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x", digest[i]];
return output;
}

I suspect the problem here is that you're converting the data using UIImageJPEGRepresentation(). Even if you started with JPEG data, there isn't a promise that this returns exactly the same bytes as you started with.
My (untested) suggestion is that you ask the CGDataProvider for the raw data. Something like this:
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef imageData = CGDataProviderCopyData(provider);
if (imageData != NULL) {
...
CFRelease(imageData);
}

Related

Deallocating images created with UIGraphicsBeginImageContext

I'm creating images array for UIImageView so I can use [_myImageView startAnimating]; method. I figuree out that if I cache(preload) it like this way the animation is fluent.
But in Instruments I see, that game is deallocated but there is still too much memory allocated - I think there are some fragments of this UIGraphicsBeginContext stuff.
Am I using it right, is there some other way how to dealloc it?
Thanks in advance!
- (NSMutableArray *)generateCachedImageArrayWithFilename:(NSString *)filename extension:(NSString *)extension andImageCount:(int)count
{
_imagesArray = [[NSMutableArray alloc] init];
_fileExtension = extension;
_animationImageName = filename;
_imageCount = count;
for (int i = 0; i < count; i++)
{
NSString *tempImageNames = [NSString stringWithFormat:#"%#%i", filename, i];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:tempImageNames ofType:extension];
UIImage *frameImage = [self loadRetinaImageIfAvailable:imagePath];
UIGraphicsBeginImageContext(frameImage.size);
CGRect rect = CGRectMake(0, 0, frameImage.size.width, frameImage.size.height);
[frameImage drawInRect:rect];
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[_imagesArray addObject:renderedImage];
if (_isDoublingFrames)
{
[_imagesArray addObject:renderedImage];
}
else if (_isTriplingFrames)
{
[_imagesArray addObject:renderedImage];
[_imagesArray addObject:renderedImage];
}
NSLog(#"filename = %#", filename);
}
return _imagesArray;
}
- (UIImage *)loadRetinaImageIfAvailable:(NSString *)path
{
NSString *retinaPath = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:#"%##2x.%#", [[path lastPathComponent] stringByDeletingPathExtension], [path pathExtension]]];
if ([UIScreen mainScreen].scale == 2.0 && [[NSFileManager defaultManager] fileExistsAtPath:retinaPath] == YES)
return [[UIImage alloc] initWithCGImage:[[UIImage imageWithData:[NSData dataWithContentsOfFile:retinaPath]] CGImage] scale:2.0 orientation:UIImageOrientationUp];
else
return [UIImage imageWithContentsOfFile:path];
}
-(void) dealloc
{
[_imagesArray removeAllObjects];
_imagesArray = nil;
}
I had memory issues similar you wrote and what I would try out in your code
1) use #autoreleasepool
to wrap either the method or the content of the loop in #autoreleasepool { } to create a local pool which will be released after method is done. In your situation it seems to me wrapping the loop is better:
for (int i = 0; i < count; i++)
{
#autoreleasepool {
// the loop code
...
}}
2) kill [UIImage imageNamed]
Using imageNamed will prepare a cache (depending on the size of image) which will never be flushed out of the memory unless you reach a memory warning. Even if you set myImage.image = nil, the image is still in memory. XCode's Intruments tool Allocations view will show it nicely. What you can do is initialize UIImage like this, and iOS won't cache the image:
image = [UIImage imageWithContentsOfFile:fullpath];
You might use imageNamed in your code under loadRetinaImageIfAvailable.
Keep both two approach and let's check this out again!

How to check if downloaded PNG image is corrupt?

I am downloading multiple images from below code and saving to the DB. But for some images I am getting below error.
Error: ImageIO: PNG invalid distance too far back
Error: ImageIO: PNG incorrect data check
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSString *imgStr = [dict objectForKey:#"image"];
imgStr = [imgStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgStr]];
UIImage *image = [UIImage imageWithData:imgData];
dispatch_sync(dispatch_get_main_queue(), ^{
mYImageView.image = image;
});
});
How to check stored image is valid or not, so I can download image again?
For PNG images, check their first two bytes and last two bytes. Below is the method, hope it helps.
Thanks. :)
- (BOOL)isImageValid:(NSData *)data
{
BOOL val = YES;
if ([data length] < 4)
val = NO;
const char * bytes = (const char *)[data bytes];
if (bytes[0] != 0x89 || bytes[1] != 0x50)
val = NO;
if (bytes[[data length] - 2] != 0x60 ||
bytes[[data length] - 1] != 0x82)
val = NO;
return val;
}
The accepted answer by swati sharma works great. Here’s a Swift extension for anyone looking to do the same in Swift:
extension Data
{
/// Returns whether or not the data is for a valid PNG file.
var isValidPNG: Bool
{
guard self.count > 4 else { return false }
return self[0] == 0x89 &&
self[1] == 0x50 &&
self[self.count - 2] == 0x60 &&
self[self.count - 1] == 0x82
}
}

NSData to byte array (or array of integers)

I am working with a webservice which gives me an image as an array of integers. I have managed to convert this array into an NSData object and then into a UIImage using the code below.
Now I am in doubt how I will convert an image into a similar array.
The array looks like this:
[255,216,255,224,0,16,74,70,73,70,0,1,1,1,0,96,0,96,0,0,255,219,0,67,0,8,6,6,7,6,5,8,7,7,7,9,9,8,10,12,...]
This is the code I use to convert the above array to a UIImage.
+ (UIImage *)imageFromBytes:(NSArray *)bytes
{
unsigned char *buffer = (unsigned char *) malloc(bytes.count);
int i = 0;
for (NSDecimalNumber *num in bytes)
buffer[i++] = num.intValue;
NSData *data = [NSData dataWithBytes:buffer length:bytes.count];
free(buffer);
return [UIImage imageWithData:data];
}
So, I need to convert a UIImage into an array of integers (a byte array).
Can anybody point me in the right direction?
You can get the data for an image with:
UIImage *image = [UIImage imageNamed:#"image.png"];
NSData *data = UIImagePNGRepresentation(image);
If you use a JPG you can use UIImageJPEGRepresentation.
And then, extract the array of bytes:
NSUInteger len = [data length];
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, [data bytes], len);
free(byteData)
You can use also the method (void)getBytes:(void *)buffer length:(NSUInteger)length from NSData:
[data getBytes:&byteData length:len];

App crash when get fullResolutionImage

I try using my app ALAssetRepresentation.and when i loop om an images there are couple of image that crash the app
for(ALAsset *asset in _assets) {
NSMutableDictionary *workingDictionary = [[NSMutableDictionary alloc] init];
[workingDictionary setObject:[asset valueForProperty:ALAssetPropertyType] forKey:#"UIImagePickerControllerMediaType"];
ALAssetRepresentation *representation = [asset defaultRepresentation];
if (!representation) {
[workingDictionary release];
continue;
}
CGImageRef imageRef = [representation fullResolutionImage];//here the app crash
UIImage *img = [UIImage imageWithCGImage:imageRef];
if (!img) {
[workingDictionary release];
continue;
}
if (!img) {
[workingDictionary release];
continue;
}
[workingDictionary setObject:img forKey:#"UIImagePickerControllerOriginalImage"];
[workingDictionary setObject:[asset valueForProperty:ALAssetPropertyOrientation] forKey:#"orientation"];
[returnArray addObject:workingDictionary];
[workingDictionary release];
}
in this line i get crash without any msg:
CGImageRef imageRef = [representation fullResolutionImage];
This is the crash msg
Program received signal: “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
That is most likely due to running out of memory, how big are the images that cause the crash?
I had a similar problem and after hours of lookin for solution I found this - the best solution of too big Asset bug:
// For details, see http://mindsea.com/2012/12/18/downscaling-huge-alassets-without-fear-of-sigkill
#import <AssetsLibrary/AssetsLibrary.h>
#import <ImageIO/ImageIO.h>
// Helper methods for thumbnailForAsset:maxPixelSize:
static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count) {
ALAssetRepresentation *rep = (__bridge id)info;
NSError *error = nil;
size_t countRead = [rep getBytes:(uint8_t *)buffer fromOffset:position length:count error:&error];
if (countRead == 0 && error) {
// We have no way of passing this info back to the caller, so we log it, at least.
NSLog(#"thumbnailForAsset:maxPixelSize: got an error reading an asset: %#", error);
}
return countRead;
}
static void releaseAssetCallback(void *info) {
// The info here is an ALAssetRepresentation which we CFRetain in thumbnailForAsset:maxPixelSize:.
// This release balances that retain.
CFRelease(info);
}
// Returns a UIImage for the given asset, with size length at most the passed size.
// The resulting UIImage will be already rotated to UIImageOrientationUp, so its CGImageRef
// can be used directly without additional rotation handling.
// This is done synchronously, so you should call this method on a background queue/thread.
- (UIImage *)thumbnailForAsset:(ALAsset *)asset maxPixelSize:(NSUInteger)size {
NSParameterAssert(asset != nil);
NSParameterAssert(size > 0);
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGDataProviderDirectCallbacks callbacks = {
.version = 0,
.getBytePointer = NULL,
.releaseBytePointer = NULL,
.getBytesAtPosition = getAssetBytesCallback,
.releaseInfo = releaseAssetCallback,
};
CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(rep), [rep size], &callbacks);
CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef) #{
(NSString *)kCGImageSourceCreateThumbnailFromImageAlways : #YES,
(NSString *)kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithInt:size],
(NSString *)kCGImageSourceCreateThumbnailWithTransform : #YES,
});
CFRelease(source);
CFRelease(provider);
if (!imageRef) {
return nil;
}
UIImage *toReturn = [UIImage imageWithCGImage:imageRef];
CFRelease(imageRef);
return toReturn;
}

How can I get image file size which selected by UIImagePickerController?

I want to know that the image file size in IPhone PhotoAlbum which selected by UIImagePickerController.
I've tried this code with 1,571,299 byte jpeg image.
UIIamge *selectedImage = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
NSData *imageData;
if ( /* PNG IMAGE */ )
imageData = UIImagePNGReprensentation(selectedImage);
else
imageData = UIImageJPEGReprensentation(selectedImage);
NSUInteger fileLength = [imageData length];
NSLog(#"file length : [%u]", fileLength);
But when I run the code, it print 362788 byte.
Is there anybody who know this?
If you have code like this to take a picture:
UIImagePickerController *controller = [[[UIImagePickerController alloc] init] autorelease];
controller.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
controller.delegate = self;
[self presentModalViewController:controller animated:YES];
Then you can retrieve a file size of the picked image in the following way:
NSURL *assetURL = [info objectForKey:#"UIImagePickerControllerReferenceURL"];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
[library assetForURL:assetURL resultBlock:^(ALAsset *asset) {
NSLog(#"Size: %lld", asset.defaultRepresentation.size);
} failureBlock:nil];
If the source type is UIImagePickerControllerSourceTypeCamera, you must save the in-memory image to disk before retrieving its file size.
As some commenters have said, even if we assume the methodology is correct you are reprocessing the image anyway so the byte sizes will not match. I use the following method for JPG images, ymmv for PNG:
+ (NSInteger)bytesInImage:(UIImage *)image {
CGImageRef imageRef = [image CGImage];
return CGImageGetBytesPerRow(imageRef) * CGImageGetHeight(imageRef);
}
The above, as a commenter noted, does return the uncompressed size however.
here is in Swift 4
extension UIImage {
var memorySize: Int {
guard let imageRef = self.cgImage else { return 0 }
return imageRef.bytesPerRow * imageRef.height
}
}