soledad penadés
repeat 4[fd 100 rt 90]

Archive for the ‘photo’ Category

20070627 Superminigallery: a gallery with ruby, rmagick and builder

Imagine you have been in a very nice place for holidays. You took a lot of pictures and want to show them to your family and friends, but you don't feel like using services like flickr or programs like iPhoto. You just want to put them in your own server and give the url to your friends.

What can you do? Well, you could do like me and create a little script to generate an HTML file, with thumbnails and even watermarked images (just in case some creepy individual decides to use your stuff without asking first).

Superminigallery thumbnail

Requirements

This script requires a couple of gems to be installed: RMagick and builder (but if you've done some stuff with Rails you might already have them). RMagick is used for dealing with the images and builder is used for generating the XHTML. This is because I didn't want to write any html by hand, with their less than and greater than signs, attributes, etc.

Using it

  1. Create a folder in your computer. For example: holidays.
  2. Then you copy there the pictures you want to show to the world.
  3. Open a terminal and cd to that directory. E.g.
    cd ~/Desktop/holidays
  4. Execute the script! E.g.
    ruby ~/code/superminigallery.rb
  5. Wait until it finishes

When it finishes you'll find there's an output folder in the holidays folder. That's where the index.html file, as well as all the thumbnails and watermarked images are. Simply upload the contents of this folder to your host and let everybody know about it!

Ok, but show me the code

The first lines act like a configuration area. You can change the output folder name, so that it is called superoutput, gallery, whatever you like (as long as it is a valid path name).

You may change the sizes of the generated pictures; these sizes are defined in the versions variable. Each pair means [width, height]. For example, the thumbnails are 300 pixels wide and 150 pixels high.

output_path = 'output'

versions = {
  'thumbnail' =>  [300,150],
  'big'       =>  [1024,768]
}

You can also configure which EXIF tags need to be retrieved. Since their names are too obscure for non-technical savvy people I decided to create this hash for storing the key (Exif tag) and the nice name to show with the value. So instead of showing DateTimeOriginal, it will simply output Taken.

exif_fields = {
  'Taken'     =>  'DateTimeOriginal',
  'Camera'    =>  'Model',
  'Exposure'  =>  'ExposureTime', 
  'Shutter Speed' =>  'ShutterSpeedValue'
}

There are way more tags you could show, but they can be confusing for normal people and only entertain geeks, so it's better to keep them down to a minimum.

Declare the builder object, and initialize it with the XHTML header.

x = Builder::XmlMarkup.new(:target=>xhtml, :indent=>1)

x.instruct!
x.declare! :DOCTYPE, :html, :PUBLIC, "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
x.html( "xmlns" => "http://www.w3.org/1999/xhtml" ) {

Now it would be amazing to have some styling in the page so that it doesn't look so ugly. We can put an style tag inside the head, and use the text! method for adding literal text to the builder object:

x.style( "type"=>"text/css" ) { x.text! "
      body{
        font-family:georgia,serif
      }
     
      h1,h2 {
        margin-top: 0;
      }
      …
      "
}

(the means there's more code but I have reduced it for clarity purposes)

Now, we need to create the output directory. I haven't bothered with outputting error messages if the directory already exists or anything. It will always try to create it:

begin
    FileUtils.mkdir output_path
  rescue
  end

We need to create a Magick::Draw object for watermarking the images, and define its parameters:

draw = Magick::Draw.new
draw.gravity = Magick::CenterGravity
draw.pointsize = 64
draw.font_family = "Helvetica"
draw.font_weight = Magick::BoldWeight
draw.stroke = 'none'
draw.fill = "#ffffff99"

Basically we are saying: use Helvetica bold 64pt, painting it with white (ffffff) and some transparency (99 for alpha channel). If you don't have Helvetica installed in your system, replace it with your favourite font.

(But since 2007 is Helvetica's 50th anniversary, you should do everything possible to use Helvetica!)

Now we open the current directory (where the script was executed) and find all files with jpg and JPG extensions, and sort them. That's because sometimes the images don't get listed in alphabetical order, and us humans like to see things in sequential order. Specially because they usually are numbered incrementally, and older numbers mean older images, so IMG001 should appear before IMG100.

Dir['*.jpg','*.JPG'].sort.each do |f|

Read each file into a Magick::Image object:

img = Magick::Image.read(f).first

And for each version…

versions.each do |k,v|

… create the version filename by appending the version name to the filename, like big_IMG_1234.jpg, and the output filename, by prepending the output path to the version filename:

version_file =  k + '_' + f
output_img_path = File.join(output_path, version_file)

If the version is 'thumbnail', we'll add the image metadata to the builder object. Note how you don't need to open or close tags, but just include things in blocks or parenthesis to get the mark up done.

if(k=='thumbnail')
        x.div('class'=>'picture') {
                x.h2(f)
                x.a('href'=> version_file.sub('thumbnail_', 'big_')) {
                        x.img('src'=>version_file)
                }
                x.dl {
                        x.dt('Dimensions')
                        x.dd(img.columns.to_s + ' x ' + img.rows.to_s)

                        exif_fields.each do |title, field|
                                key = "Exif:#{field}"
                                if img[key]!=nil
                                        x.dt(title)
                                        x.dd(img[key])
                                end
                        end
                }
        }
end

Resizing the image is as simple as

version = img.crop_resized(v[0], v[1])

crop_resized returns another Image object which we store in the version variable.

Now, if we are dealing with the 'big' version, we'll add the watermark that we prepared at the beginning. That is done with

if(k=='big')
        draw.annotate(version, 0, 0, 0, 0, "(c) soledadpenades.com")
end

You can replace my (c) soledadpenades.com with your text, of course!

And for writing the resulting image to disk:

version.write output_img_path

Very very important: do not forget to call the Garbage Collector. For some reason which I still haven't been able to elucidate, the RMagick gem leaks memory furiously. So if you forget to do a

GC.start

as I did with the first version of the script, your computer will mostly hung if you make it generate a lot of thumbnails. If you look at the current processes with top or a similar tool, you'll find a ruby process eating more and more memory with each picture it processes.

And finally, we just need to output the generated XHTML to index.html:

File.open(File.join(output_path, 'index.html'), 'w+') do |file|
  file.puts xhtml
end

Here's the result and here's the source code. With only 120 lines of code (excluding the license text :D), it's way easy to modify to suit your tastes.

Don't tell anyone but…

I must confess I got the inspiration for this from herotyc's jGallery. But he used a bash script and I thought there should be a way of doing the same with ruby :-)

20060909 Bath literature

This is a series of pictures shot at the toilettes of a pub in Old Street. I was so deeply moved by the message, and I also had my camera with me, so here you have the result!

Warning: depending on your specific case, this might not be work-safe.

I love old selotape!

I love old selotape

Someone proclaims her love for the most unsuspected item to think of in a night out (I love old selotape), someone else believes her (I believe you!), and someone else gets worried about the spelling of selotape (Is that spelt correctly?).

This one made me think a lot, not only because selotape is wrongly spelt (I think the right one is cellotape although wikipedia's cellotape also says sellotape is how it is known in UK), but because I tried to guess if that had a hidden message. Maybe wrapping people with old cellotape is the new sexual trend and we still don't know!!

Fuck like you're being filmed

Fuck like you're being filmed

Although being painted in a shy, pale purple, now that's a message! This one motivated a couple of replies in the following picture:

No. I don't fuck I make love / And I make love like I'm being filmed

No I don't fuck I make love / And I make love like I'm being filmed

First reply -No. I don't fuck I make love- is written with that kind of plastic paint that you used when you were a child. Then someone comes with an alternative and solomonic solution: And I make love like I'm being filmed. You can't argue against that one! So maybe that's why there are no follow-up's to the topic.

I wouldn't want to take me home

I wouldn't want to take me home

Another one which made think. Why not? Is she so drunk she doesn't want to take herself home, or she doesn't believe she is the right person to go home with - and has just discovered it in the bath, right now!?

Stop bitching, start a revolution

Stop bitching, start a revolution

Here we go with more strong messages. Probably she felt annoyed with so many trivial topics. Also I believe there's always a couple of this style of messages at every bath you go… something like Fuck the system, Viva la revolución, Hasta la victoria siempre, etc. So she wasn't that innovative at the end.

War 4 peace is like fuckin 4 virginity

War 4 peace is like fucking 4 virginity

And yes there are always mentions to war, peace, etc. But this one goes further and merges war, peace, fuck and virginity. What a bunch of controversial questions.

Alabama 3, I love you!!

Alabama 3, I love you!!

I presume this has some meaning. Which only the one which wrote the text can understand, because I particularly can't! Is there any movie called Alabama 3? and if there is, how come I haven't heard of Alabama and Alabama 2? Maybe it is a music group… yes… I just googled it: Alabama 3 is an english music group. As we say in Spain, you never end a day without learning something.

I like cocks but I think I would also like cunts. What should I do?

I like cocks but I think I would also like cunts. What should I do?

She's undecided. But she should have left a telephone number or e-mail address; these issues shouldn't be discussed publicly. Maybe that's why nobody replied.

I love Jerry

I love Jerry

She's got a clear mind. This is pure love, definitely. Innocence at its best.

Suck my tits / and lick my clit / eat my shit / and suck his duck

Suck my tits...

Modern poetry (or kind of…)

Baths are literature places, isn't it?

Baths are literature places

I can't agree more with her!

20060529 Real time Fairlight

I was coming back home past wednesday by St James's Park and I suddenly found this scene which reminded me to something I had seen before:Crown at St James's Park

What it was, I couldn't remember, until yesterday that I was feeling like playing some old games and downloaded some Amstrad CPC games. Then I remembered, when opening "Fairlight A Prelude". Look at this:

Fairlight

Although this screenshot is from the Spectrum version, but all of them were quite similar. Hehe…

20060501 My first application in Ruby On Rails

It's time to get serious with RoR! I finally decided that I would do some useful application to really learn how it works, and as I'm quite bored of doing applications for managing text (whether they are intranets, cms's, etc), I opted for recreating my old gallery script (which I did in php) in RoR. So I'm playing with images instead of just text.

This will give me the opportunity to test lots of extra functionalities of the framework, such as the famous integrated javascript effects, ajax, etc, as I want to improve my old gallery script, not simply redo it as it was in php.
So first thing I did was to make sure that I had a working updated version of ruby and rails. The best tutorial I have found which worked perfectly for me is this one: Building Ruby, Rails, LightTPD, and MySQL on Tiger, by Hivelogic. (Although I simply used it to update Ruby and Rails, but didn't build Lighttpd or Mysql).

With that I could be able to follow the recent tutorials and techniques, specially because I wanted to learn how to use a Login Generator, and there was something in the version of Ruby that I had which prevented me to install it (some lib error was thrown each time).

So my app has two parts, a public and an admin one (which was quite easy to anticipate). Once I managed to create the basic structure of controllers and models for the admin, I added the login functionality, as I don't want everybody to get into my app and modify pictures in there.

I used the Acts As Authenticated plugin, although I couldn't manage to get all the functionality that I expected so I took a look at the code of the Sudoku On Rails v5 tutorial at SobreRailes (sorry, spanish only!) and completed it with the missing functionality (basically, it was the activation stuff which was not generated, don't know why). It was sweet and smooth. If I had had to write it manually I would have spent at least three days.

Then I wanted to have support for uploading images. At all, this is about pictures! So after some researching I found a little plugin called FileColumn which allows to get picture uploads working quite quickly, including resizing and automatic deletion if the associated record gets deleted (no more orphan files in the filesystem because of a poor integration between the models and their associated data! hooray!). But for the resizing stuff and thumbnails generation, I needed to have RMagick installed in my system, which I didn't have. So I found a nice tutorial in the RMagick site, which explained how to compile and install RMagick and GraphicsMagick on mac os x, as well as all the libraries on which they depend, as RMagick is like a bridge between ruby and GraphicsMagick. This step took me a bit of time, specially the step of compiling ghostscript, but as I followed the instructions exactly as specified, I had everything working at the end and I could generate thumbnails and a resized version when uploading images. (Also I was also reading one of the supercool books about the tube that mr.doob gave me for my birthday so time flew fastly! :D )
If I had had to do this in php even using the best helpers that I could, I would have spent at least one week.

Then I prepared a draft version of the public interface. Now I can navigate between pictures and albums, using thumbnails and the big images as well, it shows the descriptions for the album or picture, where available. The code is so simple and beatiful that I could get it tattooed. Or maybe do a t-shirt with it, see an example for getting the data of one album:

def album
@album = Album.find(params[:id])
unless @album.nil?
@pictures = @album.pictures
end
end

No more embedded SQL in the middle of the code, no need for manually loading classes which abstract the DB information for the rest of the php code and try to provide a way of artificial modelization of the data, no need to spend time reinventing the wheel!! :D

As I specified in the models that one album has pictures (has_many : pictures), and that each picture belongs to one album (belongs_to :album), RoR is intelligent enough to deduct things from the database and save me that precious time (as it sees an album_id column in a picture row… it obviously is pointing to an album row!).

Unfortunately by the moment the front end is extremely basic, it's pure html with little css applied to it. In the next iteration I will do tasks such as find out how to redefine part of the behaviour of the FileColumn to make it update the associated images too if I upload another image when editing a picture, beautify the front end, and maybe start with those nice ajax capabilities which are supposedly quite easy to use with RoR. It will also be nice to learn how to do the fantastic unit tests with the support that this framework provides, so all in all this promises to be very exciting.

Conclussion: I absolutely love Ruby on Rails.

Stay tuned for a next release - as I already got a port for installing it on my host, it can be whenever I get a decent working version!.

UPDATE: As some people want to take a look (miguev ;) ) I have uploaded the relevant code of the current version. It just has the important things i.e. the models definition, etc. If you want to have a working application you will need to create it first with the usual rails name_of_your_app, configure the database, etc, and then use this code where appropiate. It won't work by itself :P This is just for showing how things are linked between them: cl1ck version 0.1

20051226 Christmas Holidays, day 2: an abandoned granary

abandoned granary

On the second day I went to do some photographic exploration in the countryside. I found an interesting semi-derelict building. I thought it was completely abandoned but when I came inside I discovered it wasn't like that. Anyway, I continued taking pictures of everything that seemed interesting and then ran away as fast as I could. In any case, it was difficult to be "caught" as it was 25th of december and people stay home with their families instead of going outside to enjoy that glorius sun.

Curiously, I tried to make these pictures long time ago, but when I arrived there I found to my surprise that I didn't had batteries in my camera, so I couldn't take any picture. Luckily this time it was different, and I was able to shoot more than 90 pictures! :-)