How to improve the image quality and generate random string image in the plugin validates_captcha
October 17, 2006
Hello Everyone !!
I have released a captcha plugin Simple Captcha. It is really simple to implement, and provides a cool feature of multiple styles of images.
Previous post about improving validates_captcha’s images
Validates captcha is a good plugin to implement captcha in your rails application.
However i found that there is repetition of the string of the image and the quality of image is not that good. To get a good quality image and random string
something like this

replace the code of the file /vendor/plugins/validates_captcha/lib/captcha_challenge.rb with the following code…
require 'digest/sha1'
module AngryMidgetPluginsInc #:nodoc:
# CaptchaChallenge
class CaptchaChallenge
include CaptchaConfig
extend CaptchaConfig
DEFAULT_TTL = 1200#Lifetime in seconds. Default is 20 minutes.
attr_reader :id, :created_at
attr_accessor :ttl
def initialize(options = {}) #:nodoc:
generate_id
options = {
:ttl => config['default_ttl'] || DEFAULT_TTL
}.update(options)
self.ttl = options[:ttl]
@created_at = Time.now
self.class.prune
end
# Implement in subclasses.
def correct? #:nodoc:
raise NotImplementedError
end
private
def generate_id #:nodoc:
self.id = Digest::SHA1.hexdigest(Time.now.to_s+rand.to_s)
end
def id=(i) #:nodoc:
@id = i
end
def write_to_store #:nodoc:
store.transaction{
store[:captchas] = Array.new unless store.root?(:captchas)
store[:captchas] << self
}
end
class << self
# Removes old instances from PStore
def prune
store.transaction{
if store.root?(:captchas)
store[:captchas].each_with_index{|c,i|
if Time.now > c.created_at+c.ttl
store[:captchas].delete_at(i)
end
}
end
}
end#prune
end#class << self
end
# A CAPTCHA challenge where an image with text is
# generated. A human can read the text with relative
# ease, while most robots can not. There are accessibility
# problems with this challenge, though, as people
# with reduced or no vision are unlikely to pass the test.
class CaptchaImageChallenge < CaptchaChallenge
WORDS = ‘gorilla costume, superman, banana bender, chuck norris, xray vision, ahoy me hearties,
chunky bacon, latex, rupert murdoch, clap your hands, year 2000,
sugar coated, coca cola, rastafarian, airbus a380′.split(/,s+/)
DEFAULT_DIR = ‘captcha’#public/images/captcha
WRITE_DIR = File.join(RAILS_ROOT, ‘public’, ‘images’)
DEFAULT_FILETYPE = ‘jpg’
attr_reader :image
attr_accessor :string, :dir, :filename, :filetype
# Creates an image challenge.
def initialize(options = {})
super
options = {
:string => config['words'] ? config['words'][rand(config['words'].size)] : WORDS[rand(WORDS.size)],
:dir => config['default_dir'] || DEFAULT_DIR,
:filetype => config['default_filetype'] || DEFAULT_FILETYPE
}.update(options)
self.string = Digest::SHA1.hexdigest(Time.now.to_s)[0..4].upcase #options[:string]
self.dir = options[:dir]
self.filetype = options[:filetype]
self.filename = options[:filename] || generate_filename
write_to_store
end
# Generates the image.
def generate(options = {})
options = {
:fontsize => 50,
:padding => 20,
:color => ‘#000′,
:background => ‘#fff’,
:fontweight => ‘bolw’,
:rotate => true
}.update(options)
options[:fontweight] = case options[:fontweight]
when ‘bold’ then 700
else 400
end
#added
text = Array.new
0.upto(4) do |i|
text[i] = Magick::Draw.new
text[i].pointsize = options[:fontsize]
text[i].font_weight = 600 #options[:fontweight]
text[i].fill = ‘black’ #options[:color]
text[i].gravity = Magick::CenterGravity
text[i].rotation = (rand(2)==1 ? rand(30) : -rand(30)) if options[:rotate]
end
metric = text[2].get_type_metrics(self.string)
#add bg
canvas = Magick::ImageList.new
fill = Magick::HatchFill.new(’white’,'black’)
x = metric.width+options[:padding]
y = metric.height+options[:padding]
#ADDING NOISE
img1 = Magick::Image.new(x,y,fill)
gc = Magick::Draw.new
gc.stroke_linejoin(’round’)
gc.stroke(’black’)
gc.stroke_width(1)
100.times do |i|
x1 = rand(x)
y1 = rand(y)
gc.circle(x1,y1,x1.next,y1)
end
gc.draw(img1)
canvas << img1
0.upto(4) do |i|
canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
self.background_color = ‘#000F’
y_loc = rand(y)
}.annotate(text[i], 0, 0, (i-2)*30, rand(y/4), self.string[i,1]) #.wave(5, 20)
end
canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
#changed
p = Magick::Pixel.from_color(options[:background])
p.opacity = Magick::MaxRGB
self.background_color = p
} #.add_noise(Magick::LaplacianNoise)
self.image = canvas.flatten_images.blur_image(1)
end
# Writes image to file.
def write(dir = self.dir, filename = self.filename)
self.image.write(File.join(WRITE_DIR, dir, filename))
end
# Determine if the supplied +string+ matches
# that used when generating the image.
def correct?(string)
string.downcase == self.string.downcase
end
# The full path to the image file, relative
# to <tt>public/images</tt>.
def file_path
File.join(dir,filename)
end
class << self
# Deletes old image files. Also calls CaptchaChallenge.prune
def prune
store.transaction{
if store.root?(:captchas)
store[:captchas].each_with_index{|c,i|
if Time.now > c.created_at+c.ttl
if File.exists?(File.join(WRITE_DIR, c.file_path))
begin
File.unlink(File.join(WRITE_DIR, c.file_path))
rescue Exception
end
end
end
}
end
}
super
end#prune
end#class << self
private
def generate_filename #:nodoc:
self.id+’.'+self.filetype
end
def image=(i) #:nodoc:
@image = i
end
end
end
visit Here to view the customized use of the plugin validates_captcha in RoR.










