rmagick question

Discussion in 'Ruby' started by Joe Van Dyk, Aug 26, 2005.

  1. Joe Van Dyk

    Joe Van Dyk Guest

    I'm trying to set the pixels of an image:

    require 'RMagick'

    width =3D 512
    height =3D 256

    image =3D Magick::Image.new width, height

    width.times do |x|
    height.times do |y|
    pixel =3D Magick::pixel.new rand(256), rand(256), rand(256)
    image.pixel_color x, y, pixel=20
    end
    end

    image.write ARGV.shift

    But the resulting image is all black. Why?
    Joe Van Dyk, Aug 26, 2005
    #1
    1. Advertising

  2. Joe Van Dyk

    Joe Van Dyk Guest

    On 8/25/05, Joe Van Dyk <> wrote:
    > I'm trying to set the pixels of an image:
    >=20
    > require 'RMagick'
    >=20
    > width =3D 512
    > height =3D 256
    >=20
    > image =3D Magick::Image.new width, height
    >=20
    > width.times do |x|
    > height.times do |y|
    > pixel =3D Magick::pixel.new rand(256), rand(256), rand(256)
    > image.pixel_color x, y, pixel
    > end
    > end
    >=20
    > image.write ARGV.shift
    >=20
    > But the resulting image is all black. Why?


    And here's some code from my actual application:
    =20
    pixel =3D Magick::pixel.new(*rgb)
    puts "The image pixel was: <#{ image.pixel_color(e, n) }>"
    image.pixel_color(e, n, pixel)
    puts "The pixel was <#{ pixel }>"
    puts "The image pixel is now: <#{ image.pixel_color(e, n) }>"

    And here's the result:
    The image pixel was: <red=3D65535, green=3D65535, blue=3D65535, opacity=
    =3D0>
    The pixel was <red=3D175, green=3D206, blue=3D117, opacity=3D0>
    The image pixel is now: <red=3D65535, green=3D65535, blue=3D65535, opac=
    ity=3D0>=20

    Why on earth isn't it setting the pixel color on the image?
    Joe Van Dyk, Aug 26, 2005
    #2
    1. Advertising

  3. On 26/08/05, Joe Van Dyk <> wrote:
    > I'm trying to set the pixels of an image:
    >=20
    > require 'RMagick'
    >=20
    > width =3D 512
    > height =3D 256
    >=20
    > image =3D Magick::Image.new width, height
    >=20
    > width.times do |x|
    > height.times do |y|
    > pixel =3D Magick::pixel.new rand(256), rand(256), rand(256)
    > image.pixel_color x, y, pixel
    > end
    > end
    >=20
    > image.write ARGV.shift
    >=20
    > But the resulting image is all black. Why?
    >=20
    >=20


    Use Magick::MaxRGB+1 instead of 256. RGB Values range from 0...2**16.
    And be shure to check out the View funktionality.

    regards,

    Brian

    --=20
    http://ruby.brian-schroeder.de/

    Stringed instrument chords: http://chordlist.brian-schroeder.de/
    Brian Schröder, Aug 26, 2005
    #3
  4. Joe Van Dyk wrote:
    > On 8/26/05, Ara.T.Howard <> wrote:
    >
    >>On Sat, 27 Aug 2005, Joe Van Dyk wrote:
    >>
    >>
    >>>Hm, I'm starting to think there's something wrong with my magick
    >>>installation. :(

    >>
    >>i've found that the only way to go with imagemagick is to compile from source.
    >>the redhat fedora and enterprise rpms are totally hosed: for example loseless
    >>jpeg2000 compression isn't - unless you build from source, which takes hours
    >>due to all the bloody dependancies. in any case i thought i'd let you know
    >>that in case your installation is a redhat package.
    >>
    >>note: this info above is about 4 months old - it could be fixed...

    >
    >
    > Thanks... this is on a imagemagick installation compiled from scratch
    > last night though. 6.2.4. So I'm not sure what's going on.
    >
    > If I have a bunch of RGB values from 0-256, do you know a way to
    > create an image for them? Apparently the MaxRGB on my installation is
    > around 65000 or so. The image is around 10kx10k pixels, so speed is
    > sorta important.
    >
    >


    If speed is important then the best thing to do is to build a new
    ImageMagick using the --with-quantum-depth=8 option. Using 8-bit depth
    images considerably reduces IM's memory and CPU requirements and it
    makes the channel intensities range from 0-255 instead of 0-65535, more
    in line with your expectations. For 100-million-pixel images I think it
    would be worth the trouble.

    However, if you don't want to re-install IM and you don't mind paying
    for a couple extra bit operations per channel, you can convert 8-bit
    channels to 16-bit channels like this:

    red16 = (red8 << 8) | red8

    Lastly, take a look at the #store_pixels method. This method lets you
    replace pixels in an image a section at a time, where a section can be a
    single row or column of pixels, or for that matter any rectangle. This
    might be a good compromise between pixel_color and constitute.
    Timothy Hunter, Aug 28, 2005
    #4
  5. Joe Van Dyk

    Joe Van Dyk Guest

    On 8/28/05, Timothy Hunter <> wrote:
    > Joe Van Dyk wrote:
    > > On 8/26/05, Ara.T.Howard <> wrote:
    > >
    > >>On Sat, 27 Aug 2005, Joe Van Dyk wrote:
    > >>
    > >>
    > >>>Hm, I'm starting to think there's something wrong with my magick
    > >>>installation. :(
    > >>
    > >>i've found that the only way to go with imagemagick is to compile from =

    source.
    > >>the redhat fedora and enterprise rpms are totally hosed: for example lo=

    seless
    > >>jpeg2000 compression isn't - unless you build from source, which takes =

    hours
    > >>due to all the bloody dependancies. in any case i thought i'd let you =

    know
    > >>that in case your installation is a redhat package.
    > >>
    > >>note: this info above is about 4 months old - it could be fixed...

    > >
    > >
    > > Thanks... this is on a imagemagick installation compiled from scratch
    > > last night though. 6.2.4. So I'm not sure what's going on.
    > >
    > > If I have a bunch of RGB values from 0-256, do you know a way to
    > > create an image for them? Apparently the MaxRGB on my installation is
    > > around 65000 or so. The image is around 10kx10k pixels, so speed is
    > > sorta important.
    > >
    > >

    >=20
    > If speed is important then the best thing to do is to build a new
    > ImageMagick using the --with-quantum-depth=3D8 option. Using 8-bit depth
    > images considerably reduces IM's memory and CPU requirements and it
    > makes the channel intensities range from 0-255 instead of 0-65535, more
    > in line with your expectations. For 100-million-pixel images I think it
    > would be worth the trouble.
    >=20
    > However, if you don't want to re-install IM and you don't mind paying
    > for a couple extra bit operations per channel, you can convert 8-bit
    > channels to 16-bit channels like this:
    >=20
    > red16 =3D (red8 << 8) | red8
    >=20
    > Lastly, take a look at the #store_pixels method. This method lets you
    > replace pixels in an image a section at a time, where a section can be a
    > single row or column of pixels, or for that matter any rectangle. This
    > might be a good compromise between pixel_color and constitute.


    I'll try the bitshifting approach, thanks. =20

    Previously, I had builtup a lookup array that looked like (I think):
    color_lookup_table =3D Array.new
    256.times { |i| color_lookup_table << Magick::MaxRGB / i }

    And then did a lookup on that table for each color. You think the
    bitshifting approach will be faster than an array lookup?
    Joe Van Dyk, Aug 28, 2005
    #5
  6. Joe Van Dyk wrote:
    > On 8/28/05, Timothy Hunter <> wrote:
    >
    >>Joe Van Dyk wrote:
    >>
    >>>On 8/26/05, Ara.T.Howard <> wrote:
    >>>
    >>>
    >>>>On Sat, 27 Aug 2005, Joe Van Dyk wrote:
    >>>>
    >>>>
    >>>>
    >>>>>Hm, I'm starting to think there's something wrong with my magick
    >>>>>installation. :(
    >>>>
    >>>>i've found that the only way to go with imagemagick is to compile from source.
    >>>>the redhat fedora and enterprise rpms are totally hosed: for example loseless
    >>>>jpeg2000 compression isn't - unless you build from source, which takes hours
    >>>>due to all the bloody dependancies. in any case i thought i'd let you know
    >>>>that in case your installation is a redhat package.
    >>>>
    >>>>note: this info above is about 4 months old - it could be fixed...
    >>>
    >>>
    >>>Thanks... this is on a imagemagick installation compiled from scratch
    >>>last night though. 6.2.4. So I'm not sure what's going on.
    >>>
    >>>If I have a bunch of RGB values from 0-256, do you know a way to
    >>>create an image for them? Apparently the MaxRGB on my installation is
    >>>around 65000 or so. The image is around 10kx10k pixels, so speed is
    >>>sorta important.
    >>>
    >>>

    >>
    >>If speed is important then the best thing to do is to build a new
    >>ImageMagick using the --with-quantum-depth=8 option. Using 8-bit depth
    >>images considerably reduces IM's memory and CPU requirements and it
    >>makes the channel intensities range from 0-255 instead of 0-65535, more
    >>in line with your expectations. For 100-million-pixel images I think it
    >>would be worth the trouble.
    >>
    >>However, if you don't want to re-install IM and you don't mind paying
    >>for a couple extra bit operations per channel, you can convert 8-bit
    >>channels to 16-bit channels like this:
    >>
    >>red16 = (red8 << 8) | red8
    >>
    >>Lastly, take a look at the #store_pixels method. This method lets you
    >>replace pixels in an image a section at a time, where a section can be a
    >>single row or column of pixels, or for that matter any rectangle. This
    >>might be a good compromise between pixel_color and constitute.

    >
    >
    > I'll try the bitshifting approach, thanks.
    >
    > Previously, I had builtup a lookup array that looked like (I think):
    > color_lookup_table = Array.new
    > 256.times { |i| color_lookup_table << Magick::MaxRGB / i }
    >
    > And then did a lookup on that table for each color. You think the
    > bitshifting approach will be faster than an array lookup?
    >
    >

    I don't know. My gut feel is yes but if you're going to be working on
    100,000,000 pixel images then it would be worth the trouble to actually
    compare the two approaches. A little bit of difference would mount up
    quickly :)

    Of course 100,000,000 pixel images are going to have resource
    constraints besides CPU. At 16-pixels per channel each pixel will
    require 8 bytes plus some per-image overhead, so each image will occupy
    a bit over 800MB of memory. Using quantum depth=8 cuts the memory
    requirement in half.

    No matter which approach you take let me know how it goes so I'll be
    able to make recommendations to other RMagick users who are working with
    very large images. Thanks!
    Timothy Hunter, Aug 29, 2005
    #6
  7. Joe Van Dyk

    Joe Van Dyk Guest

    On 8/28/05, Timothy Hunter <> wrote:
    > Joe Van Dyk wrote:
    > > On 8/28/05, Timothy Hunter <> wrote:
    > >
    > >>Joe Van Dyk wrote:
    > >>
    > >>>On 8/26/05, Ara.T.Howard <> wrote:
    > >>>
    > >>>
    > >>>>On Sat, 27 Aug 2005, Joe Van Dyk wrote:
    > >>>>
    > >>>>
    > >>>>
    > >>>>>Hm, I'm starting to think there's something wrong with my magick
    > >>>>>installation. :(
    > >>>>
    > >>>>i've found that the only way to go with imagemagick is to compile fro=

    m source.
    > >>>>the redhat fedora and enterprise rpms are totally hosed: for example =

    loseless
    > >>>>jpeg2000 compression isn't - unless you build from source, which take=

    s hours
    > >>>>due to all the bloody dependancies. in any case i thought i'd let yo=

    u know
    > >>>>that in case your installation is a redhat package.
    > >>>>
    > >>>>note: this info above is about 4 months old - it could be fixed...
    > >>>
    > >>>
    > >>>Thanks... this is on a imagemagick installation compiled from scratch
    > >>>last night though. 6.2.4. So I'm not sure what's going on.
    > >>>
    > >>>If I have a bunch of RGB values from 0-256, do you know a way to
    > >>>create an image for them? Apparently the MaxRGB on my installation is
    > >>>around 65000 or so. The image is around 10kx10k pixels, so speed is
    > >>>sorta important.
    > >>>
    > >>>
    > >>
    > >>If speed is important then the best thing to do is to build a new
    > >>ImageMagick using the --with-quantum-depth=3D8 option. Using 8-bit dept=

    h
    > >>images considerably reduces IM's memory and CPU requirements and it
    > >>makes the channel intensities range from 0-255 instead of 0-65535, more
    > >>in line with your expectations. For 100-million-pixel images I think it
    > >>would be worth the trouble.
    > >>
    > >>However, if you don't want to re-install IM and you don't mind paying
    > >>for a couple extra bit operations per channel, you can convert 8-bit
    > >>channels to 16-bit channels like this:
    > >>
    > >>red16 =3D (red8 << 8) | red8
    > >>
    > >>Lastly, take a look at the #store_pixels method. This method lets you
    > >>replace pixels in an image a section at a time, where a section can be =

    a
    > >>single row or column of pixels, or for that matter any rectangle. This
    > >>might be a good compromise between pixel_color and constitute.

    > >
    > >
    > > I'll try the bitshifting approach, thanks.
    > >
    > > Previously, I had builtup a lookup array that looked like (I think):
    > > color_lookup_table =3D Array.new
    > > 256.times { |i| color_lookup_table << Magick::MaxRGB / i }
    > >
    > > And then did a lookup on that table for each color. You think the
    > > bitshifting approach will be faster than an array lookup?
    > >
    > >

    > I don't know. My gut feel is yes but if you're going to be working on
    > 100,000,000 pixel images then it would be worth the trouble to actually
    > compare the two approaches. A little bit of difference would mount up
    > quickly :)
    >=20
    > Of course 100,000,000 pixel images are going to have resource
    > constraints besides CPU. At 16-pixels per channel each pixel will
    > require 8 bytes plus some per-image overhead, so each image will occupy
    > a bit over 800MB of memory. Using quantum depth=3D8 cuts the memory
    > requirement in half.
    >=20
    > No matter which approach you take let me know how it goes so I'll be
    > able to make recommendations to other RMagick users who are working with
    > very large images. Thanks!


    Yes, my Ruby program was taking up about 500 MB of memory (for a
    8700x6000 pixel image). Memory's not a problem though, all of our
    machines have more than 2 gigabytes.

    I'll report back tomorrow after I try the bitshifting and 8-bit
    imagemagick approach instead of the current array lookup.

    Unit tests are really coming in handy on this type of application.=20
    Especially the benchmark library. It's awesome to make a change and
    then build up some sample data and do automated tests and benchmarking
    on it.
    Joe Van Dyk, Aug 29, 2005
    #7
  8. Joe Van Dyk wrote:
    > On 8/28/05, Timothy Hunter <> wrote:
    >
    >>No matter which approach you take let me know how it goes so I'll be
    >>able to make recommendations to other RMagick users who are working with
    >>very large images. Thanks!

    >
    >
    > Yes, my Ruby program was taking up about 500 MB of memory (for a
    > 8700x6000 pixel image). Memory's not a problem though, all of our
    > machines have more than 2 gigabytes.
    >


    Okay, first recommendation: have a honkin' great big machine :)

    Thanks!
    Timothy Hunter, Aug 29, 2005
    #8
  9. Joe Van Dyk

    Joe Van Dyk Guest

    On 8/29/05, Timothy Hunter <> wrote:
    > Joe Van Dyk wrote:
    > > On 8/28/05, Timothy Hunter <> wrote:
    > >
    > >>No matter which approach you take let me know how it goes so I'll be
    > >>able to make recommendations to other RMagick users who are working wit=

    h
    > >>very large images. Thanks!

    > >
    > >
    > > Yes, my Ruby program was taking up about 500 MB of memory (for a
    > > 8700x6000 pixel image). Memory's not a problem though, all of our
    > > machines have more than 2 gigabytes.
    > >

    >=20
    > Okay, first recommendation: have a honkin' great big machine :)


    (in case people forgot, the color values in @tad_data are from 0-255,
    so conversion is needed if ImageMagick is using 16 bit color pixels)

    The inner code loop looked something like this:

    (height - 1).downto(0) do |n|
    # Code here that displayed percent-done status to user
    width.times do |e|
    # Lookup approach with 16 bit pixel ImageMagick
    #rgb =3D @tad_data.read(8).unpack(TAD_FORMAT).collect! { |c|
    lookup_table[c] }
    =20
    # Bitshifting approach with 16 bit pixel ImageMagick
    #rgb =3D @tad_data.read(8).unpack(TAD_FORMAT).collect! { |c| (c
    << 8) | c }

    # 8 bit ImageMagick, no conversion necessary
    rgb =3D @tad_data.read(8).unpack(TAD_FORMAT)

    pixel =3D Magick::pixel.new(*rgb)
    @image.pixel_color(e, n, pixel)
    end
    end

    Doing a 10000x10 pixel image (only 10 pixels high for unit testing
    purposes), I could process:

    13k pixels/second with the bitshifting approach (and 16 bit IM)
    13k pixels/second with the array lookup approach (and 16 bit IM)
    21k pixels/second with 256 bit IM.

    I also found that building up an array of colors for one row and then
    doing a @image.import_pixels had no speed improvements and the code
    was uglier.
    Joe Van Dyk, Aug 29, 2005
    #9
  10. Joe Van Dyk

    Joe Van Dyk Guest

    On 8/29/05, Joe Van Dyk <> wrote:
    > On 8/29/05, Timothy Hunter <> wrote:
    > > Joe Van Dyk wrote:
    > > > On 8/28/05, Timothy Hunter <> wrote:
    > > >
    > > >>No matter which approach you take let me know how it goes so I'll be
    > > >>able to make recommendations to other RMagick users who are working w=

    ith
    > > >>very large images. Thanks!
    > > >
    > > >
    > > > Yes, my Ruby program was taking up about 500 MB of memory (for a
    > > > 8700x6000 pixel image). Memory's not a problem though, all of our
    > > > machines have more than 2 gigabytes.
    > > >

    > >
    > > Okay, first recommendation: have a honkin' great big machine :)

    >=20
    > (in case people forgot, the color values in @tad_data are from 0-255,
    > so conversion is needed if ImageMagick is using 16 bit color pixels)
    >=20
    > The inner code loop looked something like this:
    >=20
    > (height - 1).downto(0) do |n|
    > # Code here that displayed percent-done status to user
    > width.times do |e|
    > # Lookup approach with 16 bit pixel ImageMagick
    > #rgb =3D @tad_data.read(8).unpack(TAD_FORMAT).collect! { |c|
    > lookup_table[c] }
    >=20
    > # Bitshifting approach with 16 bit pixel ImageMagick
    > #rgb =3D @tad_data.read(8).unpack(TAD_FORMAT).collect! { |c| (c
    > << 8) | c }
    >=20
    > # 8 bit ImageMagick, no conversion necessary
    > rgb =3D @tad_data.read(8).unpack(TAD_FORMAT)
    >=20
    > pixel =3D Magick::pixel.new(*rgb)
    > @image.pixel_color(e, n, pixel)
    > end
    > end
    >=20
    > Doing a 10000x10 pixel image (only 10 pixels high for unit testing
    > purposes), I could process:
    >=20
    > 13k pixels/second with the bitshifting approach (and 16 bit IM)
    > 13k pixels/second with the array lookup approach (and 16 bit IM)
    > 21k pixels/second with 256 bit IM.
    >=20
    > I also found that building up an array of colors for one row and then
    > doing a @image.import_pixels had no speed improvements and the code
    > was uglier.


    Hm... I don't think that those pixels/second numbers are correct. I
    was doing the timings inside a Benchmark.measure { ... } block and
    perhaps that makes things slower?

    I was able to process a 8700x6000 pixel image when using 'time ./script.rb'
    440.005u 9.917s 8:27.20 88.7% 0+0k 0+0io 52012pf+0w

    So, around 7 minutes for 5 million pixels.. that's around 118000
    pixels per second. Hm. Oh well, it's fast enough for me. This
    operation isn't done all that often. Although, a while ago I did
    something like this in C with the GD library, and it took around 10-20
    seconds to process a similar sized image.
    Joe Van Dyk, Aug 29, 2005
    #10
  11. Joe Van Dyk wrote:
    >>Doing a 10000x10 pixel image (only 10 pixels high for unit testing
    >>purposes), I could process:
    >>
    >>13k pixels/second with the bitshifting approach (and 16 bit IM)
    >>13k pixels/second with the array lookup approach (and 16 bit IM)
    >>21k pixels/second with 256 bit IM.
    >>
    >>I also found that building up an array of colors for one row and then
    >>doing a @image.import_pixels had no speed improvements and the code
    >>was uglier.

    >
    >
    > Hm... I don't think that those pixels/second numbers are correct. I
    > was doing the timings inside a Benchmark.measure { ... } block and
    > perhaps that makes things slower?
    >
    > I was able to process a 8700x6000 pixel image when using 'time ./script.rb'
    > 440.005u 9.917s 8:27.20 88.7% 0+0k 0+0io 52012pf+0w
    >
    > So, around 7 minutes for 5 million pixels.. that's around 118000
    > pixels per second. Hm. Oh well, it's fast enough for me. This
    > operation isn't done all that often. Although, a while ago I did
    > something like this in C with the GD library, and it took around 10-20
    > seconds to process a similar sized image.
    >


    I'm not surprised that the 256-color IM is the fastest solution. I'm
    also not surprised that a pure-C solution is faster than a Ruby
    solution. All those channel values have to be converted from integers to
    Fixnums and back to integers, not to mention constructing 5M
    Magick::pixel objects.

    Thanks for sharing what you learned. This'll be useful for the next guy
    to face a similar problem.
    Timothy Hunter, Aug 30, 2005
    #11
  12. Joe Van Dyk

    Ara.T.Howard Guest

    On Tue, 30 Aug 2005, Timothy Hunter wrote:

    > I'm not surprised that the 256-color IM is the fastest solution. I'm also
    > not surprised that a pure-C solution is faster than a Ruby solution. All
    > those channel values have to be converted from integers to Fixnums and back
    > to integers, not to mention constructing 5M Magick::pixel objects.
    >
    > Thanks for sharing what you learned. This'll be useful for the next guy to
    > face a similar problem.


    hi tim-

    i was playing with something along the lines of

    na = NArray::byte 1000, 1000

    img = RMagick::Image::new # i forget the syntax attm

    img.set_pixels na.to_s

    but this blew up with a complaint about needing an array. my question - is
    there any reason to check the type for these sorts of things? i mean, if it's
    only a few methods required why not just call them and blow up if the obj does
    not support them? i was hoping to combine NArray and RMagick for the obvious
    speed advantage one could gain - for instance being able to 'darken' and image
    a little with

    na = na - 1

    is pretty powerful... but getting the values back out puts you into loop
    land... in general it seems like there should be someway, with RMagick, to
    say

    img.pixels = buffer

    and that buffer had better be a binary packed respresentation of the pixels.
    this would allow usage with narray and mmap. the nice thing about using mmap
    is that you could spawn subprocesses to work on part of an image using shared
    memory and, when all children had finished, dump the pixels into your image.
    it could be very powerful. the narray lib has a similar approach in that you
    can say things like

    na = NArray::to_na buffer, NArray::BYTE, 1000, 1000

    and the buffer is slurped in at once, i assume using memcpy. might an
    approach like this for image magic make faster operations possible? or is the
    layout of pixels never inline is a way that could take advantage of something
    like that?

    cheers.

    -a
    --
    ===============================================================================
    | email :: ara [dot] t [dot] howard [at] noaa [dot] gov
    | phone :: 303.497.6469
    | Your life dwells amoung the causes of death
    | Like a lamp standing in a strong breeze. --Nagarjuna
    ===============================================================================
    Ara.T.Howard, Aug 30, 2005
    #12
  13. Ara.T.Howard wrote:
    > On Wed, 31 Aug 2005, Timothy Hunter wrote:
    >
    >> Darn. After reading Ara's post I started thinking about a way to move
    >> pixels
    >> more-or-less directly from a file into an image, bypassing the need to
    >> create Magick::pixel objects. I emailed the ImageMagick team and asked
    >> for
    >> their recommendation. The absolute fastest way to get pixel data into an
    >> image is via the ImageMagick API that RMagick's Image::import_pixels
    >> method
    >> (http://www.simplesystems.org/RMagick/doc/image2.html#import_pixels)
    >> uses,
    >> called ImportImagePixels. ImportImagePixels expects a C array of pixel
    >> data
    >> in scanline order, top-to-bottom, right-to-left. You can specify the data
    >> type of the array (char, short, int, long) and the order in which the
    >> channel data appear (RGB, RGBA, CMYK, etc.) The IM developers tell me
    >> that
    >> ImportImagePixels is optimized for the RGB case so I suspect that this
    >> would
    >> be a very - I repeat, very - fast way to load pixels into an image.

    >
    >
    > plus plus on the 'very' ;-)
    >
    >> Currently #import_pixels wants a Ruby array of Fixnums which it then
    >> converts to a C array. Lotsa overhead.
    >>
    >> Following Ara's suggestion, I was thinking about changing
    >> #import_pixels to
    >> accept a string (to be exact, any object that responds to to_str) in
    >> place
    >> of the array. In this case #import_pixels would simply call to_str and
    >> assume that the result is a C array of the correct type and size and with
    >> the channel data in the specified order and hand it off to
    >> ImportImagePixels
    >> directly.

    >
    >
    > so
    >
    > pixels, is_string =
    > case obj
    > when Array
    > [ obj, false ]
    > else
    > [ obj.to_str, true ]
    > end
    >
    > ...
    > ...
    >
    > ??
    >
    >> I realize this doesn't really help you since your input data isn't in the
    >> format ImportImagePixels wants. However, if you (or Ara, or anybody else
    >> with an interest) have an opinion about the usefulness of this idea
    >> I'd like
    >> to hear it.

    >
    >
    > it sounds brilliant! but:
    >
    > jib:~ > irb -r mmap -r narray
    >
    > irb(main):001:0> NArray::byte(42,42).methods.grep /to_s/
    > => ["to_s", "to_string"]
    >
    > irb(main):002:0> Mmap::new('/home/ahoward/.bashrc').methods.grep /to_s/
    > => ["to_sym", "to_str", "to_s"]
    >
    > so maybe something like
    >
    > cast = %w( pixels to_str to_s to_string ).select{|m| obj.respond_to? m
    > }.first
    >
    > pixels = obj.send(cast) if cast
    >
    > eg. include other likely candidates in addition to to_str. i think
    > it's safe
    > to say that any user capable of setting up a region of memory
    > representing an
    > inline image will be willing to accept any consequences of doing it
    > improperly
    > so doing a blind read is fine in this case - but that's obviously my
    > opinion.


    Actually I was thinking that NArray would meet me half-way.

    Right now #import_pixels calls Kernal.Array on the `pixels' argument. My
    reasoning for doing this is that this approach allows the caller to pass
    any object that supports #to_ary or #to_a (including NArray objects) to
    #import_pixels. (The downside for NArrays, of course, is that NArray
    responds to #to_a by constructing a real Ruby array with a zillion
    elements.)

    Now I want to filter out strings and treat them specially. Currently a
    string is not a reasonable argument since Kernel.Array simply constructs
    an array with the string as its only element. Not too useful for
    building images. My notion was to do something like this:

    if pixels.respond_to?:)to_str)
    pixel_buffer = pixels.to_str
    # pass the buffer directly to ImageMagick
    else
    pixel_array = Kernel.Array(pixels)
    # convert the array to a buffer and pass it to IM
    end

    I can't think of a way this would break existing code, can you? You
    could use the return value from NArray#to_s, mmap#to_str, or IO.read as
    the `pixels' argument.

    The upside is that for real String objects, #to_str is a no-op. The
    downside, at least for NArray objects, is that #to_s makes a copy of the
    data in the NArray. I've perused the NArray source code and I didn't
    find a way to directly access the NArray data without making a copy.

    One more complication. The ImportImagePixels function in ImageMagick
    requires an argument that identifies the type of type of the data in the
    pixel buffer as char (8-bit), short (16-bit) or int (32-bit).
    ImageMagick will convert the data as necessary to the size it needs. It
    seems to me that it would be useful to support the use of data that is
    not the same size as the underlying pixel type. That is, you could
    reasonably want to construct an image with 8-bit pixel data from an
    NArray.sint (16-bit) object, or vice versa. So, I'm thinking that
    #import_pixels should accept an optional 7th argument that indicates the
    type of the incoming data (CharPixel, ShortPixel, LongPixel enum values,
    probably). The default would be CharPixel. This would also make it
    possible to use the same script with different configurations of
    ImageMagick.

    Thoughts? I'll hold off writing any code until we're in agreement.


    >
    > thanks for keeping tabs on this btw.
    >


    Thanks for the idea!
    Timothy Hunter, Sep 1, 2005
    #13
  14. Joe Van Dyk

    Ara.T.Howard Guest

    On Thu, 1 Sep 2005, Timothy Hunter wrote:

    > Actually I was thinking that NArray would meet me half-way.
    >
    > Right now #import_pixels calls Kernal.Array on the `pixels' argument. My
    > reasoning for doing this is that this approach allows the caller to pass any
    > object that supports #to_ary or #to_a (including NArray objects) to
    > #import_pixels. (The downside for NArrays, of course, is that NArray
    > responds to #to_a by constructing a real Ruby array with a zillion
    > elements.)


    right.

    > Now I want to filter out strings and treat them specially. Currently a
    > string is not a reasonable argument since Kernel.Array simply constructs an
    > array with the string as its only element. Not too useful for building
    > images. My notion was to do something like this:
    >
    > if pixels.respond_to?:)to_str)
    > pixel_buffer = pixels.to_str
    > # pass the buffer directly to ImageMagick
    > else
    > pixel_array = Kernel.Array(pixels)
    > # convert the array to a buffer and pass it to IM
    > end


    check.

    > I can't think of a way this would break existing code, can you? You could
    > use the return value from NArray#to_s, mmap#to_str, or IO.read as the
    > `pixels' argument.
    >
    > The upside is that for real String objects, #to_str is a no-op. The
    > downside, at least for NArray objects, is that #to_s makes a copy of the
    > data in the NArray.


    sounds good. mmap.to_str is a no-op too : guy's got a lot of voodoo going on
    under the hood - but it sure works.

    > I've perused the NArray source code and I didn't find a way to directly
    > access the NArray data without making a copy.


    you just have to use my illicit narray extension ;-) works like this:

    mmap = Mmap::new 'data', 'rw', Mmap::MAP_SHARED
    memory = mmap.to_str
    na = NArray::str memory, width, height, NArray::BYTE
    na += 1
    exit

    and the entire file is incremented by one - no explicit io. however, this is
    officially (by matz i think) frowned on. i've spoken with masahiro about this
    a little and he was interested in doing something... in any case it's fair to
    dump that in the narray camp. right new it calls rb_str_new, which does, in
    fact, copy data. perhaps something like rb_str_new4, which does not copy data
    - but i don't know what the rules are for creating shared strings... in any
    case it would be quite useful now even with a copy since explicit loops
    would be avoided and it would therefore still be very fast.

    > One more complication. The ImportImagePixels function in ImageMagick
    > requires an argument that identifies the type of type of the data in the
    > pixel buffer as char (8-bit), short (16-bit) or int (32-bit). ImageMagick
    > will convert the data as necessary to the size it needs. It seems to me that
    > it would be useful to support the use of data that is not the same size as
    > the underlying pixel type. That is, you could reasonably want to construct
    > an image with 8-bit pixel data from an NArray.sint (16-bit) object, or vice
    > versa. So, I'm thinking that #import_pixels should accept an optional 7th
    > argument that indicates the type of the incoming data (CharPixel,
    > ShortPixel, LongPixel enum values, probably). The default would be
    > CharPixel. This would also make it possible to use the same script with
    > different configurations of ImageMagick.


    hmmm. i'm having thoughts of a 'Memory' or 'Data' class. it could be backed
    by file or not, and could have the notion of a 'quanta' or pixel size. i have
    some simple mmap'ing c programs that manipulate data in this way : they just
    apply operators to a line of memory where the memory is assumed to be of
    certain sized quanta or pixels... but this wouldn't really be required -
    specifying the type is fine - hopefully ImageMagick doesn't convert when it
    doesn't need too...

    all this is probably academic though - i'm sure avoiding loops and loads of
    ruby object creations will yield a huge boost even with some data
    copy/conversion so everything you've said makes good sense.

    > Thoughts? I'll hold off writing any code until we're in agreement.


    i wouldn't mind some opinions from matz, masahiro, and guy about how sharing
    memory amoung ruby objects would best be done - obviously this would be
    slickest with the minimum about of data being moved.

    cheers.

    -a
    --
    ===============================================================================
    | email :: ara [dot] t [dot] howard [at] noaa [dot] gov
    | phone :: 303.497.6469
    | Your life dwells amoung the causes of death
    | Like a lamp standing in a strong breeze. --Nagarjuna
    ===============================================================================
    Ara.T.Howard, Sep 1, 2005
    #14
  15. Joe Van Dyk

    Morgan Guest

    Reading this, I'm wondering what how efficient the from_glob and
    to_glob functions are?

    This was what I used when I had a project needing to move images
    between rmagick and other libraries. In that case I used PNG
    format, but in a case like the one being talked about I wonder if you
    could synthesize a BMP header, load your data into a string after
    it, and make an image with from_glob. (Or rather, would doing that
    be better than other available alternatives?)

    -Morgan, missed the beginning of this thread, thus apologizes if it's
    already been suggested.


    --
    No virus found in this outgoing message.
    Checked by AVG Anti-Virus.
    Version: 7.0.344 / Virus Database: 267.10.17/85 - Release Date: 08/30/2005
    Morgan, Sep 1, 2005
    #15
  16. Joe Van Dyk

    Guest

    Actually this probably would be pretty fast. I haven't worked with BMP
    images in quite a while but iirc the format is quite simple.

    The to_blob and from_blob methods are actually very similar to the
    write and read methods. The only difference is that the blob methods
    read from/write to memory and the write/read methods operate on files.
    , Sep 1, 2005
    #16
  17. Ara.T.Howard wrote:
    > you just have to use my illicit narray extension ;-) works like this:
    >
    > mmap = Mmap::new 'data', 'rw', Mmap::MAP_SHARED
    > memory = mmap.to_str
    > na = NArray::str memory, width, height, NArray::BYTE
    > na += 1
    > exit


    Sweet! So you load your pixel data into an mmap object, use the mmap
    object to create an NArray object, call some NArray methods, then hand
    the mmap object to import_pixels, which calls to_str on it to get a
    pointer to the pixel data.

    That'll do.

    ImageMagick _will_ make a copy of the data. There's no way around that.
    But we're still way ahead of the game. I think we're in agreement about
    the changes to import_pixels so unless I hear different I'll start
    coding. I've not scheduled another release of RMagick yet but I'll be
    glad to get one in the pipeline if you're anxious to use the new code.
    Otherwise I'll hold off a bit.

    Let me know if you have any other thoughts/comments/ideas, and thanks
    again for suggesting this approach!

    P.S. I won't be at Rubyconf this year but if you're there why not tug on
    Matz's coat about sharing memory?
    Timothy Hunter, Sep 1, 2005
    #17
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Tim Hunter

    [ANN] RMagick moves to RubyForge

    Tim Hunter, Jul 27, 2003, in forum: Ruby
    Replies:
    0
    Views:
    99
    Tim Hunter
    Jul 27, 2003
  2. Joe Van Dyk

    RMagick question

    Joe Van Dyk, Apr 7, 2005, in forum: Ruby
    Replies:
    16
    Views:
    239
    Chris Pine
    Apr 12, 2005
  3. Raymond Brigleb

    RMagick Thumbnailing Question

    Raymond Brigleb, Sep 8, 2005, in forum: Ruby
    Replies:
    2
    Views:
    118
  4. shawn bright

    another RMagick and rotation question

    shawn bright, Feb 13, 2008, in forum: Ruby
    Replies:
    4
    Views:
    130
    shawn bright
    Feb 14, 2008
  5. Pito Salas
    Replies:
    1
    Views:
    137
    Tim Hunter
    Apr 24, 2009
Loading...

Share This Page