DOS batch for labeling .jpgs with date from EXIF

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
blackno666
Posts: 7
Joined: 2010-03-25T07:39:45-07:00
Authentication code: 8675308

DOS batch for labeling .jpgs with date from EXIF

Post by blackno666 »

Hello,

I wrote a dos batch file which extracts the date and time of a jpg file and adds it to it - in the lower right corner. I wrote the following script (if you execute it, for all .jpg files in the directory where the script resides, a copy will be created which contains the date and time - the original image will not be modified).

The batch file does basically nothing for .jpg files which do not have EXIF information for date and time and I did not test it on files with small resolution. I intended the batch file to be used to label pictures taken with a digital camera. I tested the file using Win7 and am not sure if the syntax is valid for older version of Windows.

Here's an example:
Image

Code: Select all

SETLOCAL ENABLEDELAYEDEXPANSION

rem echo off

set ptsize=70

set x=0
set y=0

set /a dx1=%x%+1
set /a dy1=%y%+1

set /a dx2=%x%+2
set /a dy2=%y%+2

for %%f in (*.jpg) do (
rem Read out date of .jpg file (from EXIF info)
identify -format \n%%[EXIF:DateTime] %%f > curdate.txt

rem Read date from curdate.txt
for /f "usebackq eol=; tokens=1,2,3,4,5,6 delims=: " %%i in ("curdate.txt") do (
@set Year=%%i&@set Month=%%j&@set Day=%%k&@set Hours=%%l&@set Minutes=%%m&@set Secs=%%n
)
set Text=!Day!.!Month!.!Year! !Hours!:!Minutes!:!Secs!

convert -size 800x150 xc:transparent -pointsize %ptsize% -gravity southeast ^
        -fill black -draw "text %x%,%y%     '!Text!'" ^
        -fill white -draw "text %dx2%,%dx2% '!Text!'" ^
		-rotate 180 ^
		trans_stamp.png

convert -size 800x150 xc:black -threshold 100%% +matte -pointsize %ptsize% -gravity southeast ^
        -fill white -draw "text %x%,%y%     '!Text!'" ^
		-fill white -draw "text %dx2%,%dy2% '!Text!'" ^
		-fill black -draw "text %dx1%,%dy1% '!Text!'" ^
		-blur 2x2 ^
		-rotate 180^
		mask_mask.bmp

convert %%f -rotate 180 %%f.tmp.jpg
composite trans_stamp.png %%f.tmp.jpg mask_mask.bmp _%%f.rot.FP_Converted.jpg
convert _%%f.rot.FP_Converted.jpg -rotate 180 _%%f.FP_Converted

del %%f.tmp.jpg
del _%%f.rot.FP_Converted.jpg

rem Delete variables
FOR %%a in (Year Month Day Hours Minutes Secs Text) do @SET %%a=

del trans_stamp.png
del mask_mask.bmp
)

rem Delete variables
FOR %%a in (x y dx1 dy1 dx2 dy2) do @SET %%a=

ren _*.FP_Converted *.

:end

ENDLOCAL
The script basically starts (after it has extracted the file date and time) in line 26 with the convert command.
The first convert creates a stamp.
The second convert creates a mask.

I then just want to overlay the images. However, since -gravity does not affect the mask in composite (see http://www.imagemagick.org/Usage/compose/#mask), I thought it to be a good idea to rotate the images, compose them, and rotate them back.
That is what I do with the convert -rotate 180

Finally my problem:
  • - The batch file is quite slow (due to the many calls to convert). I'd like it to be faster.
    - I do not like that I create temporary files. I'd rater combine the last three commands (line 41, 42 and 43) to one command. However, I could not find a solution for this.
    - For Images not taken in portrait mode (i.e. not landscape) the label will be in the upper right corner. I'd like it to be in the lower right corner...
I'd be happy for any suggestions.

blackno666
Last edited by blackno666 on 2011-10-13T23:58:32-07:00, edited 2 times in total.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Optimize dos batch for labeling .jpgs with date from EXI

Post by fmw42 »

I am not following the rotation due to the mask not gravity sensitive. I don't program windows, so know little about that. But several points.

1) you can take your mask and put it into the alpha channel of the appropriate image. Then composite the two images together. Use convert image mask -compose _copy_opacity -composite result.

