Friday, June 30, 2006

Me and my Carb

Originally posted to El Cantar de la Lluvia on Monday, August 29, 2005.

Yesterday I decided to take advantage of the nice weather by going on a useful ride. I say useful because for a while now I have intended to address the problems that my bike has with high altitude. The cause is obvious: at higher altitudes, atmospheric pressure is less than it is at sea level, and consequently there is less oxygen available; the fuel mixture becomes rich and the engine loses power.

The problem is that I know very little about motorbikes and mechanics in general, and unfortunately the shop manual for my bike (Honda XR 125 L) is nowhere to be found online (Update 30/6/06: I finally got hold of it and photocopied it!).
What I did manage to find was an old copy of the Honda Common Service Manual, which contains information relevant to any Honda motorbike. Using this manual I was able to figure out a thing or two. It states that one must adjust the pilot screw or air screw in order to admit less fuel or more air to the carb's airflow should one wish to compensate for altitude-related carburation problems.

When I compared the horribly pixelated diagrams with my own carb, I noticed some similarities, but mostly differences. In the first place, the pilot screw would seem to be in the same position as in the diagram (the lowest and leftmost bronze screw in the picture at right). The manual says that it must only be adjusted if it is replaced as a unit (Update 30/6/06: now that I know more about these issues, mostly from my XR250R, I can say that this pilot screw can indeed need adjusting). The bike's design certainly does not encourage its manual adjustment, since it is tiny, and one would require some long and slender special tool to get at it (believe me, it's harder to get at it than it would seem from the pic) or alternatively one would have to perhaps take the exhaust off (Update 30/6/06: Yup, you guessed it: a small washer and nimble fingers allow you to turn it easily. I learnt that from Christian, the mechanic that used to service my XR125L, when he was adjusting my new second-hand XR250R so it would pass the CO gas test).

