The DevIL Image Library For Ruby

DevIL is a fast and lightweight image library that supports the loading and saving of images in almost any format. It also provides some basic image manipulation functionality.

Features:

  • Loading and saving of images.
  • Thumbnailing and scaling.
  • Rotations, cropping, composites, gamma correction, image sharpening, blurring, etc.
  • Interoperability with the Gosu game development framework.

Example Code:

Devil.with_image("horse.png") do |img|
    img.thumbnail2(150)
    img.gamma_correct(1.6)
    img.rotate(45)
    img.save("horse_thumb45.jpg")
end

Note that the Ruby Devil implementation also supports the traditional DevIL API for greater control (though extra fiddliness):


IL.Init

name = IL.GenImages(1)[0]
IL.BindImage(name)
IL.LoadImage("horse.png")

ILU.ImageParameter(ILU::FILTER, ILU::SCALE_BSPLINE)

ILU.Scale(100, 100, 1)
IL.Enable(IL::FILE_OVERWRITE)
IL.SaveImage("horse_scale.png")

Requirements:

Installation:

Ubuntu/Debian systems:

  • aptitude install libdevil1c2 libdevil-dev

Gentoo systems:

  • emerge “media-libs/devil”

Macosx systems:

  • sudo port install libdevil

Windows systems:

For other systems:

Installing The Gem:

After setting up the DevIL library (as described above) you need to install the Ruby Devil gem:

  • gem install devil

Gosu Compatibility:

If you wish to use Devil with the Gosu game development library you will also need the following gems:

  • Gosu (gem install gosu)
  • TexPlay (gem install texplay)
  • ruby-opengl (gem install ruby-opengl)

Devil augments the Gosu library in a few ways. It lets you load image files in multiple formats (Gosu itself is restricted to png and bmp). It lets you *save* Gosu::Image objects to files (only useful if the images have first been manipulated using TexPlay). Devil also provides a ‘screenshot’ method to the Gosu::Window object enabling you to take screenshots of your gameplay and save it to a file.

Note that if you combine the use of Devil with TexPlay you get a fairly complete image manipulation framework. TexPlay enhances the basic functionality of Devil, providing the ability to draw lines, circles, rectangles, bezier curves, polygons, composites, turtle graphics, l-systems and more. See TexPlay for more information.

Rdoc:

Reasonably comprehensive documentation for the high-level Devil wrapper (not the low-level API) can be found here: http://rdoc.info/projects/banister/devil

Credits:

This Ruby Devil gem is heavily based on the ruby-devil project started by Jaroslaw Tworek but abandoned in 2006.

Some elements of the Devil API  are based on the image_science project by seattlerb.

Project Page:

The github project can be found here: http://github.com/banister/devil

If you have any problems please file an issue on that page. Devil is a relatively new project and there may be some minor (or even major 🙂 ) bugs still present.

