Page 1 of 1

Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T05:53:12-07:00
by jbb5044
I am having an issue converting images from png to an 8-bit bitmap with only 256 colors.
I need to do this conversion on an embedded device with a very small amount of RAM, only about 24MB free, and every command I try to do this results in an out of memory error:

Code: Select all

convert: Memory allocation failed `out.bmp'.
I have tried to do this in the simplest way I could find:

Code: Select all

convert in.png -channel RGB -depth 8 -colors 256 -compress none +matte out.bmp
Valgrind on my desktop computer indicates that this command uses 136.8MB of ram:

Code: Select all

==16095== Memcheck, a memory error detector
==16095== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==16095== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==16095== Command: convert in.png -channel RGB -depth 8 -colors 256 -compress none +matte out.bmp
==16095== 
==16095== 
==16095== HEAP SUMMARY:
==16095==     in use at exit: 328 bytes in 9 blocks
==16095==   total heap usage: 3,250 allocs, 3,241 frees, 137,563,383 bytes allocated
==16095== 
==16095== LEAK SUMMARY:
==16095==    definitely lost: 0 bytes in 0 blocks
==16095==    indirectly lost: 0 bytes in 0 blocks
==16095==      possibly lost: 0 bytes in 0 blocks
==16095==    still reachable: 328 bytes in 9 blocks
==16095==         suppressed: 0 bytes in 0 blocks
==16095== Rerun with --leak-check=full to see details of leaked memory
==16095== 
==16095== For counts of detected and suppressed errors, rerun with: -v
==16095== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
I have also tried simply extracting the unique colors from the input image, then providing that as a map to the conversion. This worked for a few bitmaps, but still often consumes over 130MB of RAM:

Code: Select all

convert in.png -unique-colors in.colors.bmp
convert in.png -channel RGB -depth 8 -compress none +matte -map in.colors.bmp out.bmp
The first command only uses around 2MB of ram and is no problem. The second command has a similar RAM usage to the all-in-one command I tried above:

Code: Select all

==16109== Memcheck, a memory error detector
==16109== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==16109== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==16109== Command: convert in.png -channel RGB -depth 8 -compress none +matte -map in.colors.bmp out.bmp
==16109== 
==16109== 
==16109== HEAP SUMMARY:
==16109==     in use at exit: 392 bytes in 10 blocks
==16109==   total heap usage: 3,451 allocs, 3,441 frees, 137,687,938 bytes allocated
==16109== 
==16109== LEAK SUMMARY:
==16109==    definitely lost: 0 bytes in 0 blocks
==16109==    indirectly lost: 0 bytes in 0 blocks
==16109==      possibly lost: 0 bytes in 0 blocks
==16109==    still reachable: 392 bytes in 10 blocks
==16109==         suppressed: 0 bytes in 0 blocks
==16109== Rerun with --leak-check=full to see details of leaked memory
==16109== 
==16109== For counts of detected and suppressed errors, rerun with: -v
==16109== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
If anybody has any ideas for a command to do this using a resonable amount of RAM for the conversion would be greatly appreciated.
The file I am trying to convert is only around 8.6KB as a PNG and 301KB as a 24-bit bitmap.
On my device (Embedded Linux), I have ImageMagick 6.4.4 2013-06-20 Q16, an old version I know.
On my desktop (Up-to-date Arch Linux), I have ImageMagick 6.8.5-10 2013-06-07 Q16, which should be pretty close to the latest version.

I have tried running these commands through ImageMagick Q8 with no improvements to memory usage. I do not care about losing any color information, so if it were possible to tell ImageMagick to do all its work in an 8-bit format instead of a 24-bit format would be fine. The image I used to generate the valgrind output above can be found here: http://i.imgur.com/i8pnMX6.png.

Thank you for the help!

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T06:49:15-07:00
by snibgo

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T08:02:36-07:00
by jbb5044
snibgo wrote:You might try "-treedepth". See http://www.imagemagick.org/script/comma ... #treedepth
Thanks for the tip but -treedepth does not seem to affect the memory usage in any way.
I ran the conversion with a treedepth of 0 through 20 and it was always 137,563,412+-1

Code: Select all

$ for i in $(seq 0 20); do echo -n "Treedepth $i:"; valgrind convert in.png -channel RGB -depth 8 -colors 256 -compress none +matte -treedepth $i out.bmp 2>&1 |  grep 'total heap usage' | awk '{print $9}'; done
Treedepth 0:137,563,412
Treedepth 1:137,563,412
Treedepth 2:137,563,412
Treedepth 3:137,563,412
Treedepth 4:137,563,412
Treedepth 5:137,563,412
Treedepth 6:137,563,412
Treedepth 7:137,563,412
Treedepth 8:137,563,412
Treedepth 9:137,563,412
Treedepth 10:137,563,413
Treedepth 11:137,563,413
Treedepth 12:137,563,413
Treedepth 13:137,563,413
Treedepth 14:137,563,413
Treedepth 15:137,563,413
Treedepth 16:137,563,413
Treedepth 17:137,563,413
Treedepth 18:137,563,413
Treedepth 19:137,563,413
Treedepth 20:137,563,413

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T08:25:08-07:00
by magick
BMP images are fully allocated in memory (width x height). Most image formats are not. This command, for example, uses minimal memory:
  • convert -limit memory 0 -limit map 0 in.png -channel RGB -depth 8 -colors 256 -compress none +matte out.png
Take a look @ magick/magick-baseconfig.h. Is MAGICKCORE_HAVE_MMAP_FILEIO defined? Does your embedded device support disk writes? If so, we could use a memory map rather than heap memory. Let us know and we'll add a patch to optimize memory usage in the BMP image format.

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T10:19:03-07:00
by jbb5044
I understand that bitmap images are loaded completely into memory to be worked on, but I do not believe that to be the issue. This isn't a 130 MB image when in bitmap format, it's only a bit over 100KB.
I further don't believe that the bitmap loader has much to do with it because I can create a 1x1 white PNG image and the conversion continues to use over 130MB of heap.

Using the valgrind library's mastif tool, I can see that the 134,707,200B are allocated in one go:

Code: Select all

--------------------------------------------------------------------------------
  n        time(B)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 30    135,010,304      134,723,232      134,707,200        16,032            0
99.99% (134,707,200B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->99.62% (134,217,728B) 0x4F6F313: ??? (in /usr/lib/libMagickCore-Q16HDRI.so.8.0.0)
| ->99.62% (134,217,728B) 0x4F6F557: QuantizeImage (in /usr/lib/libMagickCore-Q16HDRI.so.8.0.0)
|   ->99.62% (134,217,728B) 0x53A1B2F: ??? (in /usr/lib/libMagickWand-Q16HDRI.so.0.0.0)
|     ->99.62% (134,217,728B) 0x53A6B7F: CLISimpleOperatorImages (in /usr/lib/libMagickWand-Q16HDRI.so.0.0.0)
|       ->99.62% (134,217,728B) 0x53A982F: CLIOption (in /usr/lib/libMagickWand-Q16HDRI.so.0.0.0)
|         ->99.62% (134,217,728B) 0x5345A1B: ProcessCommandOptions (in /usr/lib/libMagickWand-Q16HDRI.so.0.0.0)
|           ->99.62% (134,217,728B) 0x5345DF1: MagickImageCommand (in /usr/lib/libMagickWand-Q16HDRI.so.0.0.0)
|             ->99.62% (134,217,728B) 0x5360341: MagickCommandGenesis (in /usr/lib/libMagickWand-Q16HDRI.so.0.0.0)
|               ->99.62% (134,217,728B) 0x4008C5: ??? (in /usr/bin/magick)
|                 ->99.62% (134,217,728B) 0x5815A13: (below main) (in /usr/lib/libc-2.17.so)
|                   
->00.36% (489,472B) in 1+ places, all below ms_print's threshold (01.00%)
This output is from the command

Code: Select all

valgrind --tool=massif --time-unit=B --detailed-freq=1 convert in2.png -limit memory 0 -limit map 0 -channel RGB -depth 8 -colors 256 -compress none +matte out2.bmp
in2.png is available here: http://i.imgur.com/IoU2NGI.png

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T10:29:41-07:00
by magick
Color reduction appears to be memory greedy. We'll have a fix in ImageMagick 6.8.6-2 Beta within a few days.

In the mean-time add +dither just before -colors to reduce the memory requirements to 10MB.

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T11:19:08-07:00
by jbb5044
Looking the source for quantize.c, I noticed that the amount of RAM allocated for the reduction is dependent on the CacheShift define which when built for the iPhone is 3, but on the PC is 2.
Changing that define to use 3 on my system makes the output image look perfect while using less than 1 MB of RAM.

Since 1MB would be a bit better than 10MB, do you think this is this a reasonable solution? If not, can you tell me how you plan to fix it in TRUNK?

Thank you for the help!

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T12:33:20-07:00
by magick
Its the dithering that is consuming memory. We have a cache to speed up dithering and it consumes 128MB of memory. If you change the cache type from ssize_t to int, it reduces the memory requirement by 50%.

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T13:19:29-07:00
by jbb5044
Can you tell me what the CacheShift define does then? Experimenting with high-color images shows no clear image quality difference between having CacheShift set to 2 vs having it set to 3, while the memory usage differences are huge.
This has apparently been previously identified as an issue when being used on the iPhone, I'm wondering why that fix was apllied to only the iPhone.

Re: Conversion to 8-bit Bitmap Requires 130 MB of Ram

Posted: 2013-06-21T13:36:51-07:00
by jbb5044
Playing with this further, I notice two more things.
1. The time required to do the conversion seems to be decreased with CacheShift set to 3. My test image time to process was decreased by about one second on my desktop.
2. The amount of RAM required for the conversion goes up with increased image size / colors with CacheShift set to 2, the RAM requirements for my test image went up to 187MB. While it seems to remain constant with CacheShift set to 3, at about 256KB.

The high color image I was experimenting with is this one: http://i.imgur.com/U9Nfldc.jpg