views:

195

answers:

7

I have a screen shot I take in my mobile app. The screen shot takes about 32 KB when saved as a png on a disk.

I am sending these to a central SQL Server and 32 KB is too big for that amount of times I will need to store that screen shot (approx 2500 times a day).

Is there any kind of trickery that I can do to get it to save smaller?

Here is the code I am using now to take it from Bitmap to bytes (to send to the server for storage):

MemoryStream stream = new MemoryStream();
 _signatureImage.Save(stream, ImageFormat.Png);
 return stream.ToArray();

_signatureImage is a Bitmap and is the screenshot in question.

Here is an example of the screen shot I am saving:

Screen Shot Image

Things that pop to mind (but I don't know how to do them):

  1. Reduce the actual Height and Width of the image (but hopefully in a way that will not distort it).
  2. Change it to a black and white image (not sure if I will see any real space savings from this)
  3. Compress it more (I don't like this as much because then it is not readable from the database).

Note, this all has to be done programatically, and cannot take very long, so complex image manipulations are out.

Thanks for any help.

+3  A: 

If color depth is not an issue, you might change it to black and white, or 16 color mode. There should be a significant savings over png's 24-bpp (or 32-bpp with alpha) storage requirements. The other thing to think about is saving it as a .gif file in 256 color mode.

As you're saving signature data, I'd think changing the dimensions of the image would be a bad idea. You might also consider applying a lossless compression like zip, but that is likely more computationally intensive than you require.

arootbeer
Any hints on how to do this in C#?
Vaccano
arootbeer
My company's product, DotImage Photo, can do this and is a free SDK. Just call GetChangedPixelFormat() on an AtalaImage -- pass in an indexed format, then save with AtalaImage.Save(). https://www.atalasoft.com/photofree
Lou Franco
Sorry, just noticed that you need CE -- won't work there.
Lou Franco
A: 

Try converting to a pure monochrome bitmap. These are extremely small, since they only use one bit per pixel.

EDIT: I'm a liar. Just tried this in Paint and it's about 40k compared to the original 20k. I could have sworn those had massive space savings when I used them before...

EDIT EDIT: OK, apparently they do have massive savings. It's just that for these images, PNG has even more massive savings. Just tried a 16-color bitmap instead and that blew it up to 150k.

EDIT EDIT EDIT: BUT, the monochrome bitmap zips to 9k bytes, while the PNG stays at 20k even zipped.


So, it looks like if you want to go the compression route, you can first convert to monochrome bitmap, then compress for images around half the size of the PNG.

You could also strip some of the information out of the image. For instance, on that screen, the only thing that really needs to remain an image is the signature. The bars above and below that could be ripped out, and the numeric information could be sent as such and stored separately.

jdmichal
I'm not sure exactly how windows stores its bitmap files, but I do know that they are completely uncompressed. .png at least has some compression.
arootbeer
+2  A: 
    private static Image ResizeImage(int newSize, Image originalImage)
    {
        if (originalImage.Width <= newSize)
            newSize = originalImage.Width;

        var newHeight = originalImage.Height * newSize / originalImage.Width;

        if (newHeight > newSize)
        {
            // Resize with height instead
            newSize = originalImage.Width * newSize / originalImage.Height;
            newHeight = newSize;
        }

        return originalImage.GetThumbnailImage(newSize, newHeight, null, IntPtr.Zero);
    }

This should work with your Bitmap object Type and resize the Height or Width, depending on which is appropriate for your image dimensions. It will also maintain scale.

EDIT:

You could create a new Bitmap object and resize your original image into that Bitmap object.

Bitmap b = new Bitmap(newWidth, newHeight);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;

g.DrawImage(imgToResize, 0, 0, newWidth, newHeight);
g.Dispose();

return (Image)b;

I don't have the Compact Framework installed, but it seems that this should work for you.

pattertj
That looks good. But I can't easily use it. I should have indicated this, but this app is on the Compact Framework and GetThumbnailImage is not in the compact framework. If it comes down to it, I can re-size the image on the server side, but I would rather send a smaller image over the air if possible.
Vaccano
A: 

Firing up Fireworks with a photo, which won't be the same but should give an idea

66% quality JPG with no smoothing is 17.5k
PNG8 with 256 colours is 58K (down to 42 using websnap palette)
PNG8 with black and white palette is 14k

Note that many image files will be smaller with a screen shot since there are large contiguous groupings of the same colour, and that may be especially true with black and white. It will probably also depend on the algorithm used to convert the image, but you may want to just go with whatever you can find...

Kendrick
+2  A: 

Change the color palette to a lower color depth. Take a look at this question (well look at the answert really) which shows converting to 1bpp. You could go to 8bpp instead of 1bpp and have significant size savings. The mechanism for conversion will be the same as what is shown.

EDIT

Thinking out of the box, you might also consider just sending the data points from the upper part of the screen and the signature vector, and have the server recreate the screen for you. If size really is a big issue, I'd probably investigate that.

EDIT 2

If simply resizing the image is a reasonable solution (you'll obviously lose data in doing so) then you can use the Imaging namespace in the SDF to create a thumbnail as Alex explains here.

ctacke
+1  A: 

If monochrome image is acceptable, you can try TIFF G4 (lossless compression). TIFF G4 is known for being very effective in file size to store black and white image. Unfortunately I dont have any info available on how it compares to PNG, but worth investigating into it.

Here is the info and example on how to do it in C#

Fadrian Sudaman
A: 

FWIW storing the image physically inside SQL server isn't an optimal solution. If you're using Sql Server 2008 you should look at the file stream support that will let you store the physical images on disk and access them as if they were internal to SQL Server. This would most likely alleviate your need to worry about filesize.

Chris Marisic