RMagick/Imagemagick text-processing 对于 Heroku 来说太慢了

RMagick/Imagemagick text-processing too slow for Heroku

日本有大量的送礼文化,每年我们都必须打印大量这样的 "Noshi"。我制作了一个简单的 rails 程序,用于将文本添加到空白的 noshi 图像以添加到我们的系统(已内置 rails)。

作为参考,基本上我想制作一个没有水印的开放版本:www.noshi.jp

控制器的外观如下: 定义创建 @noshi = Noshi.new(noshi_params)

  # Set up variables
  ntype = @noshi.ntype
  omote = @noshi.omotegaki
  olength = omote.length
  opsize = (168 - (olength * 12))
  namae = @noshi.namae
  namae2 = @noshi.namae2
  # namae3 = @noshi.namae3
  # namae4 = @noshi.namae4
  # namae5 = @noshi.namae5

    replacements = [ ["(株)", "㈱"], ["(有)", "㈲"] ]
    replacements.each {|replacement| namae.gsub!(replacement[0], replacement[1])}
    replacements.each {|replacement| namae2.gsub!(replacement[0], replacement[1])}
    # replacements.each {|replacement| namae3.gsub!(replacement[0], replacement[1])}
    # replacements.each {|replacement| namae4.gsub!(replacement[0], replacement[1])}
    # replacements.each {|replacement| namae5.gsub!(replacement[0], replacement[1])}

    names = []
    names += [namae, namae2] # removed namae3, namae4, namae5 for the time being
    longest = names.max_by(&:length)
    nlength = longest.length
  npsize = (144 - (nlength * 12))
  i = 0
  # Pull Noshi Type
  noshi_img = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi#{ntype}.jpg")
    # Resize to A4 @ 300dpi
    noshi_img.resize "2480x3508"
    # Iterate through each character
    omote.each_char do |c|
      # Open new blank/transparent noshi
      chars = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
      chars.resize "2480x3508"
      # Draw Each Omotegaki Character
      chars.combine_options do |d|
        d.gravity 'North'
        # Placement based on point size
        plcmnt = ((opsize / 12 * 12) + (opsize * i * 1.2))
        d.draw "text 0,#{plcmnt} '#{c}'"
        d.font 'TakaoPMincho'
        d.pointsize opsize
        d.fill("#000000")
        i += 1
      end
      # Composite each letter as iterated
      noshi_img = noshi_img.composite(chars) do |comp|
        comp.compose "Over"    # OverCompositeOp
        comp.geometry "+0+0" # copy second_image onto first_image from (0, 0)
      end
    end
  # Iterator Reset
  i = 0
    # Draw Name Text (Line 1)
    namae.each_char do |c|
      # Iterate through each character
      # Open new blank/transparent noshi
      chars = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
      # Resize to a square so it's easy to flip
      chars.resize "2480x3508"
      chars.combine_options do |d|
      # Middle position for first line so set to 0
      xplcmnt =  (npsize / 12) * 0
      yplcmnt = (625 - npsize) - (npsize * i)
        d.gravity 'south'
        # Placement based on point size, fix for katakana dash
        # positive x is 
        if c == 'ー' 
          yplcmnt += 15
          d.draw "text 0,#{yplcmnt} '|'"
          d.pointsize (npsize * 0.85)
        else
          d.draw "text 0,#{yplcmnt} '#{c}'"
          d.pointsize npsize
        end
        d.font 'TakaoPMincho'
        d.fill("#000000")
        i += 1
      end
      # Composite each letter as iterated
      noshi_img = noshi_img.composite(chars) do |comp|
        comp.compose "Over"    # OverCompositeOp
        comp.geometry "+0+0" # copy second_image onto first_image from (0, 0)
      end
    end
  # Iterator Reset
  i = 0
    # Draw Name Text (Line 2)
    namae2.each_char do |c|
      # Iterate through each character
      # Open new blank/transparent noshi
      chars = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
      # Resize to a square so it's easy to flip
      chars.resize "2480x3508"
      chars.combine_options do |d|
      # Next position for second line so set by font size
      xplcmnt = (npsize / 6) - npsize * 1.45
      yplcmnt = (625 - (npsize * 2)) - (npsize * i)
        d.gravity 'south'
        # Placement based on point size, fix for katakana dash
        if c == 'ー' 
          yplcmnt += 15
          d.draw "text #{xplcmnt},#{yplcmnt} '|'"
          d.pointsize (npsize * 0.85)
        else
          d.draw "text #{xplcmnt},#{yplcmnt} '#{c}'"
          d.pointsize npsize
        end
        d.font 'TakaoPMincho'
        d.fill("#000000")
        i += 1
      end
      # Composite each letter as iterated
      noshi_img = noshi_img.composite(chars) do |comp|
        comp.compose "Over"    # OverCompositeOp
        comp.geometry "+0+0" # copy second_image onto first_image from (0, 0)
      end
    end

  # Setup and save the file
  noshi_img.format "png"
  fname = "#{@noshi.omotegaki}_#{@noshi.namae}"
  dkey = Time.now.strftime('%Y%m%d%H%M%S')
  ext = '.png'
  finlname = fname + dkey + ext
  noshi_img.write finlname
  @noshi.image = File.open(finlname)
  File.delete(finlname) if File.exist?(finlname)

respond_to do |format|
  if @noshi.save
    format.html { redirect_to @noshi, notice: '熨斗が作成されました。' }
    format.json { render :show, status: :created, location: @noshi }
  else
    format.html { render :new }
    format.json { render json: @noshi.errors, status: :unprocessable_entity }
  end