Rss Feeds
1.
Jerry | October 27, 2006 at 12:51 am
Hello,Sur
Can you send a mail about this correct source code to me?
When i put above code to my ruby editor,it show some error.
I think it has some syntax error.Maybe it be bring by html editor.
So,if you correct source code please send a copy to me.
Thank you so much!!!
wangchangxiao@hotmail.com
2.
Sur Max | October 27, 2006 at 5:09 am
Hi Jerry !!
Sorry if there is any mistake residing in the above code. I have also done some more modifications in the source code of this file to make the images more practically implementable. I will upload the new code here today itself, side by i will mail it to you also.
——————————————————————————
Hello everybody !!
Everyone who is reading this post, as jerry suggested… if you are getting any problem with the code provided here then let me know via comment(or else) and provide your email… so that i can mail you that modified file itself.
3.
Chintan Shah | November 14, 2006 at 2:13 pm
hi ,
it is a great thing and thnx a lot
can u help me for doing some changes in yr code.
1) how can i change the size of the image ie height and width?
2)how can i remove the black dots from the image that u r generating for the word?
4.
Sur Max | November 14, 2006 at 2:37 pm
Hi Chintan !!
Although i would recommend to use the dots coz there are some smart bots available which are based on pixel recognition and can even read the string from images if images are clean(without noise) and easy to read.
However remove this code from the code above given to remove the dots
100.times do |i|
x1 = rand(x)
y1 = rand(y)
gc.circle(x1,y1,x1.next,y1)
end
Do always remember to restart the server whenever you change the code of any plugin.
5.
Sur Max | November 14, 2006 at 2:39 pm
I havent explored the resizing yet…
hopefully i will tell you soon… and will upload the code also.
6.
Chintan Shah | November 14, 2006 at 3:15 pm
thnx Sur
7.
Laurent | December 2, 2006 at 2:20 pm
Hi,
I still have problems with the image (i have a lot of noise and no characters) even with the modified code of the class from this page.
Could you please send me the source code at laurent.bois@gmail.com
8.
Dirk | December 12, 2006 at 2:29 pm
I would like to use this too.
Could you please send me the source code at
diddek@gmail.com
Thanks in advance
Dirk
9.
Sur Max | December 12, 2006 at 4:39 pm
Hi Dirk !!
The modified code for the file capcha_challenge.rb is right there in the post itself. If you wanna see the usage of this plugin then see this post to implement captcha and then add the code given above, to improve the image quality !!
10.
Dirk | December 13, 2006 at 11:38 am
Dear ‘Sur Max’
I am sorry bu thte code provided above does not run ( `const_missing’: uninitialized constant CaptchaConfig (NameError))
The posts here mention the same, so I wondered if you could provide me with the working version
Can you either update the source in the post or mail me?
Dirk
11.
Sur Max | December 13, 2006 at 11:49 am
Hi Dirk !
I have mailed the captcha plugin which i am using with all the modified code. Try it by just putting it into vendor/plugin directory and restarting the server… Assuming u have written all the code in model and controller correctly it would work fine.
12.
Dirk | December 13, 2006 at 12:05 pm
Hi Sur Max
I am sorry if I dont get it, but what’s the difference with the official version?
The word list is the same in your version and the ‘official’ one:
WORDS = ‘gorilla costume, superman, chuck norris, xray vision, ahoy me hearties,
chunky bacon, latex, rupert murdoch, clap your hands, year 2000, disco rocks,
sugar coated, staple my ears, rastafarian, airbus a380, good old days’.split(/,\s+/)
I dont see a random text or the image like you display on top of this page.
Differences I do see is that the code is arranged differently and ‘namespaces’ have been changed (AngryMidget … instead of FleksPlugin) - what am I missing here?
Dirk
13.
Sur Max | December 13, 2006 at 12:09 pm
Yes u didnt get it !! lol …
The words written now are only text in the modified code and not being used anywhere, i am wandering if you are still getting the repeated words and not random string… probably u havnt restarted the server or u have not implemented it and just looking at code only …
14.
Chintan Shah | December 13, 2006 at 3:07 pm
Hi Sur,
i m in big problem, u have to help me at any how? check yr captcha code with the mongrel? it creates big problems so pls reply me soon i m waiting for reply
it gives me an error like “pid…….” and when i tried to run the method which contains the captcha code it just displays as blank page and when i go for the page source it showm me blank there also, and it also clear the clusters of the mongrel, from there mongrel will be stoped so when i tried to go some where else it gives me an error of 503…. so pls help me as soon as possible.
bye and thnx
15.
Matt | December 15, 2006 at 3:34 pm
Could you send me the code?
16.
Stabilo | January 1, 2007 at 5:14 pm
Got the same error as Dirk from above ( `const_missing’: uninitialized constant CaptchaConfig (NameError))
Any ideas? Seems to be some namespace problem
Regards
Stabilo
17.
Captcha in Ruby on Rails &hellip | January 31, 2007 at 9:01 am
[...] However image is too noisy and it contains repeated strings. To improve the quality of images generated by the plugin validates_captcha visit Here. [...]
18.
Eduard Martini | February 2, 2007 at 11:03 am
As you maybe saw, this code is not working. I made some adjustments and is working perfectly for me. I modified to not use words and use random chains of letters.
First in the CaptchaImageChallenge class replace constant ‘WORDS’ with this one:
LETTERS = %w[a b c d e f g h i j k l m n o p q r s t u v x z 1 2 3 4 5 6 7 8 9 0]
then replace all code from def generate(options = {}) with:
options.reverse_merge!(
:fontsize => 35,
:padding => 20,
:color => '#000',
:background => '#fff',
:fontweight => '500',
:rotate => true,
:font => config['font']
).symbolize_keys!
options[:fontweight] = case options[:fontweight]
when ‘bold’ then 700
else options[:fontweight].to_i
end
text = Magick::Draw.new
text.pointsize = options[:fontsize]
text.font_weight = options[:fontweight]
text.fill = options[:color]
text.gravity = Magick::CenterGravity
text.font = options[:font] if options[:font]
#rotate text 5 degrees left or right
text.rotation = (rand(2)==1 ? 5 : -5) if options[:rotate]
metric = text.get_type_metrics(self.string)
#add bg
canvas = Magick::ImageList.new
canvas
then add a private function to the class:
def make_string(arr)
size=rand(3)+4
str=""
1.upto(size) do |n|
str+=arr[rand(arr.size)].to_s
end
return str.upcase
end
Thats all
19.
Sur | February 2, 2007 at 11:11 am
Hi Eduard !!
Actually my code was running very fine with the previous version of this plugin…. and i am using the same version since then…. people started getting errors with the new version…. so as you have given the patch for the newer version it should run fine i suppose.
Thanks.
I am not sure if you have read the latest post here on this blog that i am moving my blog and also releasing my own captcha plugin that is really very simple to use and provides some cool image styles. I have also given sample images in the post above
20.
Neeraj | February 7, 2007 at 4:43 am
In my view unless you are the next yahoo, a simple image without much dots and distortion will serve the purpose. I downloaded your code and I like what I see.
I don’t want to put user through all this rigor if a simple text works. So I am making most of distortion , dots etc configurable. So next time if I am beaten by a blog you can just tune the configuration file without actually touching the code.
If time permits and if you like the idea I would suggest you to make some of the parameters configurable. That would be a great help.
Either way. Good work. Keep it up.
21.
Sur Max | February 7, 2007 at 4:47 am
Hey Neeraj !!
Check out my captcha plugin buddy.
I have just launched it today on my new blog….. its almost the same way u are suggesting… It provides multiple image styles to chose from..
Check this out here
22.
buy cialis online&hellip | March 8, 2007 at 5:36 am
buy cialis online