0

I scanned an image (.tiff) with Macintosh millions of colors which means 24 bits per pixel. The scanned image that I am getting has the following attributes: size = 330KB and dimensions = 348 * 580 pixels. Since, there are 24 bits per pixel, the size should actually be 348 * 580 * 3 = 605KB.

Is there something incorrect? I also used this code to extract the image raw data from the url of the image scanned:

NSString * urlName = [url path];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:urlName];
NSData *imageData = [image TIFFRepresentation];
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)CFBridgingRetain(imageData), NULL);
CGImageRef imageRef =  CGImageSourceCreateImageAtIndex(source, 0, NULL);
NSUInteger numberOfBitsPerPixel = CGImageGetBitsPerPixel(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
NSUInteger width = CGImageGetWidth(imageRef);

From this code also, I am getting the same info about the width, height and number of bits per pixel in the image.

Basically, I have to use this image's information and reproduce it somewhere else in some other form, so if I'll not be able to get the correct the information, the final product is not reproducible. What can be wrong here?

P.S.: If some other information is needed to answer the question, then I'll be happy to provide that.

Sankalp
  • 2,796
  • 3
  • 30
  • 43

2 Answers2

1

The most common image formats jpg, png and tiff compress the image which is why the filesize is lower than w*h*bits per pixel.

JPEG use lossy compression, PNG use lossless compression and TIFF can be uncompressed, use lossy compression or lossless compression.

Lossy compression means that some color imformation is lost in the compression so the image won't look exactly like it did before compression but you can reduce the file size even further using lossy compression.

An example of lossless compression is run length encoding which basically means that if you have several consequtive pixels with the same color you just say that you have N pixels with value (R,G,B) instead of saying (R,G,B),(R,G,B),...,(R,G,B)

Simon
  • 6,293
  • 2
  • 28
  • 34
  • .tiff images can also be compressed – Simon Oct 03 '13 at 09:06
  • Ok. But, if the image has been compressed, when I see the info of the file in windows or mac, the dimensions are same i.e. `348 * 580`. The dimensions should have been of the compressed image, isn't it? – Sankalp Oct 03 '13 at 09:08
  • The dimensions aren't changed during compression. What is changed is that you don't store the RGB value for each pixel but instead you find other ways of representing the colors of the pixels in the image. – Simon Oct 03 '13 at 09:14
  • Ok. Got it. So, could you tell me any method of decompressing the `.tiff` image so that I can reproduce it correctly? – Sankalp Oct 03 '13 at 09:24
  • Your code will already get you decompressed data so you can indeed trust the pixel values you have there. – Simon Oct 03 '13 at 09:28
  • But, from the code, I am getting same values as I mentioned.. but, since, you said that the dimensions do not change, so the decompressed image should give me correct size of the image i.e. 605KB. But, that is not the case.. I am getting the size of the image as 330KB only. I am calculating the size of the image by: `int size = [imageData length];` – Sankalp Oct 03 '13 at 09:35
1

A few days ago you asked the (nearly) same question and got no answers. I had not the time to answer your questions but now it is time to write some remarks.

First of all your question(s) and (most of) the answers and comments show a big misunderstanding of NSImage, NSImageRep and the image stored in the filesystem.

The image stored in the filesystem is a complicated data structure which not only contains all the pixels of an image (if it is a raster image) but also a lot of metadata: comments, some dates, informations about the camera, thumbnail images and all this sometimes in different formats: exif, photoshop, xml etc. So you cannot assume that the size of the file has something to do with the image in the computer to be depicted on the screen or to be asked for some special properties. To get these data for further usage you can do:

NSData *imgData = [NSData dataWithContentsOfURL:url];

or

NSData *imgData = [NSData dataWithContentsOfFile:[url path]];

or you directly load an image as an object of NSImage:

NSImage *image = [[NSImage alloc] initWithContentsOfURL:url];  // similar methods:see the docs

And and if you now think this is the file image data transformed into a Cocoa structure you are wrong. An object of the class NSImage is not an image, it is simply a container for zero, one or more image representations. Gif, jpg, png images have always only one representation, tiff may have one ore more and icns have about 5 or 6 image representations.

Now we want some information about the image representations:

for( NSUInteger i=0; i<[[image representations] count]; i++ ){
   // let us assume we have an NSBitmapImagedRep
   NSBitmapImageRep *rep = [[image representations] objectAtIndex:i];
   // get informations about this rep
   NSUInteger pixelX = [rep pixelsWide];
   NSUInteger pixelY = [rep pixelsHigh];
   CGFloat sizeX = [rep size].width;
   CGFloat sizeY = [rep size].height;
   CGFloat resolutionX = 72.0*pixelX/sizeX;
   CGFloat resolutionY = 72.0*pixelY/sizeY;

   // test if there are padding bits per pixel
   if( [rep bitsPerSample]>=8 ){
       NSInteger paddingBits = [rep bitsPerPixel] - [rep bitsPerSample]*[rep samplesPerPixel];

   // test if there are padding bytes per row
   NSInteger paddingBytes = [rep bytesPerRow] - ([rep bitsPerPixel]*[rep pixelsWide]+7)/8;

   NSUInteger bitmapSize =  [rep bytesPerRow] * [rep pixelsHigh];
}

Another remark: you said:

I scanned an image (.tiff) with Macintosh millions of colors which means 24 bits per pixel.

No, that need not be so. If a pixel has only three components it may not only use 24 but sometimes 32 bits because of some optimization rules. Ask the rep. It will tell you the truth. And ask for the bitmsapFormat! (Details in the doc).

Finally: you need not use the CG-functions. NSImage and NSImageRep do it all.

Heinrich Giesen
  • 1,765
  • 1
  • 11
  • 8
  • +1 Thanks for the information! And I would definitely check the number of representations idea that you have presented. But, coming back to my question, I need to decompress the image to get the exact information contained in it. Converting it into `NSImage` or `NSData` will not do it. Do you have any ideas there? – Sankalp Oct 03 '13 at 16:33
  • And regarding `number of bits per pixel`, I am not just assuming it. This line of code, `NSUInteger numberOfBitsPerPixel = CGImageGetBitsPerPixel(imageRef);`, also returns 24. – Sankalp Oct 03 '13 at 16:36
  • I also checked by using your code, and it turns out that there is only **one representation** of the image, so all the data remains the same. The basic problem for difference in size is that the image as input is compressed and it's needed to be decompressed. So, overall, I require some algo or an API to decompress the image at hand. – Sankalp Oct 03 '13 at 16:51
  • I do not understand. The data within an NSBitmapImageRep are decompressed. The data in the bitmap represent exactly the bits and bytes you see on the screen and the size is [rep bytesPerRow] * [rep pixelsHigh]. The method TIFFRepresentation creates data in TIFF format which are good for writing to a file but nothing else. – Heinrich Giesen Oct 03 '13 at 19:05
  • I understand that the image dimensions can be used to calculate the size, but I need to get my hands dirty on the decompressed image data. Are you saying that I can get the decompressed from `NSBitmapImageRep` itself? – Sankalp Oct 03 '13 at 19:11
  • You can get the data (decompressed and stored byte by byte) with: – Heinrich Giesen Oct 03 '13 at 19:14
  • You can get the data (decompressed and stored byte by byte) with ... ? – Sankalp Oct 03 '13 at 19:19
  • You can get the data (decompressed and stored byte by byte) with: unsigned char *bitmapData = [rep bitmapData]; a simple C structure, a sequence of bytes with [rep bytePerRow] a line and 3 or 4 bytes per pixel. The first 3 (or 4) bytes are the data of the very first pixel. So bitmapData[0] is the red component of the fist pixel and bitmapData[1] is the corresponding green value. – Heinrich Giesen Oct 03 '13 at 19:22
  • If this is the case, then there could be nothing wonderful than this! I'll check if this works or not tomorrow, since this is seeming too good to be true! It's because the file that I have as input is compressed by some TIFFCompression and if a single line of code that calls some in-built API does the trick of decompressing it, then it's awesome. – Sankalp Oct 03 '13 at 19:37
  • Hey! I had to make some changes in my code and then it worked like a charm! Can you tell me how can I make you bountiful ("+100")? Also, it would be of great help if you could tell me how to decompress a `.jpeg`? – Sankalp Oct 04 '13 at 05:07
  • I got to know of the problem that I am having in case of `.jpeg`. For this file format, I am getting number of padding bits as 8. So, the final output is not what I wanted, instead it's garbled. What should I do to get a work-around over the padding bits and why specifically does it have padding bits? – Sankalp Oct 04 '13 at 06:11