end end

它是如何工作的。 1. 用户拍摄一个 noshi 背景,选择一个 noshi header 类型(为 お歳暮 or 祝い 或其他),并输入一个名字 2. 应用程序然后从 gCloud 中获取相应的文件作为 noshi 背景。 3. 应用程序获取每个字母,并根据字母总数和行数计算字体大小和位置。 4. 它获取一个空图像文件并将每个字母放在它自己的图像上,然后将它们全部合并成最终图像。

是的,有必要为每个字母制作一个新图像,因为据我所知,ImageMagick 没有 (right-side-up) 垂直文本格式(对于大部分的planet [China, Japan, Korea] 所以我很惊讶它没有这个。

这在开发中工作得很好,为了我们的目的,我不介意它很慢。但是,在 Heroku 上,如果处理时间超过 30 秒,这 return 就会出错,即使每次都正确创建了 noshi。

问题:

我读到 "scale" 而不是 "resize" 可能会有帮助,但看看我的代码,我觉得必须有一种更有效的方法来完成我在这里所做的事情。我尝试使用较小文件大小的基本图像,这没有太大帮助。

有没有更有效的方法?

如果没有,有没有办法让用户在某个地方等待 noshi 完成,这样就不会每次都 return 出错?

更新:

刚回来展示 Rails 控制器上的工作 Ruby 我最终得到了:

def create
        @noshi = Noshi.new(noshi_params)

        # Set up variables
        ntype = @noshi.ntype
        omote = @noshi.omotegaki
        omote_length = omote.length
        omote_point_size = (168 - (omote_length * 12))

        #make an array with each of the name lines entered
        name_array = Array.new
        name_array << @noshi.namae
        name_array << @noshi.namae2
        name_array << @noshi.namae3
        name_array << @noshi.namae4
        name_array << @noshi.namae5

        #replace multi-character prefixes with their single charcter versions
        #replace katakana dash with capital I
        #insert line breaks after each letter for Japanese vertical type
        name_array.each do |namae|
            replacements = [ ["(株)", "㈱"], ["(有)", "㈲"], ["ー", "|"] ]
            replacements.each {|replacement| namae.gsub!(replacement[0], replacement[1])}
        end
        def add_line_breaks(string)
            string.scan(/.{1}/).join("\n")
        end
        name_array.map!{ |namae| add_line_breaks(namae)}
        #add line breaks after each character for the omote as well
        omote = add_line_breaks(omote)
        #find the longest string (important: after the character concatenation) in the name array to calculate the point size for the names section
        name_array_max_length = (name_array.map { |namae| namae.length }).max
        name_point_size = (204 - (name_array_max_length * 10))
        #max omote size is 156, and the name needs to be an order smaller than that by default.
        if name_point_size > 108
            name_point_size = 108
        end

        # Pull Noshi Type
        noshi_img = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi#{ntype}.jpg")
        # Resize to A4 @ 300dpi
        noshi_img.resize "2480x3508"

        #create the overlay image
        name_overlay = MiniMagick::Image.open("#{ENV['GBUCKET_PREFIX']}noshi/noshi_blank.png")
        name_overlay.resize "2480x3508"
        #first time for omote
        name_overlay.combine_options do |image|
            image.gravity 'North'
            # Placement based on point size
            omote_placement_y = (348 - (omote_length * (omote_point_size / 2)))
            image.font 'TakaoPMincho'
            image.pointsize omote_point_size
            image.fill("#000000")
            image.draw "text 0,#{omote_placement_y} '#{omote}'"
        end
        #count number of names in array, add a name for each time
        name_array.count.times do |i|
            name_overlay.combine_options do |image|
                image.gravity 'North'
                # Placement based on point size and iteration
                name_placement_x = (0 - i * name_point_size)
                name_placement_y = 1150 + ((i * name_point_size) - (name_point_size / 2))
                image.font 'TakaoPMincho'
                image.pointsize name_point_size
                image.fill("#000000")
                image.draw "text #{name_placement_x},#{name_placement_y} '#{name_array[i]}'"
            end
        end

        noshi_img = noshi_img.composite(name_overlay) do |comp|
            comp.compose "Over"    #OverCompositeOp
            comp.geometry "+0+0"   #copy second_image onto first_image from (0, 0)
        end


        # Setup and save the file
        noshi_img.format "png"
        #name the file
        fname = "#{@noshi.omotegaki}_#{@noshi.namae}"
        dkey = Time.now.strftime('%Y%m%d%H%M%S')
        ext = '.png'
        final_name = fname + dkey + ext
        #write a temporary version
        noshi_img.write final_name
        #write/stream the file to the uploader
        @noshi.image = File.open(final_name)
        #delete the original temporary
        File.delete(final_name) if File.exist?(final_name)

    respond_to do |format|
        if @noshi.save
        format.html { redirect_to @noshi, notice: '熨斗が作成されました。' }
        format.json { render :show, status: :created, location: @noshi }
        else
        format.html { render :new }
        format.json { render json: @noshi.errors, status: :unprocessable_entity }
        end
    end
    end

YES it is necessary to make a new image for each letter because as far as I can tell there is no (right-side-up) vertical text format for ImageMagick

在 ImageMagick 命令行中,您可以通过在每个字符后放置换行符来创建垂直对齐的文本字符串图像。

convert -background white -fill black -pointsize 18 -font arial -gravity center label:"t\ne\ns\nt\ni\nn\ng" result.png


这对你有帮助吗?或者这不实用?