On the other hand, the air screw (pic on left) seems to be more accessible (Update 30/6/06: This is not the air screw, this is the idle speed adjustment screw. All it does is raise the carb's slider a bit so air will get under it even when the throttle is closed, so the engine can idle. Anyway, I'll leave this section unmodified because it's cute to look back and see how clueless one was :-) and therefore I felt much more confident about adjusting it. Of course, it might not even be the air screw, but my manual calls it the idle speed adjustment screw, and the Common Service Manual calls it the idle speed / air screw. So they're probably the same thing. In any case, I'm talking about the slotted screw that is located on the side of the flat part of the black box that sits on the carb (not the phillips screw, that one is strctural). I marked it, so as not to lose track of its original position. I'll get ahead of myself here and anticipate that the idle speed adjustment screw just helped slightly between 2000 and 2500 metres above sea level, but at higher altitude I was not able to avoid the loss of power at low RPMs, that the engine misfired every now and then and the general unpleasantless of the riding experience. (Update 30/6/06: Well, there you go. Subjective measurements are precisely that, and I now know that whatever improvements I detected upon varying the idle speed screw, they were certainly imaginary). At 3100 metres above sea level I had given the idle speed screw three half-turns and the idle speed was ridiculously high, but the altitude-related problems persisted.

I had to choose a place that would allow me to gain altitude quickly. The right destination was obvious: Farellones, and then Valle Nevado. At 2000 and a bit, and 3100 metres above sea level respectively, I'd get good altitude and good views. Here are Valle Nevado and Farellones on Google Maps (the sat pics were obviously taken in summer).

I equipped myself with an altimeter watch, a block of Post-Its, a pen, and all the other paraphernalia that I stuff in my bag when I tour on the bike, and set off.

This is the first stop before the curves start. I started doubting the wisdom of riding in shorts.

The stratification of the receding snow could be seen clearly, it looked like sedimentary rock.

This took me by surprise. It seems that tall, day-glow orange posts are used to mark the way in case of a heavy snowfall. What I certainly did not expect was to see one of these posts trapped in the snow. And it wasn't the last one, either.

Icicles! Some were enormous. Every now and then you could here noises from rocks that were being dislodged by the thawing ground, and falling onto the road. It dodn't seem like the safest place to be, but oh well...

I didn't have much sunlight left, but I didn't mind. The light was very special: vertical things were orange, horizontal things were bluish, due to the blue, blue sky. The shadows and snow were perfect canvases for capturing everything.

This pic was taken from Valle Nevado, from the ski center itself. It was time to go back... my hands were numb, as a matter of fact they hurt, despite two pairs of gloves, and my legs were in an even worse state, as I had only taken thin cotton trousers as an alternative to my shorts. The watch said it was 6°C.

The ride home was easier engine-wise, but the cold made my hands hurt intensely. Once I'd passed Farellones again the ambient temperature went up a little bit, my nose stopped running, and my fingers stopped hurting.

But I'd do it again ;-)

Update 30/6/06: And I did. Strangely enough, that was one of the first places I took my new XR250R. There's something special about climbing up to the top of the world on a new bike.

Labels: ,

Friday, June 16, 2006

How to Watermark your Photos

I have been posting increasingly-large images on my other blog, El Cantar de la Lluvia, and wanted to embed at least some ownership info in the pics. I don't mean something fancy like Digimarc, or even a full-image ghosted overlay. I just wanted an easy and automated way to add a string to the image along the right hand side, rotated, and hopefully using a tool smart enough to detect if the image was horizontal or vertical.

I tried quite a few OS X apps, some of them free, some demos, and hated them all. I decided to write my own tool to do it. After all, I do have an excellent and easy to use image handling library at my disposal (which, I might add --most un-humbly--, I wrote myself) called PNGwriter.

I wrote PNGwriter to let amateur programmers just get at the darned pixels, and to spare them the trouble of using some horrible format like PPM if they didn't have the time or knowledge to learn the interface to an image library.

My objectives for this project were as follows.

  • Write something that takes the 630-pixel-wide images I export from iPhoto, once I've selected them for my blog, and adds a text string to them.
  • The text string should be easily modifiable, so no hard coding it into the program.
  • The program should accept UTF-8 strings (my love for the UTF-8 text encoding knows no bounds).
  • The text string should ideally be placed along the right edge of the image, rotated so as to be as unobtrusive as possible.
  • The text string should be automatically written in black on light backgrounds, and in white on dark backgrounds.
  • The text string should possibly be blended with the background, with a transparency effect.
  • The program should be able to be called easily from a shell script, so as to automate the processing of many images.
  • The program should add the string -wmk to the basename of the watermarked image, while the original should be kept intact.
  • I should stop adding items to this list now.

After a few hours of writing code that consisted mostly of mistakes and blunders (I haven't programmed for about a year) I managed to produce something that satisfied the above requirements. Along the way I realised that having the program accept the "copyright string", or the string to be added to the image, was not such a hot idea, as getting the shell script to play nice with spaces and unicode characters was beyond my almost inexistent shell script knowledge. In the end I opted for the simplest alternative.

The program, which I decided to call watermarker, takes three arguments, no more, no less. An example use of the program is as follows:
./watermarker watermarker.config copyrightstring-utf8.txt IMG00001.png

The names are arbitrary and the relevant files can be in any location. The only requirement is that the input image be a 24 byte-per-pixel (this is important), that the file copyrightstring-utf8.txt contain the relevant UTF-8 formatted text string to use (or just plain text, because plain characters in UTF-8 are just plain characters); and that watermarker.config contain the following information:

  • X-offset, in pixels, of the bottom right corner of the (vertical) text to be plotted, as measured from the right side of the image.
  • Y-offset, in pixels, of the bottom right corner of the text to be plotted, as measured from the bottom of the image.
  • Font size
  • Blending coefficient, a decimal value from 0.0 to 1.0 (1.0 is fully opaque)
  • A valid TrueType font file

For example, the config file might look like this:

Here is the program's code. Feel free to use it however you like, after all, it's just a few lines of code, but I'd appreciate credit ("Based on..." or "Inspired by...") if you derive something from it. In any case, if you find it useful, please leave a comment and tell me about it!
#include <pngwriter.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <fstream>
using namespace std;

int main(int argc, char * argv[])
if(argc != 4)
std::cout << "Wrong number of args (" << argc - 1 << "). Usage: ";
std::cout << "watermarker <config file> <UTF8 string file>";
std::cout << "<png image file>\n";
std::cout << "UTF8 string file should be a plain text or";
std::cout << "UTF8-formatted text file containing the copyright string";
std::cout << "to be used. \nConfig file should be a text file containing,";
std::cout << "on separate lines, \n";
std::cout << " pos_x;\n pos_y\n font_size\n blend coef.\n fontfile\n";

// Get copyright string into a binary array.
char * copyrightfile = argv[2];
char * copyright;
long size;
ifstream file (copyrightfile, ios::in|ios::binary|ios::ate);
if (! file.is_open())
std::cout << "Error opening UTF8 text file."; exit (1);
size = file.tellg();
file.seekg (0, ios::beg);
copyright = new char [size]; (copyright, size);

// Load configuration parameters.
std::ifstream params (argv[1]);
if (! params.is_open())
std::cout << "Error opening config file"; exit (1);

string filename(argv[3]);
string fontfile;
int pos_x;
int pos_y ;
int font_size;
double angle;
double blend;

params >> pos_x;
params >> pos_y;
params >> font_size;
params >> blend;
params >> fontfile;

// Create PNGwriter instance
pngwriter image(1,1,0,"caca.png");

char * fn;
fn = new char[1024];
strcpy( fn, fontfile.c_str());

double average;
int minx, miny, maxx, maxy;
average= 0.0;
angle = 1.57079633;

minx = image.getwidth() - pos_x;
maxx = image.getwidth();
maxy = pos_y + image.get_text_width_utf8(fn, font_size, copyright);
miny = pos_y;

// Calculate the average intensity of the image
// behind the text.
for(int xx=minx; xx <=maxx; xx++)
for(int yy=miny; yy <=maxy; yy++)
average = average + image.dread(xx,yy);
average = average/( (double)( (maxx-minx)*(maxy-miny) ));
std::cout << "Average image intensity in text area: " << average;
std::cout << ", Number of pixels taken: " << (maxx-minx)*(maxy-miny);
std::cout << ", text will be";

// Plot the text on the image.
std::cout << " black." <<std::endl;
image.plot_text_utf8_blend( fn, font_size,
image.getwidth() - pos_x, pos_y, angle,
0.0, 0.0, 0.0);
std::cout << " white." <<std::endl;
image.plot_text_utf8_blend( fn, font_size,
image.getwidth() - pos_x, pos_y, angle,
1.0, 1.0, 1.0);

// Rename the file, add -wmk to the basename.
char * newfilename;
char fl[1024];
strcpy(fl, filename.c_str());
newfilename = strtok (fl, "." );
strcat(newfilename, "-wmk.png" );

image.settext(filename.c_str(), "Paul Blackburn",
"Copyright 2006 Paul Blackburn, paulwb AT, All Rights Reserved.",
"Watermaked with PNGwriter plus a small program called watermarker.");

// Write the PNG image to disk.

delete [] fn;

return 0;

Finally, if you'd like to have the program work on all images in a directory, you might find this shellscript useful:

for caca in *.jpg
base=`basename $caca .jpg`
convert -depth 24 $caca $png
./watermarker watermarker.config copyrightstring.txt $png
convert -depth 8 ${base}-wmk.png ${base}-wmk.jpg
echo "Finished " ${base}-wmk.jpg

Here are two images that I've watermarked rather unobtrusively. The program selected white text for the first, and black text for the second.

I went through many iterations of this program before I found one that I liked. I wrote versions that detected portrait or landscape pictures and placed the text accordingly, versions that allowed an arbitrary angle to be set and so on. I think the current setup is the best balance between versatility and simplicity. If you find this does not suit your needs, go ahead and modify the code.

And that's how to watermark your photos, when none of the watermarking apps are good enough for you!

Tuesday, June 13, 2006

And he translated it into English, and he saw that it was good.


First post on this blog. I have decided to take my spanish-only blog, El Cantar de la Lluvia, and start translating most of the articles on there. There's a lot to see, and I feel a lot of people are missing out on the story behind some of those great pics.

Also, I often come across small snippets of information that I think might be useful to some Google Searcher in the mysterious future, so I have decided to add those posts to my list of suitable topics.