convert ciimage to writable format - objective-c

I need to convert CIImage to a format which could be written to disk.
Currently I am using the following code to convert it to JPG format .
NSBitmapImageRep* rep = [[[NSBitmapImageRep alloc] initWithCIImage:result] autorelease];
NSData *JPEGData = [rep representationUsingType:NSJPEGFileType properties:nil];
[JPEGData writeToFile:targetPath atomically:YES];
But the real memory usage shoots up to above 100 MB . My application requires me to handle a large number of images so i need to optimise my memory usage.
Can anyone please suggest anything ???

If it's the cumulative memory that's an issue, and not just one image, you can try wrapping the two last lines in your own autorelease pool:
#autoreleasepool {
NSData *JPEGData = [rep representationUsingType:NSJPEGFileType properties:nil];
[JPEGData writeToFile:targetPath atomically:YES];
}

Related

Appending random bytes to an image doesn't change the outcome?

So to simplify the situation I want to append a couple random bytes to an image to change the MD5 hash every time.
I have the code set up to look up the image then create an NSImage. After that it converts the NSImage to NSMutableData which offers me the opportunity to append random bytes. I then end it all by exporting the altered NSImage to the desktop.
That all works fine and dandy until I run my program twice and compare the MD5 hashes of the two outputs. They are exactly the same! It doesn't matter if I append 1 or 1000 random bytes, if you compare the two outputs, it is exactly the same to each other.
My code:
- (void)createNewImage:(NSString *)filePath {
// NSImage from path
NSImage *newImage = [[NSImage alloc]initWithContentsOfFile:filePath];
// NSData to NSMutableData
NSData *imgData = [newImage TIFFRepresentation];
NSMutableData *mutableData = [imgData mutableCopy];
// Get the random bytes
NSData *randomData = [self createRandomBytes:10];
// Append random data to new image
[mutableData appendData:randomData];
(etc...)
// Create file path for the new image
NSString *fileName = #"/Users/Computer/Desktop/MD5/newImage.jpg";
// Cache the new image
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:mutableData];
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor];
NSData *newData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
[newData writeToFile:fileName atomically:NO];
}
-(NSData *)createRandomBytes:(NSUInteger)amount {
return [[NSFileHandle fileHandleForReadingAtPath:#"/dev/random"] readDataOfLength:amount];
}
UPDATE:
With the help of picciano, I found that exporting the edited NSData directly manages to achieve my goal
[mutableData writeToFile:fileName atomically:NO];
HOWEVER, the image is significantly larger. The source image is 182 KB while the new images are 503 KBs. picciano's answer explains why this happens but does anyone happen to have a workaround to the inflation?
You are adding random data, but it is not being used in creating the image. When the image is converted back to a JPG data representation, only the valid portion of the image data is used.
To verify this, check the length of your newData object.

How to run multiple task at the same time safely?

I'm developing a iPhone app and I'm relatively new to objective-c so I hope some one can give a clue.
What im doing is reading a file in chunks and encoding the chuncks into base64 and everything is working fine, the problem is that in this line NSString *str = [data base64EncodedString]; it takes a little bit of time because im encodeing chunks of 256KB, there is no problem with one file the problem is that i'm encoding image files so imagine that I encode 10 images it will be alot of chunks per image so the process can be slow.
this is the process:
*Get the file.
*Read chunck of 256KB of the file.
*Encode chunck to base64.
*Save the encoded chunck and repeat until there is no more bytes to read from the file.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:referenceURL resultBlock:^(ALAsset *asset)
{
NSUInteger chunkSize =262144;
uint8_t *buffer = calloc(chunkSize, sizeof(*buffer));
ALAssetRepresentation *rep = [asset defaultRepresentation];
NSUInteger length = [rep size];
self.requestsToServer=[[NSMutableArray alloc]init];
NSUInteger offset = 0;
do {
NSUInteger bytesCopied = [rep getBytes:buffer fromOffset:offset length:chunkSize error:nil];
offset += bytesCopied;
NSData *data = [[NSData alloc] initWithBytes:buffer length:bytesCopied];
NSString *str = [data base64EncodedString];
//After this I add the str in a NSMutableURLRequest and I store the request
//in a NSMutableArray for later use.
} while (offset < length);
free(buffer);
buffer = NULL;
}
failureBlock:^(NSError *error)
{
}];
I want to start another thread so I can be encoding the chuncks in paralel and know when the process finish, this way while encoding one chunck I can be encodign another 3 or 4 chunks at the same time.
How I can implement this in a safely way or is this a good idea?
Thanks for your time.
Look at NSOperation and NSOperationQueue.
http://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html
Simply create one NSOperation per chunk and pass them the chunk they need to encode and queue them up.
You can tell the queue how many operations can run simultaneously.
There are lots of good options for doing chunks of work in parallel for iOS.
Take a look at the Apple's Concurrency Programming Guide to get you going.

Memory issue with [NSData dataWithContentsOfFile]

I am developing application which required image caching. For doing this, I am using JMImageCache library. It is work fine for caching. But It can not release memory occupied by
following line.
[NSData dataWithContentsOfFile]
Here, is function which content code for cache image from disk.
- (UIImage *) imageFromDiskForURL:(NSString *)url {
NSData *data = [NSData dataWithContentsOfFile:cachePathForURL(url) options:0 error:NULL];
UIImage *i = [[[UIImage alloc] initWithData:data] autorelease];
data = nil;
[data release];
return i;
}
I have check it with instruments and it alloc 2.34 MB each time.
data = nil;
[data release];
Why do you expect this at all to work? Why should this release the original data? You're sending the release message to nil, which is a no-op.
Furthermore, if you don't create the object using alloc or copy, then it's autoreleased. That means if you release it once more, it will be overreleased and most likely your app is going to crash. What you need is:
One. Wrap the method call in an explicit autorelease pool:
- (UIImage *)imageFromDiskForURL:(NSString *)url
{
UIImage *i;
#autoreleasepool {
NSData *data = [NSData dataWithContentsOfFile:cachePathForURL(url) options:0 error:NULL];
i = [[UIImage alloc] initWithData:data];
}
return [i autorelease];
}
Two, alloc-init or manually release the data object:
- (UIImage *)imageFromDiskForURL:(NSString *)url
{
NSData *data = [[NSData alloc] initWithContentsOfFile:cachePathForURL(url) options:0 error:NULL];
UIImage *i = [[[UIImage alloc] initWithData:data] autorelease];
[data release];
return i;
}
alter the sequence nil and release
[data release];
data = nil;
and for clearing the cache use following delegate methods
[[JMImageCache sharedCache] removeAllObjects];
[[JMImageCache sharedCache] removeImageForURL:#"http://dundermifflin.com/i/MichaelScott.png"];
read the read me file of library
https://github.com/jakemarsh/JMImageCache/blob/master/README.markdown
What you are trying to do cannot work due to the way how UIImage uses a data you pass it. The data object is retained by the image, or more precisely by a CGImageSource that the UIImage has internally. From this data it is able to decompress and create the image any time. There is an option on CGImageSource to also keep around the decompressed data, but UIImage does not use that because it optimized for small UI graphics.
One thing that you can do to alleviate memory pressure is to not load the entire NSData into memory, but to memory-map it instead. This makes creation or recreation of the image a tad slower, but the created NSData object is very small in comparison.

Building NSImage from bytes

Im trying to build an NSImage from some strange bytes.
Im using BlackMagic SDK to get the bytes of a recieved frame:
unsigned char* frame3 = NULL;
unsigned char* frame2 = (Byte*)malloc(699840);
videoFrame->GetBytes ( (void**)&frame3);
memcpy(frame2, frame3, 699840);
NSData* data = [NSData dataWithBytes:frame2 length:sizeof(frame2) ];
NSImage *image = [[NSImage alloc] initWithData:data];
//(till now i use statically 699840, because i know its size)
Why i said the bytes are strange is that the content of the "frame2" looks like this:
printf("content: %s",frame2);
\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200\200.........\200 (to the end)
It should be blank black frame.
Does somebody know how could I figure out something with this?
You should use these apis to get an image from data bytes.
NSString *filePath = [yourDirectory stringByAppendingPathComponent:#"imageName.jpg"];
[data writeToFile:filePath atomically:YES];

Write to file not working

I'm trying to combine images in my app into one file and write it to disk.
NSMutableArray *array = [NSMutableArray arrayWithObjects:
[NSData dataWithContentsOfFile:#"0.png"],
[NSData dataWithContentsOfFile:#"1.png"],
[NSData dataWithContentsOfFile:#"2.png"],
nil];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSError *error = nil;
NSString *path=#"/Users/myusername/Desktop/_stuff.dat";
[data writeToFile:path options:NSDataWritingAtomic error:&error];
or
NSArray *array = [NSArray arrayWithObjects:
[NSImage imageNamed:#"0"],
[NSImage imageNamed:#"1"],
[NSImage imageNamed:#"2"],
nil];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSError *error = nil;
NSString *path=#"/Users/myusername/Desktop/_stuff.dat";
[data writeToFile:path options:NSDataWritingAtomic error:&error];
But both produce a file that is 4KB (empty). If I NSLog the error it is (null). Am I making the data the wrong way?
Edit: If I open the resulting file with a text editor, it looks like this:
I wrote a quick example:
Missing: memory management / error handling / proper file handling
// Archive
NSMutableArray *array = [[NSMutableArray alloc] init];
NSString * input = #"/Users/Anne/Desktop/1.png";
[array addObject:[NSData dataWithContentsOfFile:input]];
[array addObject:[NSData dataWithContentsOfFile:input]];
[array addObject:[NSData dataWithContentsOfFile:input]];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSString *path = #"/Users/Anne/Desktop/archive.dat";
[data writeToFile:path options:NSDataWritingAtomic error:nil];
// Unarchive
NSMutableArray *archive = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSData * firstObject = [archive objectAtIndex:0];
NSString * output = #"/Users/Anne/Desktop/2.png";
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:output];
[firstObject writeToURL:fileURL atomically:YES];
You can also add NSImages to the NSMutableArray:
NSString * input = #"/Users/Anne/Desktop/1.png";
NSImage *image = [[NSImage alloc] initWithContentsOfFile: input];
[array addObject:image];
But that will significantly increase the file size.
Response to the following comment:
So if I only need to access an image at runtime (in the archive), is there a way to access that image at an index without unarchiving the whole thing? Seems like unnecessary overhead to me.
I assume you're still struggling with this problem?
Hiding (or encrypting) app resources?
Like i mentioned earlier, combining all files into one big file does the trick.
Just make sure you remember the file-length of each file and file-order.
Then you can extract any specific file you like without reading the whole file.
This might be a more sufficient way if you only need to extract one file at the time.
Quick 'dirty' sample:
// Two sample files
NSData *fileOne = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/1.png"];
NSData *fileTwo = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/2.png"];
// Get file length
int fileOneLength = [fileOne length];
int fileTwoLength = [fileTwo length];
// Combine files into one container
NSMutableData * container = [[NSMutableData alloc] init];
[container appendData:fileOne];
[container appendData:fileTwo];
// Write container to disk
[container writeToFile:#"/Users/Anne/Desktop/container.data" atomically:YES];
// Read data and extract sample files again
NSData *containerFile = [NSData dataWithContentsOfFile:#"/Users/Anne/Desktop/container.data"];
NSData *containerFileOne =[containerFile subdataWithRange:NSMakeRange(0, fileOneLength)];
NSData *containerFileTwo =[containerFile subdataWithRange:NSMakeRange(fileOneLength, fileTwoLength)];
// Write extracted files to disk (will be exactly the same)
[containerFileOne writeToFile:#"/Users/Anne/Desktop/1_extracted.png" atomically:YES];
[containerFileTwo writeToFile:#"/Users/Anne/Desktop/2_extracted.png" atomically:YES];
// Only extract one file from the container
NSString * containerPath = #"/Users/Anne/Desktop/container.data";
NSData * oneFileOnly = [[NSFileHandle fileHandleForReadingAtPath:containerPath] readDataOfLength:fileOneLength];
// Write result to disk
[oneFileOnly writeToFile:#"/Users/Anne/Desktop/1_one_file.png" atomically:YES];
Tip:
You can also save the 'index' inside the container file.
For example: The first 500 bytes contain the required information.
When you need a specific file: Read the index, get the file position and extract it.
You are archiving a NSMutable array of NSImage. This two classes conform to the NSCoding protocol required by NSKeyedArchiver, so I don't see where would be your problem.
So, here are many ideas to test.
First, are you sure that the data you think you have are valid? In your first code snippet, you write [NSData dataWithContentsOfFile:#"0.png"]. This method expects an absolute file path.
Assuming the problem is not in your code, just in your question, let's continue:
Do you have something different than nil in the variable data after your archiving? Ie, after the assignement to data, can you add this code. If the assertion fail, you will get an exception at runtime:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSAssert(nil != data, #"My object data is nil after archiving");
If the problem was not here, what is the return of the line [data writeToFile:path options:NSDataWritingAtomic error:&error];
(Not the variable error, but the return value of the call to the method - writeToFile: options: error:)
What happens if you simplify your code and just do this:
result = [NSKeyedArchiver archiveRootObject:data
toFile:archivePath];
If everything was ok, have you tried to unarchive your file with NSKeyedUnarchiver?
The problem is that [NSData dataWithContentsOfFile:#"0.png"] looks for the file "0.png" in the current directory, but what the application thinks of as the current directory is probably not the place you're expecting. For graphical apps, you should always either use an absolute path or a path relative to some place that you can get the absolute path of (e.g. your app bundle, the application support directory, some user-selected location).
For command-line tools, using the current directory is more common. But I doubt that's the case here.
Another thing I noticed on Mavericks and up is that the folders in the path must be in existence. Meaning you must create the folder structure prior to saving into that folder. If you try to write to a folder on the desktop or elsewhere, even with sandboxing off, it will fail if the folder does not exist. I know this has been answered already, but I found that my issue continued regardless, but once I make sure that the folder structure was in place, I could do my writing to that folder.
On a side note: I'm sure that you could do this from NSFileManager, and I'll be doing that myself once I finalize my app structure, but hope this helps someone else lost in the sauce.