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
这对你有帮助吗?或者这不实用?
日本有大量的送礼文化,每年我们都必须打印大量这样的 "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
这对你有帮助吗?或者这不实用?