How do I determine good pointsize for my watermark?

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
Post Reply
r.osmanov
Posts: 7
Joined: 2011-08-24T05:55:12-07:00
Authentication code: 8675308

How do I determine good pointsize for my watermark?

Post by r.osmanov »

Hi!

How do I obtain good pointsize for text depending on the image I print the text on?

Particularly, I'm making script to stamp watermakrs automatically on a wide variety of images(canvas sizes). I've made bash script for testing:

Code: Select all

#!/bin/bash - 

#{{{ Parse CLI args
function my_split_str()
{
    if (( $# <=1 )) 
    then 
        IFS='=' 
    else 
        IFS=$2 
    fi 
    set -- $1
    echo $*
}

# For string like part1=part2
# returns part2 
function my_arg_val()
{
    echo $1 | sed 's/[-a-zA-Z0-9]*=//'
}

# Get args
for i in $*
do
    case $i in
		$1*)
            SRC_IMG=`my_arg_val $i` 
			;;
        --src-img=*)
            SRC_IMG=`my_arg_val $i` 
            ;;
        --wm-width=*)
            WM_WIDTH=`my_arg_val $i`
            ;;
        --wm-height=*)
            WM_HEIGHT=`my_arg_val $i`
            ;;
        --font-size=*)
            WM_FONT_SIZE=`my_arg_val $i`
            ;;
        *)
            # unknown option
            #a=(`my_split_str $i '='`)
            #echo "Unknown option ${a[0]}"
            #exit 2
            ;;
    esac
done
#}}}

OUT_IMG="out.jpg"

if [ -z $SRC_IMG ]; then
	echo "--src-img";
	exit 2
fi

#{{{ Arg defaults 
: ${WM_TEXT:="megagroup.ru"}
: ${WM_WIDTH:=300}
: ${WM_HEIGHT:=50}
: ${WM_FONT:="DejaVu-Sans-Condensed"}
: ${WM_FONT_SIZE:=20}

: ${WM_FG_IMAGE:="stamp_fg.png"}
: ${WM_BG_IMAGE="stamp_bg.png"}
: ${WM_STAMP_MASK="stamp_mask.png"}
: ${WM_STAMP="stamp.png"}
: ${WM_GRAVITY:="south-west"}
#}}}

convert -size $WM_WIDTH"x"$WM_HEIGHT xc:transparent \
	-font "$WM_FONT" -pointsize $WM_FONT_SIZE\
	-draw "gravity center  \
	fill grey15 text 0,0 '$WM_TEXT' \
	fill white text 8,8 '$WM_TEXT' " \
	 "$WM_STAMP"	

mogrify -trim +repage "$WM_STAMP"

composite -dissolve 25% \
	-gravity "$WM_GRAVITY" \
	"$WM_STAMP"  "$SRC_IMG" "$OUT_IMG"
The script prints watermark text on transparent canvas with width = width of
original image and height picked roughly, ex. 300px. Then it trims and repages
watermark image and applies it on original image via "dissolve".

Thus, the watermark first canvas size doesn't matter. The only thing I have to
adjust here is pointsize. So how do I determine what pointsize to pick for
arbitrary original image size?

With the following command pointsize of 220 results font height of about 134px:

Code: Select all

./test.sh --src-img=a.jpg --wm-width=2200 --wm-height=300 --font-size=220

Regards.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How do I determine good pointsize for my watermark?

Post by fmw42 »

You could specify the size you want and use label: to set the point size automatically rather than using -draw.

see
http://www.imagemagick.org/Usage/text/
http://www.imagemagick.org/Usage/text/#label
r.osmanov
Posts: 7
Joined: 2011-08-24T05:55:12-07:00
Authentication code: 8675308

Re: How do I determine good pointsize for my watermark?

Post by r.osmanov »

fmw42 wrote:You could specify the size you want and use label: to set the point size automatically rather than using -draw.

see
http://www.imagemagick.org/Usage/text/
http://www.imagemagick.org/Usage/text/#label
Thank you very much! This partially solved my problem.

However, I cannot find how to specify offset for a label. I used the following sequence of commands before:

Code: Select all

convert -size $WM_WIDTH"x"$WM_HEIGHT xc:transparent \
    -font "$WM_FONT" -pointsize $WM_FONT_SIZE\
    -draw "gravity center  \
    fill grey15 text 0,0 '$WM_TEXT' \
    fill white text 8,8 '$WM_TEXT' " \
     "$WM_STAMP"
