.NET Forum / Languages / C# / February 2008
struggling to save a tiff as a PNG and keep filesize down
|
|
Thread rating:  |
sklett - 28 Feb 2008 21:08 GMT I have a Tiff (fax) with the following properties: width: 1728 height: 1090 x resolution: 204 y resolution: 98 bit depth: 1
If I open this tiff in Photoshop and change the resolution to 96 and resize to 816 x 1056, then save as png the size is 3.1Kb (great!)
If I resize the tiff with gdi+ and save as png (same setting as Photoshop) the filesize is 31Kb (bad!!!)
Here is the code I'm using to resize and save as png: <code> Image img = Bitmap.FromFile("../../2109790117_080129_77862164.tif"); if(img.HorizontalResolution != img.VerticalResolution) { const float resolution = 96F;
// get the physical dimensions of the document SizeF size = new SizeF(img.Width / img.HorizontalResolution, img.Height / img.VerticalResolution); Size pixelDimensions = new Size((int)(size.Width * resolution), (int)(size.Height * resolution));
Bitmap newImage = new Bitmap(pixelDimensions.Width, pixelDimensions.Height); newImage.SetResolution(resolution, resolution); using(Graphics g = Graphics.FromImage(newImage)) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(img, new Rectangle(0, 0, pixelDimensions.Width, pixelDimensions.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel); }
img = newImage; }
string fn = Guid.NewGuid().ToString() + ".png"; img.Save(fn, System.Drawing.Imaging.ImageFormat.Png); </code>
The image quality of the resize using GDI+ is much better than Photoshops. I'm using NearestNeighbor in both cases but the results are not the same.
If anyone can shed some light, offer some pointers, whatever I would really appreciate it. I need to keep the filesize as small as possible.
Thanks, Steve
Peter Duniho - 28 Feb 2008 21:51 GMT > I have a Tiff (fax) with the following properties: > width: 1728 [quoted text clipped - 10 lines] > Photoshop) > the filesize is 31Kb (bad!!!) The most obvious thing I see is that the image you create isn't a 1 bpp image as the original is. Perhaps Photoshop is preserving that 1 bpp color depth, whereas your code is not.
What happens if you create the new image instance as "new Bitmap(pixelDimensions.Width, pixelDimensions.Height, PixelFormat.Format1bppIndexed)"?
Pete
sklett - 28 Feb 2008 21:57 GMT >> I have a Tiff (fax) with the following properties: >> width: 1728 [quoted text clipped - 18 lines] > Bitmap(pixelDimensions.Width, pixelDimensions.Height, > PixelFormat.Format1bppIndexed)"? Hi Peter,
I thought of that as well, but when I try to use a 1bpp image and create a Graphics instance from it I get the following exception: "A Graphics object cannot be created from an image that has an indexed pixel format."
Photoshop is created a 32 BPP image. Very strange.
Thanks for the suggestion though! -Steve
Peter Duniho - 28 Feb 2008 22:15 GMT > [...] >> What happens if you create the new image instance as "new [quoted text clipped - 9 lines] > pixel > format." Ah, right. I forgot about that limitation.
You could resize the image into a 24/32bpp image and then use LockBits() on that and a 1 bpp image to copy the data directly.
> Photoshop is created a 32 BPP image. Very strange. Are you sure it is? 3K seems awfully small for a file that really was saved as 32 bpp. At 800 x 1000, you're looking at over 3MB of data for a 32 bpp image. If so, that'd mean that PNG compression is achieving a 1000-to-1 compression ratio when you save from Photoshop.
Even JPEG wouldn't normally be able to save an image of that dimension without practically destroying the image, and PNG being lossless shouldn't be able to come anywhere close to that for a full-color image of any reasonable complexity (even the best-case scenarios that one might consider are unlikely IMHO and those require careful matching of the input data to the specific PNG compression algorithm being used).
It's hard to comment precisely without having a copy of the original bitmap and of the Photoshop-created image. But I still suspect this is a color depth issue.
Pete
Steve K. - 28 Feb 2008 23:20 GMT >> [...] >>> What happens if you create the new image instance as "new [quoted text clipped - 21 lines] > 32 bpp image. If so, that'd mean that PNG compression is achieving a > 1000-to-1 compression ratio when you save from Photoshop. I agree, it "smells" a bit. This is not a full color image though, the source TIFF is a 1BPP image. I will check this some additional ways and see what I can find out.
> Even JPEG wouldn't normally be able to save an image of that dimension > without practically destroying the image, and PNG being lossless shouldn't [quoted text clipped - 6 lines] > bitmap and of the Photoshop-created image. But I still suspect this is a > color depth issue. If we can't resolve this through the NG and you would like the images I would be happy to send to you ;0)
Thanks for your continued efforts to help me! -Steve
Peter Duniho - 29 Feb 2008 06:15 GMT > [...] >> Are you sure it is? 3K seems awfully small for a file that really was [quoted text clipped - 6 lines] > source TIFF is a 1BPP image. > I will check this some additional ways and see what I can find out. It's true, it's possible that with the 32 bpp image only have two colors PNG is able to compress it dramatically. But you should definitely confirm that's what's going on, given the disparity from what .NET is doing.
The only other thing I can think of is that Photoshop is doing a more elaborate analysis of the data. PNG offers a surprisingly wide variety of compression options, mainly because how well a particular algorithm will compress an image depends a lot on the data in the image. Fancier PNG compression tools (which may include Photoshop) will run multiple compressions, using varying options, and choose the best-compressing output.
I doubt that .NET does any sort of comparative compression like this, and probably just uses some "best average case" options for its compression. If that's the reason for the difference, then you're out of luck with respect to using the built-in .NET PNG compression.
Other options would include implementing a PNG compressor yourself (non-trivial, but not too hard either since the PNG org web site has links to sample code), or use some third-party library (I know of at least one command-line utility that will do fancier compression...you could access the command-line utility from your .NET application to handle the compression).
But before you do all that, you really should confirm that you're comparing apples to apples. It would be a shame to do all that work for nothing.
Finally, another possibility: you mentioned that Photoshop doesn't resize the image with as high quality as you're getting from .NET. It's possible that due to the lower quality, the image Photoshop is compressing really is much more highly compressable than the image .NET is compressing. An interesting test would be to open the image Photoshop generates (just read the PNG file in as an Image instance), and then resave that as a new PNG. That way you know for sure you're compressing the same exact data Photoshop is.
That would be another "verify apples to apples" check you could make before pursuing alternatives to using .NET's built-in PNG functionality.
> [...] >> It's hard to comment precisely without having a copy of the original [quoted text clipped - 4 lines] > If we can't resolve this through the NG and you would like the images I > would be happy to send to you ;0) If you want to share the files, I recommend any of several free file upload sites that you can use. That way you can post the link and easily share the files with whomever thinks they might be able to help, without any extra effort on your part and without inefficiently transmitting the files via email.
Here are three that I think work pretty well: http://www.filecrunch.com/ http://www.sendspace.com/ http://www.yousendit.com/
They all allow the upload of files for download by others without any requirement to register or share email addresses (at least one makes it look like they require an email address, but it's really optional, as with the others).
> Thanks for your continued efforts to help me! Well, I'm always happy to speculate. I wish I had more specific information. And sadly, just based on replies I've seen to other posts regarding PNG questions (of which there haven't been many, granted), I have some of the most experience with that image file format out of any of the folks who normally reply. And that's not encouraging news for you, as I don't really have that much experience with it. :(
Sorry. :) But I'll keep trying to help if I come up with new ideas. :)
Pete
sklett - 29 Feb 2008 20:25 GMT > The only other thing I can think of is that Photoshop is doing a more > elaborate analysis of the data. PNG offers a surprisingly wide variety of [quoted text clipped - 3 lines] > compressions, using varying options, and choose the best-compressing > output. Hi Peter,
My latest research seems to indicate that the PNG saved from PS is indeed a 1BPP even though Windows reports it's a 32BPP. I checked this by opening the PNG back into PS and it shows the "Mode" as "Bitmap".
The exact operations that I'm performing on the source TIFF in PS are: 1) open TIFF file 2) resize image to 8.5" x 11" with interpolation mode: Nearest Neighbor 3) Change resolution to 96 4) save as PNG
The resizing is required because the "Pixel Aspect ratio" is not square because the source TIFF resolution is 204 * 98
The .Net code to mimic what I'm doing with PS is <code> Image img = Bitmap.FromFile("../../NA_080129_77887111.tif"); if(img.HorizontalResolution != img.VerticalResolution) { const float resolution = 96F;
SizeF size = new SizeF(8.5F, 11.0F); Size pixelDimensions = new Size((int)(size.Width * resolution), (int)(size.Height * resolution));
Bitmap newImage = new Bitmap(pixelDimensions.Width, pixelDimensions.Height); newImage.SetResolution(resolution, resolution); using(Graphics g = Graphics.FromImage(newImage)) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(img, new Rectangle(0, 0, pixelDimensions.Width, pixelDimensions.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel); }
img = newImage; }
string fn = "../../" + Guid.NewGuid().ToString() + ".png"; img.Save(fn, System.Drawing.Imaging.ImageFormat.Png); </code>
I have created a comparison of a specific (zoomed way in) section of the two png files http://www.pmddirect.com/temp/gdi+%20and%20PS%20comparison%20copy.png
When I open the .net saved PNG in PS, the "Mode" is RGB Color/ 8 Bits
SO! That is a great clue, not sure how I missed this before, apologies if I've had you barking up the wrong tree.
At this point, I would say my whole problem/challenge is getting .Net to save a 1BPP png and I bet I would have results on par with that of PS.
> I doubt that .NET does any sort of comparative compression like this, and > probably just uses some "best average case" options for its compression. [quoted text clipped - 7 lines] > the command-line utility from your .NET application to handle the > compression). I will look into this, sounds a bit daunting but might be my only option.
> But before you do all that, you really should confirm that you're > comparing apples to apples. It would be a shame to do all that work for [quoted text clipped - 47 lines] > > Sorry. :) But I'll keep trying to help if I come up with new ideas. :) Again, thanks for the great help! I really appreciate it. I will do some research and try to find out if it's possible to save a 1BPP png from code. I would rather not resort to an external application if I can avoid it.
Gotta run to a meeting, I will update this thread with any additional info I find in a few hours.
-Steve
Peter Duniho - 29 Feb 2008 22:21 GMT > [...] > I have created a comparison of a specific (zoomed way in) section of the [quoted text clipped - 3 lines] > > When I open the .net saved PNG in PS, the "Mode" is RGB Color/ 8 Bits Not to mention the doc sizes. Almost 2.5Mb for the one saved from .NET, and only 100K for the one from Photoshop. :)
> SO! That is a great clue, not sure how I missed this before, apologies > if > I've had you barking up the wrong tree. Not really. I suspected a bit-depth problem all along. :)
> At this point, I would say my whole problem/challenge is getting .Net to > save a 1BPP png and I bet I would have results on par with that of PS. I would hope so. There's no guarantee, I think it's likely.
Of course, all that said, even the 100-to-1 compression you're getting now isn't too shabby. Obviously, getting the file size down to 3K from 30K would be great. That 10X savings is definitely significant. But there's theoretically a lot of information in that 800x1000(-ish) image, and keeping all that information in just 30K is pretty good.
> [...] > Again, thanks for the great help! I really appreciate it. I will do > some > research and try to find out if it's possible to save a 1BPP png from > code. > I would rather not resort to an external application if I can avoid it. For your first test, you should see if you can load a monochrome image into .NET and have it preserve the bit depth. If .NET does, rather than upscaling the color resolution to 24 or 32 bpp, then you have a 1 bpp image you can test saving. If saving that 1 bpp image works, then you can spend more time learning how to actually create the necessary 1 bpp image with Bitmap.LockBits().
Those two tests, you should be able to code in five or ten minutes. They'll give you good information as to whether it's worth pursuing the LockBits() solution, which could take longer (I'd guess an hour or two for someone who's unfamiliar with LockBits() but knows bitmap formats generally, and perhaps anywhere from two to ten times as long or longer for someone brand new to the whole topic, depending on their general abilities).
In particular, if you can create a 1 bpp image in memory with those test, but saving that image doesn't help, then it won't be worth your effort to bother figuring out LockBits().
Pete
not_a_commie - 29 Feb 2008 15:50 GMT > Are you sure it is? 3K seems awfully small for a file that really was > saved as 32 bpp. At 800 x 1000, you're looking at over 3MB of data for a [quoted text clipped - 7 lines] > consider are unlikely IMHO and those require careful matching of the input > data to the specific PNG compression algorithm being used). Huh? JPEG is for photos and nothing else. Don't try to use that on your screen shot or your 1bpp image. PNG uses a variety of compression mechanisms. It chooses the best for your current line in the image. So if you line is all one color it will use RLE compression. Instead of being 1728 integers in data it will be two intigers in data: one with 1728 and the next with the color. That's a 1728-to-2 compression with no hard work at all. And if it can use 16bit values for color and run length it would be smaller yet, etc.
Peter Duniho - 29 Feb 2008 18:17 GMT > Huh? JPEG is for photos and nothing else. Don't try to use that on > your screen shot or your 1bpp image. No one is suggesting that he does.
> PNG uses a variety of compression > mechanisms. It chooses the best for your current line in the image. So > if you line is all one color it will use RLE compression. Instead of > being 1728 integers in data it will be two intigers in data: one with > 1728 and the next with the color. That's a 1728-to-2 compression with > no hard work at all. Yes, for a particular kind of data. And PNG supports handling that compression for horizontal lines or vertical lines (for example).
I already said that for special cases, PNG can compress very well. But those special cases don't usually hold true. Without knowing anything else about the image, the fact is that the chances of any random image being able to be compressed by 1000-to-1 is extremely low.
Pete
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|