2) to combine all the steps do not use composite, use convert ... -composite, and if needed parenthesis so that you can have one command line


see
http://www.imagemagick.org/Usage/layers/#convert
http://www.imagemagick.org/Usage/compose/#compose
http://www.imagemagick.org/Usage/compose/#copyopacity
http://www.imagemagick.org/Usage/basics/#parenthesis
blackno666
Posts: 7
Joined: 2010-03-25T07:39:45-07:00
Authentication code: 8675308

Re: Optimize dos batch for labeling .jpgs with date from EXI

Post by blackno666 »

Wow. That was fast. Thanks!

I already tried using convert (instead of composite), but failed. Can't remember the reason. I tried it again with convert and it's been quite easy:

convert ( %%f -rotate 180 ) ( trans_stamp.png -rotate 180 ) ( mask_mask.bmp -rotate 180 ) -composite -rotate 180 _%%f

basically works just fine and faster. The only problem now is that this command uses parenthesis which screws up my for ( ) loop... but that's a Windows problem... I'll post a working script, once this works.
blackno666
Posts: 7
Joined: 2010-03-25T07:39:45-07:00
Authentication code: 8675308

Re: Optimize dos batch for labeling .jpgs with date from EXI

Post by blackno666 »

Well, figured that out. The script now doesn't use temporary files, but still could use improvement (on the IM side).

Code: Select all

rem echo off

set ptsize=70

set x=0
set y=0

set /a dx1=%x%+1
set /a dy1=%y%+1

set /a dx2=%x%+2
set /a dy2=%y%+2

for /f %%f in ('dir /b *.jpg') do if not "%%a" == "" call :DoTheMagix "%%f"
goto :Cleanup

:DoTheMagix
rem Read out date of .jpg file (from EXIF info)
identify -format \n%%[EXIF:DateTime] %1 > curdate.txt

rem Read date from curdate.txt
for /f "usebackq eol=; tokens=1,2,3,4,5,6 delims=: " %%i in ("curdate.txt") do (
@set Year=%%i&@set Month=%%j&@set Day=%%k&@set Hours=%%l&@set Minutes=%%m&@set Secs=%%n
)  
set Text=%Day%.%Month%.%Year% %Hours%:%Minutes%:%Secs%

convert -size 800x150 xc:transparent -pointsize %ptsize% -gravity southeast ^
        -fill black -draw "text %x%,%y%     '%Text%'" ^
        -fill white -draw "text %dx2%,%dx2% '%Text%'" ^
        trans_stamp.png

convert -size 800x150 xc:black -threshold 100%% +matte -pointsize %ptsize% -gravity southeast ^
        -fill white -draw "text %x%,%y%     '%Text%'" ^
        -fill white -draw "text %dx2%,%dy2% '%Text%'" ^
        -fill black -draw "text %dx1%,%dy1% '%Text%'" ^
        -blur 2x2 ^
        mask_mask.bmp

convert ( %1 -rotate 180 ) ( trans_stamp.png -rotate 180 ) ( mask_mask.bmp -rotate 180 ) -composite -rotate 180 _%1


:EndDoTheMagix
goto :EOF

:Cleanup
rem Delete variables
FOR %%a in (Year Month Day Hours Minutes Secs Text) do @SET %%a=

del trans_stamp.png
del mask_mask.bmp

rem Delete variables
FOR %%a in (x y dx1 dy1 dx2 dy2) do @SET %%a=
which basically comes down to three calls to convert.

Now, I tried to combine those to one convert call, basically replacing the filenames (e.g. trans_stamp.png) by the commands for the converts above (i.e. -size 800x150 xc:transparent -po...). Probably this was naive, but since I am not too experienced with IM I thought that this might work. I am sure that the solution lies somewhere hidden in the docs... but if someone could give me another hint, I'd very much appreciate it.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Optimize dos batch for labeling .jpgs with date from EXI

Post by fmw42 »

should be no problem putting them together. this should work, but I am not a windows person so don't know if I have all the windows-specific syntax correct.