40 Responses to “The DevIL Image Library For Ruby”

  1. How does it compare to RMagick perfomance-wise?

    • i just performed some relatively informal benchmarks on RMagick and DeviIL.

      According to my benchmarks DevIL is, on average, roughly 2 times faster than RMagick. Certain benchmarks I performed I had to keep the number of iterations very low as RMagick would grind my system almost to a halt (the result of a memory leak?)

      Anyway, you can see some of my results in the gists here:


      # NOTE: had to keep the number of iterations very very low or my system would grind to a halt
      ruby bench_rotate.rb
      performing 5 iterations…
      user system total real
      Devil 0.650000 0.110000 0.760000 ( 1.114564)
      RMagick 5.360000 0.690000 6.050000 ( 7.602843)

      view raw

      gistfile1.txt

      hosted with ❤ by GitHub


      require 'rubygems'
      require 'benchmark'
      require 'RMagick'
      require 'devil'
      N = 5
      puts "performing #{N} iterations…"
      @dimg = Devil.load("tank.png")
      @rimg = Magick::Image.read("tank.png").first
      Benchmark.bm do |x|
      x.report("Devil") {
      N.times {
      @dimg.rotate(-45)
      }
      }
      x.report("RMagick") {
      N.times {
      @rimg.rotate!(45)
      }
      }
      end

      view raw

      gistfile2.rb

      hosted with ❤ by GitHub

      http://gist.github.com/211451
      http://gist.github.com/211449


      ruby bench_negate.rb
      performing 30 iterations…
      Rehearsal ——————————————-
      Devil 0.110000 0.090000 0.200000 ( 0.264404)
      RMagick 0.250000 0.230000 0.480000 ( 0.598489)
      ———————————- total: 0.680000sec
      user system total real
      Devil 0.090000 0.080000 0.170000 ( 0.244527)

      view raw

      gistfile1.txt

      hosted with ❤ by GitHub


      require 'rubygems'
      require 'benchmark'
      require 'RMagick'
      require 'devil'
      N = 30
      puts "performing #{N} iterations…"
      @dimg = Devil.load("texture.png")
      @rimg = Magick::Image.read("texture.png").first
      Benchmark.bmbm do |x|
      x.report("Devil") {
      N.times {
      @dimg.dup.negative
      }
      }
      x.report("RMagick") {
      N.times {
      @rimg.negate
      }
      }
      end

      view raw

      gistfile2.rb

      hosted with ❤ by GitHub

      • Is the difference in speed due to the underlying C libraries, or is it the Ruby implementation? I’ve seen mention that DevIL is faster, but also less stable. Also, the maintainer of DevIL says that he will not be working on it for a time-being.

        You can see some more comparisons here:
        http://www.wikivs.com/wiki/DevIL_vs_ImageMagick

        It seems like ImageMagick’s strong user base and broader support for file formats makes it a better choice for common image resizing problems. If you need features beyond basic file resizing, go with DevIL.

      • Hi,

        The difference in speed is due to the underlying C libraries.

        I disagree that ImageMagick is a better choice for common image resizing problems. In fact, common and simple applications like image resizing or rotations are exactly DevIL’s forte 🙂

        While it is true that ImageMagick supports more image formats, 90% of these are probably obscure and not used much in practice. DevIL on the other hand supports 44 formats for reading and 17 for writing. I very much doubt that an ordinary user will come across an image format that DevIL does not support 🙂

        ImageMagick also has a LOT more functionality than DevIL and 95% of ImageMagick’s capabilities will just go unused if you’re just doing basic things like scaling and rotations, etc.

        DevIL is in general much simpler, leaner, faster, more lightweight than ImageMagick and does not suffer from the memory leaks that plague ImageMagick.

        As a result of DevIL’s simpler design and smaller scope it is also significantly easier to install than ImageMagick.

        Thanks for the link to that wiki, however I have never seen it stated anywhere that DevIL is unstable, nor does it say it is in the wiki. DevIL is nearly a decade old and has reached a very high degree of maturity and stability. Users do not need to be concerned about DevIL’s stability 🙂

        Thanks for your comment and the links 🙂

  2. Personally I don’t care about performance _too_ much. I’m concerned about memory usage/leaks. Just today I was asking about the “nokogiri” for image editing, so I’ll be checking Devil out 🙂

    • DevIL is quite well known for its maturity and stability so in theory there shouldn’t be any issues with memory leaks. However, I am relying on Ruby’s finalizers (ObjectSpace.define_finalizer) to clear up gc’d Devil::Image objects and I have not yet run any tests to see just how well they do their job. I will be undertaking extensive benchmarking and testing in the next few days though to determine exactly what needs to be improved or reworked.

      Remember that DevIL itself is quite a simple library with a primary focus on just loading and saving images. It does include some rather basic image manipulation functionality though (see the rdoc) but nothing rivaling the power of RMagick.

      In addition to DevIL, then, I have also written a more fully featured image manipulation library called TexPlay. Currently TexPlay is very tightly coupled to the Gosu game development framework but in the coming weeks I hope to decouple it from Gosu and wire it up properly to DevIL for more efficient and flexible image manipulation. You can still use TexPlay in its current form, however, but it requires Gosu to interface to DevIL and is also limited to an image size of roughly 1022 x 1022.

  3. Any chance to get this to work on Snow Leopard?

    I installed the libs with MacPorts but “sudo gem install devil” results in a error:

    Building native extensions. This could take a while…
    ERROR: Error installing devil:
    ERROR: Failed to build gem native extension.

    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb
    platform is universal-darwin10.0
    checking for main() in -lDevIL… no
    *** extconf.rb failed ***
    Could not create Makefile due to some reason, probably lack of
    necessary libraries and/or headers. Check the mkmf.log file for more
    details. You may need configuration options.

    Provided configuration options:
    –with-opt-dir
    –without-opt-dir
    –with-opt-include
    –without-opt-include=${opt-dir}/include
    –with-opt-lib
    –without-opt-lib=${opt-dir}/lib
    –with-make-prog
    –without-make-prog
    –srcdir=.
    –curdir
    –ruby=/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
    –with-DevILlib
    –without-DevILlib

    Gem files will remain installed in /Library/Ruby/Gems/1.8/gems/devil-0.1.6 for inspection.
    Results logged to /Library/Ruby/Gems/1.8/gems/devil-0.1.6/ext/devil/gem_make.out

    • argh, i think i see the problem. I check for a RUBY_PLATFORM match to /win/ hoping to just single out the windows platform, but of course it catches darwin too. In macosx i think the library is called simply IL (whereas in windows it’s called DevIL, hence the reason for the different treatment).

      I’ll roll out a new gem sometime in the next few hours. sorry about that.

    • Hi,

      I uploaded a new gem. You should def. remove any trace of the last attempt and perhaps clear cache as i did not bump the version number of the replacement.

      let me know if it works!
      thanks

      • Sorry 4 the last response:

        bullet:gems root# sudo gem install devil
        Building native extensions. This could take a while…
        Successfully installed devil-0.1.6
        1 gem installed
        Installing ri documentation for devil-0.1.6…
        Installing RDoc documentation for devil-0.1.6…
        bullet:gems root#

        BINGO!!!

        Thanx a lot!!!

        (Rails 2.3.4, Snow Leopard)

      • Ok, there seems to be still a problem:

        Devil.with_image(“1.png”) do |img|
        img.thumbnail(150)
        img.gamma_correct(1.6)
        img.rotate(45)
        img.save(“1_thumb.jpg”)
        end

        —>>

        NameError (uninitialized constant StaticController::Devil):

    • try ::Devil instead.

    • do you still have your error or is it working now? If you do have an error please post more information on it. Also, out of interest….did you remember to require ‘devil’ ? 😉

      • Great!!!

        “::Devil” did the trick!!

        ::Devil.with_image(“horse.png”) do |img|
        img.thumbnail(150)
        img.gamma_correct(1.6)
        img.rotate(45)
        img.save(“horse_thumb45.jpg”)
        end

        Thanx

  4. Hi,

    I want to use the IL.TexImage function.
    eg. IL.TexImage (1000, 1000, 0, 3, IL:: RGB, IL: UNSIGNED_BYTE, ?????)
    Something’s wrong! What is the last parameter?

    Thank you!

    • Hi,

      What exactly are you trying to do? This part of the API is inherited from the first implementation of ruby-devil which was by another programmer. I have not reworked this aspect of the API for this first release. However, to answer your question the last parameter is expecting a Wrapped C Struct that contains a pointer to the data. This may or may not be of use to you (look at ruby_il.c line 4 and line 111).

      Another function you may find more useful is IL.FromBlob() the parameters for this are: blob, width, height (in that order). The blob parameter is the image data stored as a Ruby String object, width and height parameters are the width and height of the image contained in that data.

      The FromBlob function expects that the data is formatted as RGBA and UNSIGNED_BYTE. FromBlob also creates and returns a NEW image (it does not modify the currently bound one).

      In a future release I will probably convert all the functions that expect a Wrapped C Struct to accept a Ruby String instead (where the image data is stored in the strings).

      If you tell me more information about what you are trying to do perhaps I can help more. 🙂

      Thanks

      • Thank you for your reply.

        I would like an existing image in a new picture up.
        The parts of the copy image. IL.Blit I’d like to use this function.

        And in the end to save the new image.

        Thanks

    • Hi,

      If you want to copy an image use IL.CloneCurImage

      🙂

      • Hi,

        I use a different resolution images.

        The source image – 1, eg : 500×400 pixels
        The source image – 2, eg : 300×100 pixels
        The destination image, eg. 1000×1000 pixels

        And here I want to use the IL.Blit function.

        Thanks

    • Just a note, if you are still having trouble getting this to work in DevIL (perhaps due to an incomplete API, i’m still working on it…) you may instead like to have a go with TexPlay. TexPlay is however limited to images of 1022×1022 but it has quite a nice interface. Check out the project page: https://banisterfiend.wordpress.com/2008/08/23/texplay-an-image-manipulation-tool-for-ruby-and-gosu/

      • Hi,

        All right. There is no error in the code. Works. Thank you very much.

        Note: I use a transparent PNG.

    • Hi,

      The following code should do what you want:

      # Note: that in this code, ‘green’ is the destination image, and red and blue
      # are source images.

      IL.Init

      red, green, blue = IL.GenImages(3)

      IL.BindImage(red)
      IL.LoadImage(“red.png”)
      red_width = IL.GetInteger(IL::IMAGE_WIDTH)
      red_height = IL.GetInteger(IL::IMAGE_HEIGHT)

      IL.BindImage(blue)
      IL.LoadImage(“blue.png”)
      blue_width = IL.GetInteger(IL::IMAGE_WIDTH)
      blue_height = IL.GetInteger(IL::IMAGE_HEIGHT)

      IL.BindImage(green)
      IL.LoadImage(“green.png”)

      IL.Blit(red, 0, 0, 0, 0, 0, 0, red_width, red_height, 1)
      IL.Blit(blue, red_width, 0, 0, 0, 0, 0, blue_width, blue_height, 1)

      IL.SaveImage(“blit.png”)

    • Hi,

      The new Devil 0.1.8 API now supports blitting, you can do the following:

      Devil.with_image(“canvas.png”) do |img|
      source1 = Devil.load(“source1.png”)
      source2 = Devil.load(“source2.png”)
      img.blit source1, 0, 0
      img.blit source2, 150, 150, :crop => [50, 50, 100, 100]
      img.save(“canvas_blit.png”)
      end

      See the updated rdoc for more info 🙂

  5. Hello,

    whenever I ran the same code you supplied, I get this:

    /Users/Matt/.gem/ruby/1.8/gems/devil-0.1.6/lib/devil.bundle: [BUG] Segmentation fault
    ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin10.0.0]

    Abort trap

    any suggestions?

    -Matt.

    • Nevermind, I got it working, but when I ran the code using a .png inside the with_image parameters, it gave me a runtime. However, when I ran the code using a .jpg in the parameters, it worked perfectly.

  6. it works cool on my macbook 🙂

    looking forward for texplay

  7. I sent you this on github, but this is probably a more appropriate place.

    I’m loving the api, and seems well built, but I cannot generate images with decent quality. Is there anything I can do to make the resulting image quality any better?

    Im currently using it on my macbook in an irb session, installed using mac ports.

    • Hi,

      Yes the image quality for scaling can be changed using the low-level API (i will wrap some of this up into the high level api for the next release).

      Here is a list of all the scaling algorithms supported by DevIL:
      ILU::NEAREST
      ILU::LINEAR
      ILU::BILINEAR
      ILU::SCALE_BOX
      ILU::SCALE_TRIANGLE
      ILU::SCALE_BELL
      ILU::SCALE_BSPLINE
      ILU::SCALE_LANCZOS3
      ILU::SCALE_MITCHELL

      You currently initialize those scaling algorithms using, for example:
      ILU.ImageParameter(ILU::FILTER, ILU::SCALE_MITCHELL)

      As I said before, I will wrap these up into the high level API for the next release.

      Other features coming to the high level API for the next release include:
      * image blitting (otherwise known as composite)
      * mirroring
      * embossing
      * edge detection
      * ability to enlarge canvas

      and a few other things

      🙂

      • Version 0.1.8 now released.

        Check out the rdoc: http://rdoc.info/projects/banister/devil for full details on new functionality.

        Notable changes:
        * default scaling algorithm is now lanczos3 (for very high quality thumbnails)
        * blitting (composite) functions now available in high-level API
        * Devil.set_options() method now exists for better control of Devil
        * embossing filters and edge detection, etc

  8. I’m taking a 200×160 12k image and creating two thumbnails, 250 and 100. The resulting files are much larger (37k, 12k) than those generated with mini_magick (8k, 4k) and lower quality visually IMO (although Devil is much faster). Others may want to do some A/B testing before deciding which image library to use.

    Tips on how to optimize Devil for image quality and size, not speed, would be appreciated.

    • hmm that’s odd that the quality is lower since i set the most recent gem release of Devil to use the Lanczos filter, which is the same as that used by ImageMagick by default. You could try playing around with the different scale filters – Devil supports nine – and see what you get.

      e.g: img.thumbnail(100, :filter => Devil::SCALE_MITCHELL) etcetera
      (a full list of filters is found in the rdoc)

      However, I corresponded with a user who was using Macosx and he stated that the various filter options had little to no effect and all seemed to behave as Devil::BILINEAR.

      On my system, linux / debian the filters do have a marked effect with lanczos3 being the superior quality filter IMO.

      If you are running a macosx system and also having trouble with the filters then I am assuming this is a problem with the macosx (macports) Devil package.

      If you like you can send me the images you are trying to scale and I can play around with them. The quality of the thumbnailed images should be at least equivalent to those generated with ImageMagick

    • I just noticed the different scale filters are ignored on windows too – they all just behave as ILU::NEAREST – which is the lowest quality.

      This is really bizarre, I’ll do some research on it and get back to you with what’s going on.

      • Hei Yeah mans – what is it you saying? This is good code Banister. Please keep blog all this good advice and insite of yours.

        Very good day now.

    • @Mike

      One fix to get reasonable quality thumbnails on macosx/macports (much better than the IL::NEAREST filter) is to blur the image before thumbnailing, try something like:

      img.blur(2).thumbnail(100)

      You may have to play around with parameters to #blur() to get the outcome you want.

      Hopefully the macports DevIL maintainers will upgrade the package very soon, I’m convinced it’s the out of date macports package that’s responsible for the low quality scale filters on macosx.

  9. Hello,

    I’ve just used you’re cool wrapper to do some image processing (a lot nicer than in C/C++) but I came across something which may anyone else looking at this some time.

    The blit function will blend the source image into the destination image by default using the current clear colour setting (i think). Anyway I needed a straight copy preserving the alpha channel in my source data and discovered there is already a flag/option which can be disabled to copy the data. Its called IL_BLIT_BLEND.

    Its a single line update in ruby_li.c:

    rb_define_const(mIL, “BLIT_BLEND”, INT2NUM(IL_BLIT_BLEND));

    now in my main ruby code I can use IL.Disable(IL::BLIT_BLEND) to get the blit I want.

    I’ll try and submit a change using github.

    Thanks again for the ruby wrapper.

    • Heya,

      sure just fork it on github and send me a pull request, alternately i could just add the line, up to you 🙂

      thanks

      • I’ve never made a github commit before and I’ve got some other stuff to do. I took a quick look at compiling and adding tests but my environment needs to be fixed to make it work.

        I’ll add it to my TO-DO list, and If you haven’t added it by then I’ll try to add it.

        Thanks.

Trackbacks

Leave a reply to Senthil Nayagam Cancel reply