willglynn / ruby-zbar

Ruby bindings for the ZBar barcode recognition library
MIT License
83 stars 24 forks source link

Decoding PNG images #7

Closed lanceschi closed 10 years ago

lanceschi commented 10 years ago

Hi,

I was just trying in IRB your nice binding albeit I wasn't able to decode and process PNG images. I took a look at the source code and I cound't find any references to PNG file format. I'm a correct? Are there any workarounds, since command line 'zbarimg' can handle those?

Thanks, Luca

willglynn commented 10 years ago

zbarimg links against ImageMagick to handle image conversions; it's not a feature of the underlying library. The zbar library itself only handles a few formats, and I think the only reason JPEG made the cut is because zbar can decompress JPEG more efficiently than if you provided it a 24/32-bit RGB surface.

There are lots of options for image conversion in Ruby, and I didn't want to pick one and make it a ruby-zbar dependency, so as a result ruby-zbar handles only a few formats as well.

lanceschi commented 10 years ago

@willglynn: thanks for you reply. Since I've already ImageMagick on my VM how I'm a supposed then to initialize accordingly ZBar module and Image class after I manage to load the picture? Here's the code:

require 'RMagick'
require 'zbar'

img = Magick::Image.read('barcode01.png').first
...

Thanks, Luca

willglynn commented 10 years ago

I don't have RMagick immediately handy, but probably something like:

pgm = img.to_blob { |attrs| attrs.format = 'PGM' }
zbar_image = ZBar::Image.from_pgm(pgm)
symbols = zbar_image.process
lanceschi commented 10 years ago

@willglynn: yep, it did the trick! Thanks! Just to recap:

require 'RMagick'
require 'zbar'

img = Magick::Image.read('barcode01.png').first
pgm = img.to_blob { |attrs| attrs.format = 'PGM' }
zbar_image = ZBar::Image.from_pgm(pgm)
symbols = zbar_image.process

Thanks again!

willglynn commented 10 years ago

Symbol quality is somewhat ill-defined:

Returns: an unscaled, relative quantity: larger values are better than smaller values, where "large" and "small" are application dependent. Note: expect the exact definition of this quantity to change as the metric is refined. currently, only the ordered relationship between two values is defined and will remain stable in the future

I wouldn't use that value for anything terribly important.

Any MPixels limitations that you're aware of?

No, but you'll probably have memory issues at some point if you process a lot of images, especially if you're using RMagick to handle conversions. Learning about the Ruby GC is most of the solution; RMagick::Image#destroy! is another part.

There's also ZBar::Symbol @location which holds an array of arrays, can you further comment this? Here the decoding results:

Locations are a bunch of [x,y] pairs describing the polygon in which ZBar found a barcode.

lanceschi commented 10 years ago

No, but you'll probably have memory issues at some point if you process a lot of images, especially if you're using RMagick to handle conversions. Learning about the Ruby GC is most of the solution.

Good point. I was thinking of scaling pictures to speed up the raw pgm conversion. Do you suggest to force a GC after the decoding process has come to an end? Any better approach?

Thanks, Luca

willglynn commented 10 years ago

I've found the most reliable way to control memory consumption in a Ruby process is to do image work out-of-process. MiniMagick can help: it lets you do things like crop and convert with a simple Ruby API, but it delegates the actual processing to command-line ImageMagick/GraphicsMagick tools. This isn't necessarily the ideal solution, but it's less likely to give you grief.

lanceschi commented 10 years ago

@willglynn: I gave MiniMagick a try:

require 'mini_magick'
require 'zbar'

img = MiniMagick::Image.open('barcode01.jpg')
img.format('PGM')
pgm = img.to_blob
zbar_image = ZBar::Image.from_pgm(pgm)
symbols = zbar_image.process

It seems to me that MiniMagick with the first line create an img object pointing to a tmp file onto which directing further processing (a good thing!).

1.9.3-p484 :003 > img = MiniMagick::Image.open('barcode01.jpg')
    => #<MiniMagick::Image:0x000000015889e0 @path="/tmp
    /mini_magick20140129-2325-1toyrgc.jpg", @tempfile=#<File:/tmp
    /mini_magick20140129-2325-1toyrgc.jpg (closed)>> 

On the other hand pgm object or better String still resides in memory after creation. Any way to force object destruction and the memory release after ZBar processing has terminated? Or should I wait for Ruby GC?

Thanks, L.

willglynn commented 10 years ago

On the other hand pgm object or better String still resides in memory after creation. Any way to force object destruction and the memory release after ZBar processing has terminated? Or should I wait for Ruby GC?

I wouldn't worry about it as long as you let it go out of scope before processing another image. Back of the napkin: PGM is 8 bits per pixel, so a 5000 * 5000 image weighs 25 MB. Even assuming there's an extra copy or two held temporarily, that's readily affordable as long as you go one at a time.

The primary way that RMagick can bite you is because Ruby doesn't "see" the memory used by ImageMagick. This means you can burn hundreds of MB and run out of RAM before the GC ever gets invoked. Passing image data around as a Ruby string isn't ideally efficient, but it avoids that problem since it's Ruby-allocated memory. Ruby will run its garbage collector automatically as you use more RAM, and it'll clean up the bits you don't need any more.

andyweiss1982 commented 9 years ago

Hi @willglynn, was wondering if you had any insight into an issue I'm having. I am using the Grim gem to strip a multi-page PDF into one PNG per page. Then I am trying to read a barcode on each page using the process outlined above. I am getting an error I've never seen before and did a little source diving into the gem code but can't figure out where max_value is being set. I am attaching here a screenshot of the error I am getting as well as the PNG file I am trying to read. Thanks!

Error: screen shot 2015-10-16 at 12 55 08 pm

PNG I am trying to read: pdf_page_1

Source code: Where does $4 come from? screen shot 2015-10-16 at 12 58 18 pm

willglynn commented 9 years ago

$4 is the fourth capture from that regex, P5 being the first. 255 is expected, since that corresponds to an 8-bit PGM, which encodes image data identically to zbar's Y800 format. What is the value in your blob?

andyweiss1982 commented 9 years ago

@willglynn I got 65535. Does that seem crazy?

andyweiss1982 commented 9 years ago

screen shot 2015-10-16 at 2 40 53 pm screen shot 2015-10-16 at 2 41 30 pm

willglynn commented 9 years ago

That's a 16-bit image, which zbar doesn't support. Convert it to 8-bit before turning into a PGM.

andyweiss1982 commented 9 years ago

This worked -- thanks @willglynn !