mogrify -trim +repage "$WM_STAMP"

composite -dissolve 25% \
    -gravity "$WM_GRAVITY" \
    "$WM_STAMP"  "$SRC_IMG" "$OUT_IMG"
But it caused the problem with poinsize. Then I tried the following:

Code: Select all

convert -background transparent \ 
    -fill grey15 -font "$WM_FONT" \
    -size $WM_WIDTH"x"  label:"$WM_TEXT" \
    "$WM_STAMP"

composite -dissolve 25% \
    -gravity "$WM_GRAVITY" \
    "$WM_STAMP"  "$SRC_IMG" "$OUT_IMG"
Works perfectly, but with single label. I need white label upon grey one with the same text(or vice versa).
r.osmanov
Posts: 7
Joined: 2011-08-24T05:55:12-07:00
Authentication code: 8675308

Re: How do I determine good pointsize for my watermark?

Post by r.osmanov »

I've managed to produce diagonal text with it's copy of +10+10 offset and white color:

Code: Select all

# ...
convert -background transparent \
    \( -fill grey75 -font "$WM_FONT" \
    -size $WM_SIZE label:"$WM_TEXT" \) \
    \( +clone -fill white -size $WM_SIZE label:"$WM_TEXT" -roll -10-10 \) \
    -reverse -composite \
    -rotate "$WM_ANGLE" \
    "$WM_STAMP"

composite -dissolve 35% \
    -gravity "$WM_GRAVITY" \
    "$WM_STAMP" "$SRC_IMG" \
    "$OUT_IMG"
By means of the following PHP script I obtain angle and watermark image width:

Code: Select all

<?php
$in_filename = $argv[1];

$size = getimagesize($in_filename);
$width = $size[0];
$height = $size[1];

// Get watermark text angle in degrees
$wm_angle = floor(rad2deg(atan($width / $height))) - 90;

// Set watermark width to the length of hypothenouse
$wm_width = floor(sqrt(pow($width, 2) + pow($height, 2))) - 80;

$cmd = "./wm.sh --src-img=".escapeshellarg($in_filename) ." --wm-width=$wm_width --wm-angle=$wm_angle";
echo $cmd, "\n";
exec($cmd);
?>

Code: Select all

$ php wm.php b.jpg
./wm.sh --src-img='b.jpg' --wm-width=4920 --wm-angle=-37
http://i53.tinypic.com/25qvqz7.jpg
Image

The command is slow on images about 3000x4000px, but works fine with about 1024x2058px thumbnails.
I suspect my command is not optimal... And if there is a space, it prints first word only.

Share your opinions please.
r.osmanov
Posts: 7
Joined: 2011-08-24T05:55:12-07:00
Authentication code: 8675308

