Random files and Image Stitching in DOS

I’m a bit of a fan of command prompts and batch files. Geeky yes, and most other geeks would probably hate the fact that I rather like doing things in Windows command line interpreter (cmd.exe). “Hey, I could find a random file in a single-line perl script, and it would stitch two images together, and it would make you a double espresso” you may say. “Well maybe, but you’re still an ass” I would retort. Despite what some people think Microsofts command processor is actually rather powerful, as demonstrated by the oft interesting Command Line Kung Fu.
Anyway, I decided I wanted to have a photo of my son on my Windows desktop at work – partly to remind me why I spend my days there (so we can afford to keep the little tyke and don’t have to send him back for a refund), and to give me encouragement to go home at a reasonable time of an evening. However, my work system is a dual-display machine, so my desktop resolution is 3200×1200, with a big black gap in the middle (the monitor case). So my idea was to stitch together two images selected randomly from a directory-full, set that as the background, and refresh the display. How to do it? Batchfiles, of course!

The problem can be split into three:

  1. Select a random file
  2. Stitch together two images
  3. Set the wallpaper and refresh the display

Taking each of those in turn, here are my solutions.

1. Select a random file
I created a batchfile called “random_file.bat”, which looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@echo off
setlocal enabledelayedexpansion
 
:main
if not .%1==. (
	set filemask=%1
) else (
	set filemask=*
)
 
:: we have a mask, now work out how many items there are
if not exist %filemask% goto error_nofile
 
set count=0
for /F "usebackq" %%a in (`dir /a-d /b %filemask%`) do (
	set /a count=!count!+1
)
 
:: %count% is the number of files - if it's more than 0 we can pick a file
if !count!==0 goto error_nofile
 
set /a count=%random% %% !count!
set filename=
:: now do the dir again, and pick out the specific file we want
for /F "usebackq tokens=*" %%a in (`dir /a-d /b %filemask%`) do (
	if !count!==0 set filename=%%a
	set /a count=!count! - 1
)
 
if ".%filename%"=="." goto error_nofile
echo %filename%
exit /b 0
 
:error_nofile
exit /b 1

Most of that is fairly straightforward, and if it’s not… don’t worry your pretty little head about it. The clever bits are from line 15, where we use a for loop create a count of all non-directory files matching the passed-in filemask. This could be done without a for loop (instead passing the output of the “dir” command into “find /c”, and using a method seen below to get the count into a variable), but for the moment the loop works well.
Line 22 uses the built-in random number generator to get a number from 0..32767, and gets a modulo with the file count. This provides a way to count through the file list until we reach our randomly-selected file. Again, this could be done much easier, but it works.
Finally, we output the randomly-selected filename.

2. Stitch together two images
I created a batchfile called “stitch_image.bat”, which looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@echo off
setlocal enabledelayedexpansion
 
:: we want to pick two random files
:: we do this by calling the random_file.bat, and storing the output
 
:: the two files should be different, so the first gets set, then the second in a loop
set file1=
for /F "tokens=* usebackq" %%a in (`random_file.bat *.jpg`) do (
	if not "%%a"=="" set file1="%%a"
)
if .%file1%==. goto error_nofile
 
:loop1
set file2=
for /F "tokens=* usebackq" %%a in (`random_file.bat *.jpg`) do (
	if not "%%a"=="" set file2="%%a"
)
if .%file2%==. goto error_nofile
if .%file2%==.%file1% goto loop1
 
echo file1 = %file1%
echo file2 = %file2%
 
:: use nconvert to stitch the images together
nconvert -canvas 3200 1200 top-left -wmpos 1600 0 -wmfile %file1% -out bmp -o _out.bmp %file2%
 
::Placeholder
 
exit /b 0
 
:error_nofile
echo Error - no files found
exit /b 1

Here I’ve used a for loop to store the output of a call to my random_file.bat. The resulting random filename is stored in the file1 variable.
I then perform the same function again in a loop, storing the filename to file2, until file1 != file2. This ensures that the two files are different.
Then I use nconvert to stitch the images together. nconvert is a handy command-line tool for image manipulation, and comes with the also-wonderful XnView. Annoyingly it doesn’t provide a way to stick one image onto the side of another… or does it?
Taking the command apart step-by-step, we have the following:

  • -canvas 3200 1200 top-left : this takes the passed-in image (%file2%) and puts it into the top-left of a canvas twice its size (my images are all 1600×1200 resolution)
  • -out bmp -o _out.bmp : this specifies that I want an bitmap output, called _out.bmp
  • -wmpos 1600 0 -wmfile %file1% : this is the clever part. The main image (%file2%) is sitting at the top-left of the double-sized canvas, so this command places the other image (%file1%) on the right as a watermark

The result of all of this is that the two randomly-selected images end up next to each other in the file “_out.bmp”. Now we just need to get that image onto the desktop…

3. Set the wallpaper and refresh the display
Here we need a little Windows-magic. At the end of “stitch_image.bat”, where we see the text “::Placeholder”, we add the following code:

1
2
3
4
5
6
7
8
9
:: get the full filename of the image we just created
set fullname="%CD%\_out.bmp"
 
:: set the name in the registry
reg add "HKCU\Control Panel\Desktop" /v Wallpaper /t reg_sz /f /d %fullname%
:: set the image to be tiled
reg add "HKCU\Control Panel\Desktop" /v TileWallpaper /t reg_sz /f /d 1
:: finally, refresh the desktop so we get the new image
rundll32 user32.dll,UpdatePerUserSystemParameters

First up we work out the full path of the image file we’ve just created, then we run a couple of registry commands to add that image as the selected wallpaper, and specify that it’s tiled.
Then we issue a rundll32 command that causes the desktop to refresh (otherwise we won’t see the change until we log out and log back in again).

And that’s it. Seems to work, albeit a little slowly… perhaps I’ll look into speeding it up at some point, but if it works, why bother.

3 Comments

  1. Posted August 21, 2009 at 10:30 am | Permalink

    There’s an oddity with the codebox in Firefox it seems – I can’t spot anything obvious, even using Firebug, but the line numbers occupy slightly less height than the lines, meaning that they don’t… line up. Weird. It works fine in both Chrome and IE6 (not tried newer IE).
    So if you’re using Firefox, until I can find a solution you’ll just have to count the lines by hand.

  2. Posted October 29, 2009 at 3:38 am | Permalink

    I am impressed the way you have provided this method.Your mind is doing excellent work in cmd.

  3. QGazQ
    Posted February 10, 2012 at 11:47 am | Permalink

    While compiling I was looking at my bookmarks and went here and was looking through.
    The oddity about the codebox seems to be related to the “bold” of the keywords. If you go turn the font-weight: bold off then they line up again.
    I’m not sure what this means in terms of fixing it but hey just thought I’m mention it.

Post a Comment

Your email is never shared. Required fields are marked *

*
*