convert ( %1 -rotate 180 ) ^
( -size 800x150 xc:transparent -pointsize %ptsize% -gravity southeast ^
-fill black -draw "text %x%,%y% '%Text%'" ^
-fill white -draw "text %dx2%,%dx2% '%Text%'" -rotate 180 ) ^
( -size 800x150 xc:black -threshold 100%% +matte -pointsize %ptsize% -gravity southeast ^
-fill white -draw "text %x%,%y% '%Text%'" ^
-fill white -draw "text %dx2%,%dy2% '%Text%'" ^
-fill black -draw "text %dx1%,%dy1% '%Text%'" ^
-blur 2x2 -rotate 180 ) -composite -rotate 180 _%1


Note -threshold 100% is going to be totally black and so should not be needed if you are using xc:black. Also you could remove the +matte and use -alpha off at the end of the third paren to disable to opaque alpha channel that is created by -draw. Your xc:black with white and black fill is going to be opaque, but -draw will add an opaque alpha channel.

Best way to test is to take this code outside your Bat file and put specific values and images in and then run it in the command line. You can even put -write tmpx.png just before the closing parens to see what each paren is creating.

Sometimes problems are with the Bat file and not IM commands.
blackno666
Posts: 7
Joined: 2010-03-25T07:39:45-07:00
Authentication code: 8675308

Re: Optimize dos batch for labeling .jpgs with date from EXI

Post by blackno666 »

Thanks again for the competent and quick answer and (for me) for the new insights.

I stripped the code down to a command and removed most of the DOS syntax (replace %1 by any .jpg filename, the). The call now is:

Code: Select all

convert ( %1 -rotate 180 -write temp0.png ) ^
( -size 800x150 xc:transparent -pointsize 72 -gravity southeast ^
-fill black -draw "text 0,0 'Text'" ^
-fill white -draw "text 2,2 'Text'" -rotate 180 -write temp1.png ) ^
( -size 800x150 xc:black -alpha off -pointsize 72 -gravity southeast ^
-fill white -draw "text 0,0 'Text'" ^
-fill white -draw "text 2,2 'Text'" ^
-fill black -draw "text 1,1 'Text'" ^
-blur 2x2 -rotate 180 -write temp2.png ) -composite -rotate 180 _%1
When I execute this, I obtain a file which does not have the Text written in it.

However, if I execute the command above, and then invoke

Code: Select all

convert temp0.png temp1.png temp2.png -composite -rotate 180 out.jpg
The output file is as I want it to be. To me, this does not make sense, since the calls should do the identical thing...

Again, if someone has an hint or a solution, I'd be happy (even more than I am already)!
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Optimize dos batch for labeling .jpgs with date from EXI

Post by fmw42 »

try

convert ( %1 -rotate 180 -write temp0.png ) ^
( -size 800x150 xc:transparent -pointsize 72 -gravity southeast ^
-fill black -draw "text 0,0 'Text'" ^
-fill white -draw "text 2,2 'Text'" -rotate 180 -write temp1.png ) ^
( -size 800x150 xc:black -pointsize 72 -gravity southeast ^
-fill white -draw "text 0,0 'Text'" ^
-fill white -draw "text 2,2 'Text'" ^
-fill black -draw "text 1,1 'Text'" ^
-blur 2x2 -rotate 180 -alpha off -write temp2.png ) -compose over -composite -rotate 180 _%1

Thus you are resetting the compose method, because -blur may have changed it.

Alternately you could try adding -respect-parenthesis right after convert, but I am not sure that it actually respects the compose setting.

If that does not work, post a link to your image, so others can test with it.
blackno666
Posts: 7
Joined: 2010-03-25T07:39:45-07:00
Authentication code: 8675308

Re: Optimize dos batch for labeling .jpgs with date from EXI

Post by blackno666 »

-respect-parenthesis alone did the trick!

The final script now only uses one call to convert and looks as follows:

Code: Select all

@echo off

set ptsize=70

set x=0
set y=0

set /a dx1=%x%+1
set /a dy1=%y%+1

set /a dx2=%x%+2
set /a dy2=%y%+2

set cnt=0
set nFiles=0

for /f %%f in ('dir /b *.jpg') do if not "%%a" == "" call :GetNumberOfFiles

for /f %%f in ('dir /b *.jpg') do if not "%%a" == "" call :DoTheMagix "%%f"
goto :Cleanup

:GetNumberOfFiles
set /a nFiles += 1
goto :EOF

:DoTheMagix
set /a cnt += 1
echo Processing file %cnt% of %nFiles% (%1)