Re: Terrible CPU consumption :(

Post by r.osmanov »

The following commands consume CPU too much :( Please, help me optimize it.

Code: Select all

# Prepare watermark PNG
convert -background transparent \
\( -fill grey75 -font DejaVu-Sans-Condensed \ 
-size $wm_width label:$wm_text \
-gravity center \) \
\( +clone -fill white -size $wm_width \
label:$wm_text \
-roll -3-3 -gravity center \) \
-reverse -composite -gravity center -rotate $wm_angle \
 $wm_filename

# Apply watermark in-place
composite -dissolve $wm_opacity% -gravity center \
$wm_filename $src_filename $src_filename
Regards.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How do I determine good pointsize for my watermark?

Post by fmw42 »

On large images, you may be reaching memory limitations and thrashing to disk.

It might be helpful to have an example image to work with and your output result and exact command line, i.e. numbers rather than variables.

I have tried your command to make a watermark on a 500x500 image and it has a very large separation between the two sets of text. It does not look like your example above. So I want to be sure I understand your watermark process before I can help improve its performance.

If I use just 500, it looks more like shading of the text.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How do I determine good pointsize for my watermark?

Post by fmw42 »

OK. Try this. The src_filename is 500x500 in size. You can change the picture and the parameters. The idea here is to first use label: to get the dimension of the text image and the pointsize. Then use -annotate to do the drawing without having to clone the image. Also to do the dissolve in the same step by using convert rather than composite. Note I used font Verdana as I did not have your font. You can change that back.


wm_width=500
wm_text="testing"
wm_angle=45
wm_opacity=50
src_filename="freckles2.jpg"
outfile="freckles2_text.jpg"
data=`convert -gravity center -fill grey75 -font verdana \
-size $wm_width label:$wm_text \
-format "%wx%hx%[label:pointsize]" info:`
wd=`echo "$data" | cut -dx -f 1`
ht=`echo "$data" | cut -dx -f 2`
dim="${wd}x${ht}"
point=`echo "$data" | cut -dx -f 3`
convert $src_filename \( -size $dim xc:none \
-gravity center -fill white -font verdana -pointsize $point -annotate +3+3 $wm_text \
-gravity center -fill grey75 -font verdana -pointsize $point -annotate +0+0 $wm_text \
-background none -rotate $wm_angle \) \
-compose dissolve -define compose:args=$wm_opacity,100 -composite $outfile

see the last section of http://www.imagemagick.org/script/compose.php about convert ... -compose dissolve -define ... -composite.

Also see the section on -compose dissolve at http://www.imagemagick.org/Usage/compose/#dissolve
and
see -annotate at http://www.imagemagick.org/Usage/text/#annotate. It has built in offset, but you have to specify the exact size of the image and pointsize.

Also see viewtopic.php?t=16449 regarding the [label:pointsize]
r.osmanov
Posts: 7
Joined: 2011-08-24T05:55:12-07:00
Authentication code: 8675308

Re: How do I determine good pointsize for my watermark?

Post by r.osmanov »

fmw42 wrote:...

wm_width=500
wm_text="testing"
wm_angle=45
wm_opacity=50
src_filename="freckles2.jpg"
outfile="freckles2_text.jpg"
data=`convert -gravity center -fill grey75 -font verdana \
-size $wm_width label:$wm_text \
-format "%wx%hx%[label:pointsize]" info:`
wd=`echo "$data" | cut -dx -f 1`
ht=`echo "$data" | cut -dx -f 2`
dim="${wd}x${ht}"
point=`echo "$data" | cut -dx -f 3`
convert $src_filename \( -size $dim xc:none \
-gravity center -fill white -font verdana -pointsize $point -annotate +3+3 $wm_text \
-gravity center -fill grey75 -font verdana -pointsize $point -annotate +0+0 $wm_text \
-background none -rotate $wm_angle \) \
-compose dissolve -define compose:args=$wm_opacity,100 -composite $outfile

...
Thank very much, fmw42! Worked perfectly on local machine:

Code: Select all

 $ cat ./test.sh
#!/bin/bash - 

# Input image: d.jpg, 960x1280px

# Since watermark goes diagonally, it's width *should* be: 
# wm_width = sqrt(pow(width, 2) + pow(height, 2)) - some_delta;
# But, I noticed, it consumes CPU a lot when text goes out of the image area. Thus, I use average dimension: 
# wm_width = (width + height) / 2 = (1280 + 960) / 2 = 1120

# Rotation angle should also depend on image width and height:
# wm_angle = arctg(width / height) - pi / 2 =  arctg(960 / 1280) - pi / 2
# 0.64350110879328 - 1.570796327 = −0.927295218 radians ~= -53 degrees

wm_width=1120
wm_angle=-53
wm_opacity=35
wm_text="testing"
src_filename="d.jpg"
outfile="out.jpg"
wm_font="DejaVu-Sans-Condensed"

# current timestamp
date "+%s"

data=`convert -gravity center -fill grey75 -font $wm_font \
    -size $wm_width label:$wm_text \
    -format "%wx%hx%[label:pointsize]" info:`
echo "data:$data"
wd=`echo "$data" | cut -dx -f 1`
ht=`echo "$data" | cut -dx -f 2`
dim="${wd}x${ht}"
point=`echo "$data" | cut -dx -f 3`

convert $src_filename \( -size $dim xc:none \
    -gravity center -fill white -font $wm_font -pointsize $point -annotate +3+3 "$wm_text" \
    -gravity center -fill grey75 -font $wm_font -pointsize $point -annotate +0+0 "$wm_text" \
    -background none -rotate $wm_angle \) \
    -compose dissolve -define compose:args=$wm_opacity,100 -composite $outfile
# current timestamp
date "+%s"

$ ./test.sh
1314535756
data:1120x424x362
1314535757
Image
Just a second vs. 4..8 seconds with my commands before!

Thanks again. Going to commit changes to my server...
Post Reply