• home
  • forum
  • my
  • kt
  • download
  • rmagick

    Author: 2007-08-25 16:54:28 From:

    This is a Ruby on Rails controller that produces PNG barcode images using RMagick (http://rmagick.rubyforge.org/) and Gbarcode (http://gbarcode.rubyforge.org/), the Ruby Gnu Barcode ( http://www.gnu.org/software/barcode/barcode.html ) wrapper. You will need to install RMagick and Gbarcode.

    On Mac OS X, you can use the Locomotive RMagick bundle (http://locomotive.raaum.org/bundles/index.html) if you install the gbarcode gem into it first, for instance:

    % export GEM_HOME=/Applications/Locomotive2/Bundles/rmagickRailsMar2007_i386.locobundle/framework/lib/ruby/gems/1.8/
    % gem install gbarcode
    


    To use it, save this code as barcode_controller.rb in the apps/controllers directory of your Rails application. Generate the barcodes using a URL like this:

    http://localhost:3000/barcode/send_barcode?string=A-string-to-encode

    This example uses the BARCODE_128 encoding; you can use different barcode encodings by adjusting the code.

    require 'RMagick'
    require 'gbarcode'
    
    class BarcodeController < ApplicationController
    
      def send_barcode
        @image_data = get_barcode_image
        send_data(@image_data, :type     => 'image/png',
                               :disposition => 'inline')
      end
    
      def get_barcode_image
        string_to_encode = params[:string]
        if string_to_encode.nil?
          string_to_encode = "No string specified"
        end
        eps_barcode = get_barcode_eps(string_to_encode)
        return convert_eps_to_png(eps_barcode)
      end
    
      def get_barcode_eps(string_to_encode)
        bc = Gbarcode.barcode_create(string_to_encode)
        Gbarcode.barcode_encode(bc, Gbarcode::BARCODE_128)
        read_pipe, write_pipe  = IO.pipe
        Gbarcode.barcode_print(bc, write_pipe, Gbarcode::BARCODE_OUT_EPS)
        write_pipe.close() 
        eps_barcode = read_pipe.read()
        read_pipe.close()
        return eps_barcode
      end
      
      def convert_eps_to_png(eps_image)
        im = Magick::Image::read_inline(Base64.b64encode(eps_image)).first
        im.format = "PNG"
        read_pipe, write_pipe = IO.pipe
        im.write(write_pipe)
        write_pipe.close() 
        png_image= read_pipe.read()
        read_pipe.close()
        return png_image
      end
      
    end
    

    install rmagick ubuntu

    let's undo some of the damage (just in case):
    sudo apt-get remove --purge librmagick-ruby-doc librmagick-ruby1.8
    


    Next let's get the version we need:
    sudo apt-get install libmagick9-dev ruby1.8-dev
    


    Lastly, go grab rmagick, the gem
    sudo gem install rmagick
    

    Avatar Resizer

    I go on a lot of Bulletin Board, every has its own limits of size for the avatars, instead of the resize manually I created a script which does it for me with RMagick

    #!/usr/bin/ruby
    require "RMagick"
    $SIZES = [80 , 100 , 110 , 128]
    
    if !ARGV[0]
      puts "Usage: mk_avatars.rb SourceAvatarPath"
      exit
    end
    
    image = Magick::Image.read(ARGV[0]).first
    $SIZES.each do |sz|
      puts "Generating Avatar : #{sz}"
      out = image.thumbnail(sz,sz)
      file = "out_#{sz}.#{image.format}"
      out.write(file)
    end
    

    Garmin Forerunner TCX file processing

    This is a small ruby file I wrote to process the TCX file that I can download from MotionBased website that is processed from the data off my Garmin Forerunner 305. It processes the XML file and generates a little badge/infographic that I can put on a website.

    // insert code here..
    require "date"
    require "rexml/document"
    require 'rubygems'
    require 'RMagick'
    
    class ForeRunner
      
      M2MI = 1609.344 # meters to miles
      MINIMUM_LAP_TIME = 300 # minimum seconds to count as a full lap
      
      attr_accessor :laps, :total_time, :distance, :calories
      
      def initialize(file)
        @source_doc = REXML::Document.new file
        
        @laps = 0
        @lap_times = Array.new
        @lap_bpm = Array.new
        @full_laps = 0
    
        @total_time = 0    
        @distance = 0
        @calories = 0
        @map_data = []
        
        self.process_file  
      end
      
      def generate_infographic(output_filename)
        canvas = Magick::Image.new(250, 80)
        map_size = 50
        map_color = 'green'
    
        max_lap_height = 25
        
        gc = Magick::Draw.new
    
        # Draw ellipse
        gc.stroke('grey50')
        gc.stroke_width(2)
        gc.fill_opacity(0)
    
        # draw the relative lap times
        lap = 0
        max_time = @lap_times.max
        @lap_times.each do |s|
          lap += 1
          x = 10 + (lap * 5)
          y = 60 - (s / (max_time / max_lap_height))
          gc.line(x, 60, x, y)
        end
    
        #draw the heartbeat avg
        gc.stroke('#c9a')
        
        lap = 0
        lx = nil
        ly = nil
        max_bpm = @lap_bpm.max
        min_bpm = @lap_bpm.min
        
        max_bpm_height = 18
        
        @lap_bpm.each do |s|
          lap += 1
          x = 10 + (lap * 5)
          y = 21 - ( (s - min_bpm) / ((max_bpm - min_bpm) / max_bpm_height))
          if !lx
            lx = x
            ly = y
          end
    
          gc.line(lx, ly, x, y)
    
          lx = x
          ly = y
        end
    
        # draw the map
        lat_diff = (@map_data['max_lat'] - @map_data['min_lat']).abs
        lon_diff = (@map_data['max_lon'] - @map_data['min_lon']).abs
    
        lat_off = lat_diff / map_size
        lon_off = lon_diff / map_size
        
        gc.fill(map_color)
        @map_data['map_data'].each do |i|
          lt = (map_size - ((i[0] - @map_data['min_lat']) / lat_off)).round
          lg = ((i[1] - @map_data['min_lon']) / lon_off).round
          gc.point((240 - map_size) + lg, (65 - map_size) + lt)
        end
    
        # Annotate
        gc.stroke('transparent')
        gc.fill('black')
        gc.text(120, 15, @distance.to_s[0, 5] + ' mi')
        gc.text(120, 30, (@total_time / 60 / 60).to_s[0, 4] + ' hr')
        gc.text(120, 45, @calories.to_s + ' cal')
    
        gc.fill('#555')
        total_hr = 0 
        @lap_bpm.each { |hr| total_hr += hr }
        gc.text(15, 32, 'avg bpm: ' + (total_hr / @laps).round.to_s)
        gc.text(15, 75, 'avg pace: ' + ((@total_time / @full_laps) / 60).to_s[0, 4] + ' min/lap')
    
        gc.draw(canvas)
        canvas.write(output_filename)
      end
      
      protected 
      
      def process_file
        @source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/*') do |element| 
          if element.name == "TotalTimeSeconds"
            @lap_times << element.text.to_f
            if element.text.to_f > 300  # for removing warmup and warmdown laps
              @total_time += element.text.to_f
              @full_laps += 1
            end
            @laps += 1
          end
    
          if element.name == "DistanceMeters" 
            @distance += (element.text.to_f / M2MI)
          end
    
          if element.name == "Calories" 
            @calories += element.text.to_f
          end
    
          if element.name == "AverageHeartRateBpm" 
            element.elements.each('Value') { |v| @lap_bpm << v.text.to_f }
          end
        end
        @map_data = self.generate_map_points
      end
      
      def generate_map_points  
        map = []
    
        max_lat = -300
        max_lon = -300
        min_lat = 300
        min_lon = 300
    
        @source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/Track/Trackpoint/*') do |element|
          if element.elements['LatitudeDegrees']
            lat = element.elements['LatitudeDegrees'].text.to_f
            lon = element.elements['LongitudeDegrees'].text.to_f
    
            map << [lat, lon]
            max_lat = lat if (lat > max_lat) 
            max_lon = lon if (lon > max_lon) 
            min_lat = lat if (lat < min_lat) 
            min_lon = lon if (lon < min_lon) 
          end
        end
        {'map_data' => map, 'max_lat' => max_lat, 'min_lat' => min_lat, 'max_lon' => max_lon, 'min_lon' => min_lon}
      end
      
    
    end
    
    
    if ARGV[0].nil? 
      puts "This program takes one argument, a Garmin Forerunner TCX File"
      puts "Like this: run.rb filename.tcx"
      exit
    end
    
    output_filename = ARGV[0].gsub(/\.tcx$/, "-" + Time.now.strftime("%Y%m%d") + ".png")
    puts "Generating Graph #{output_filename}"
    
    fr = ForeRunner.new(File.new(ARGV[0]))
    fr.generate_infographic(output_filename)
    

    generic file and image models for uploaded files

    These are basic models that store a file in a dedicated files table. Use has_one or has_many to associate this with your actual models. RMagick is required for images.

    This is my first code dealing with uploads and rmagick, so please comment if you have suggestions.

    class DbFile < ActiveRecord::Base
      IMAGE_TYPES = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png']
      before_validation     :sanitize_filename
      validates_presence_of :size, :filename, :content_type
    
      class << self
        def new_file(file_data)
          content_type = file_data.content_type.strip
          (IMAGE_TYPES.include?(content_type) ? DbImage : DbFile).new \
            :data         => file_data.read,
            :filename     => file_data.original_filename,
            :size         => file_data.size,
            :content_type => content_type
        end
      end
    
      protected
      def sanitize_filename
          # NOTE: File.basename doesn't work right with Windows paths on Unix
          # get only the filename, not the whole path
          filename.gsub! /^.*(\\|\/)/, ''
    
          # Finally, replace all non alphanumeric, underscore or periods with underscore
          filename.gsub! /[^\w\.\-]/, '_'
      end
    end
    
    require 'rmagick'
    require 'base64'
    class DbImage < DbFile
      def data=(file_data)
        with_image(file_data, true) do |img|
          self.width  = img.columns
          self.height = img.rows
        end
      end
    
      def with_image(file_data = nil, save_image = false, &block)
        img = Magick::Image::read_inline(Base64.b64encode(file_data || self.data)).first
        block.call(img)
        write_attribute('data', img.to_blob) if save_image
        img = nil
        GC.start
      end
    end


    Controller Usage:

    # returns DbImage if content_type matches
    db_file = DbFile.new_file(params[:file][:data])
    db_file.save


    Model Usage:

    # raw binary image data
    File.open('my_file', 'w') { |f| f.write(db_file.data) }
    
    # Image resizing with rmagick
    # automatically creates RMagick::Image and 
    # invokes GC.start
    db_file.with_image do |img|
      img.scale(.25)
      img.write('thumb.jpg')
    end
    

    On-the-fly thumbnailer method for a Rails 'Photo' controller

    require 'RMagick'
    
    class PhotoController < ApplicationController
    
    [...snip...]
    
        def render_resized_image
                    @photo=Photo.find(@params["id"])
    
                    maxw = @params["width"] != nil ? @params["width"].to_i : 90
                    maxh = @params["height"] != nil ? @params["height"].to_i : 90
                    aspectratio = maxw.to_f / maxh.to_f
    
    
                    pic = Magick::Image.from_blob(@photo.image)[0]
    
    
                    picw = pic.columns
                    pich = pic.rows
                    picratio = picw.to_f / pich.to_f
    
                    if picratio > aspectratio then
                            scaleratio = maxw.to_f / picw
                    else
                            scaleratio = maxh.to_f / pich
                    end
    
                    #breakpoint
    
                    thumb = pic.resize(scaleratio)
    
                    @response.headers["Content-type"]=@photo.mime
        end
    end
    

    Requires RMagick

    Based on Thumbnailer in Ruby and RMagick

    Thumbnailer in Ruby and RMagick

    require 'RMagick'
    
    maxwidth = 120
    maxheight = 160
    aspectratio = maxwidth.to_f / maxheight.to_f
    imgfile = 'world'
    
    pic = Magick::Image.read(imgfile + '.jpg').first
    imgwidth = pic.columns
    imgheight = pic.rows
    imgratio = imgwidth.to_f / imgheight.to_f
    imgratio > aspectratio ? scaleratio = maxwidth.to_f / imgwidth : scaleratio = maxheight.to_f / imgheight
    thumb = pic.resize(scaleratio)
    
    white_bg = Magick::Image.new(maxwidth, thumb.height)
    pic = white_bg.composite(thumb, Magick::CenterGravity, Magick::OverCompositeOp)
    pic.write(imgfile + '.thumb.jpg')

    discuss this topic to forum

    relation tutorial

    No relevant information

    Category

      Database Related (2)
      Getting Started (7)
      Helpers (4)
      Image Manipulation (2)
      Security (4)

    New

    Hot