rem Read out date of .jpg file (from EXIF info)
identify -format \n%%[EXIF:DateTime] %1 > curdate.txt

rem Read date from curdate.txt
for /f "usebackq eol=; tokens=1,2,3,4,5,6 delims=: " %%i in ("curdate.txt") do (
@set Year=%%i&@set Month=%%j&@set Day=%%k&@set Hours=%%l&@set Minutes=%%m&@set Secs=%%n
)  
set Text=%Day%.%Month%.%Year% %Hours%:%Minutes%:%Secs%

convert -respect-parenthesis ( %1 -rotate 180 ) ^
        ( -size 800x150 xc:transparent -pointsize %ptsize% -gravity southeast ^
          -fill black -draw "text %x%,%y%     '%Text%'" ^
          -fill white -draw "text %dx2%,%dy2% '%Text%'" -rotate 180 ) ^
        ( -size 800x150 xc:black -threshold 100%% +matte -pointsize %ptsize% -gravity southeast ^
          -fill white -draw "text %x%,%y%     '%Text%'" ^
          -fill white -draw "text %dx2%,%dy2% '%Text%'" ^
          -fill black -draw "text %dx1%,%dy1% '%Text%'" ^
          -blur 2x2 -rotate 180 ) ^
          -composite -rotate 180 _%1

goto :EOF

:Cleanup
rem Delete variables
FOR %%a in (x y dx1 dy1 dx2 dy2) do @SET %%a=
FOR %%a in (Year Month Day Hours Minutes Secs Text cnt nFiles) do @SET %%a=

Thanks for the great support.
liucscai
Posts: 1
Joined: 2013-11-01T16:31:22-07:00
Authentication code: 6789

Re: DOS batch for labeling .jpgs with date from EXIF

Post by liucscai »

Thanks for the great job.
Just two little corrections to support file name with spaces (in "for" loop I added "delim=" and in "convert" command I replaced %1 with "%~1"):

Code: Select all

        @echo off

    set ptsize=70

    set x=0
    set y=0

    set /a dx1=%x%+1
    set /a dy1=%y%+1

    set /a dx2=%x%+2
    set /a dy2=%y%+2

    set cnt=0
    set nFiles=0

    for /f %%f in ('dir /b *.jpg') do if not "%%a" == "" call :GetNumberOfFiles

    for /f "delims=" %%f in ('dir /b *.jpg') do if not "%%a" == "" call :DoTheMagix "%%f"
    goto :Cleanup

    :GetNumberOfFiles
    set /a nFiles += 1
    goto :EOF

    :DoTheMagix
    set /a cnt += 1
    echo Processing file %cnt% of %nFiles% (%1)

    rem Read out date of .jpg file (from EXIF info)
    identify -format \n%%[EXIF:DateTime] %1 > curdate.txt

    rem Read date from curdate.txt
    for /f "usebackq eol=; tokens=1,2,3,4,5,6 delims=: " %%i in ("curdate.txt") do (
    @set Year=%%i&@set Month=%%j&@set Day=%%k&@set Hours=%%l&@set Minutes=%%m&@set Secs=%%n
    ) 
    set Text=%Day%.%Month%.%Year% %Hours%:%Minutes%:%Secs%

    convert -respect-parenthesis ( %1 -rotate 180 ) ^
            ( -size 800x150 xc:transparent -pointsize %ptsize% -gravity southeast ^
              -fill black -draw "text %x%,%y%     '%Text%'" ^
              -fill white -draw "text %dx2%,%dy2% '%Text%'" -rotate 180 ) ^
            ( -size 800x150 xc:black -threshold 100%% +matte -pointsize %ptsize% -gravity southeast ^
              -fill white -draw "text %x%,%y%     '%Text%'" ^
              -fill white -draw "text %dx2%,%dy2% '%Text%'" ^
              -fill black -draw "text %dx1%,%dy1% '%Text%'" ^
              -blur 2x2 -rotate 180 ) ^
              -composite -rotate 180 "_%~1"

    goto :EOF

    :Cleanup
    rem Delete variables
    FOR %%a in (x y dx1 dy1 dx2 dy2) do @SET %%a=
    FOR %%a in (Year Month Day Hours Minutes Secs Text cnt nFiles) do @SET %%a=
Post Reply