Update (2010-06-14)
Thanks to Glenn Turnbull I’ve fixed a bug where the last contact sheet would not be created when the number of photos is evenly divisible by the contact sheet size.
Additionally, this script and others will now be kept updated at http://github.com/jmhobbs/helper-scripts
Wow, long time no post. Darcy and I got a digital camera about a week ago, a Nikon D90. We haven’t really had a chance to put it through it’s paces, but we’ve taken a few pictures around the house to play with it.
At about 3mb each (JPEG’s) the images are really slow to preview in Konqueror. I decided it would be better to be able to download all the photos from the card, then run a script to make my thumbnails. That way I wouldn’t have to wait around while I was viewing photos, instead I could just wait once at the beginning of the process.
My resulting script may have some holes, but it works well for me on Sidux. It takes all of the images in the current directory and makes 600×600 base thumbnails into a directory called “thumb” then uses those to make 12 image contact sheets into a directory called “contact”.
| real | user | sys | |
|---|---|---|---|
| resize | 0m43.478s | 0m40.625s | 0m2.525s |
| scale | 0m25.449s | 0m22.975s | 0m2.236s |
| sample | 0m18.362s | 0m15.983s | 0m2.211s |
| Script times for 16 JPEG images at 3Mb each To 600×600 thumbnails and 200×200 contact sheet frames. |
|||
Your results will vary, but I ran it with three different scaling types (resize, scale, sample). I’m fine with the output from the fastest one (sample) but you can do as you please. I didn’t add command line options because I wanted to have consistent sizes and qualities every time I use it.
Side By Side Resize Method Comparison Click For Fullsize |
Sample Contact Sheet Click For Full Size |
It keeps you updated so you know it hasn’t stalled, here is a sample run.
jmhobbs@asuka:~/Desktop/D90/dcim/example$ digiCamProc.sh Processing 16 Images Creating Thumbnails 100% Creating Contact Sheets 1 of 2 2 of 2 jmhobbs@asuka:~/Desktop/D90/dcim/example$
And here it is. Feel free to comment your changes!
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #!/bin/bash # Digital camera thumbnail/contact sheet tool. # http://www.velvetcache.org/2009/03/30/imagemagick-thumbnails-and-contact-sheets # http://github.com/jmhobbs/helper-scripts # # Copyright (c) 2009-2010 John Hobbs # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # CHANGELOG # 2010-06-14 - Fixed contact sheet problem, thanks to Glenn Turnbull. (John Hobbs) # 2009-03-30 - Created script. (John Hobbs) ### SETTINGS ### # Scaling Methods: # resize (Best/Slow) # scale (Middle/Middle) # sample (Worst/Fast) METHOD="sample" # Thumbnail Size THUMBSIZE="600x600" # Thumbnail Directory THUMBDIR="thumb" # Thumbnail Quality THUMBQUALITY="80" # Contact Item Size CONTACTSIZE="200x200" # Contact Sheet Max Width CONTACTWIDTH="3" # Contact Sheet Max Height CONTACTHEIGHT="4" # Horizontal Spacing CONTACTSPACINGH="3" # Vertical Spacing CONTACTSPACINGV="3" # Contact Sheet Directory CONTACTDIR="contact" # Contact Sheet Quality CONTACTQUALITY="100" ################ CONTACTCOUNT=$(($CONTACTWIDTH * $CONTACTHEIGHT)) PIX=$(ls -l *.jpg | wc -l) echo "Processing $PIX Images" echo echo "Creating Thumbnails" mkdir -p $THUMBDIR CTR=0 echo -n "0%" for i in *.jpg; do echo -ne "\r" echo -n "$((100 * $CTR / $PIX))%" convert -strip -quality ${THUMBQUALITY} -${METHOD} ${THUMBSIZE} "$i" "${THUMBDIR}/${i}" CTR=$(($CTR + 1)) done echo -ne "\r" echo "100%" echo echo "Creating Contact Sheets" mkdir -p $CONTACTDIR CTR=0 PAGES=$(($PIX / $CONTACTCOUNT)) if [ $(($PIX % $CONTACTCOUNT)) -ne 0 ]; then PAGES=$(($PAGES + 1)) fi PAGE=1 LIST="" for i in ${THUMBDIR}/*.jpg; do if [ $(($CTR % $CONTACTCOUNT)) -eq 0 ] && [ $CTR -ne 0 ]; then echo "$PAGE of $PAGES" montage -label %f -quality $CONTACTQUALITY -frame 5 -tile ${CONTACTWIDTH}x${CONTACTHEIGHT} -geometry ${CONTACTSIZE}+${CONTACTSPACINGH}+${CONTACTSPACINGV} $LIST jpg:- > ${CONTACTDIR}/${PAGE}.jpg LIST="" PAGE=$(($PAGE + 1)) fi LIST="$LIST $i" CTR=$(($CTR + 1)) done if [ "" != "$LIST" ]; then echo "$PAGE of $PAGES" montage -label %f -quality $CONTACTQUALITY -frame 5 -tile ${CONTACTWIDTH}x${CONTACTHEIGHT} -geometry ${CONTACTSIZE}+${CONTACTSPACINGH}+${CONTACTSPACINGV} $LIST jpg:- > ${CONTACTDIR}/${PAGE}.jpg fi |
I’ve been playing with OpenCV recently and was having troubles with creating effects. That’s all over now thanks to the OpenCV mailing list, you can see the fix in the comments of this post. Since I can now manipulate colors and pixels with ease I’ve been writing a few more neat effects. And yes, I know that this is not what OpenCV is for, but it’s a really handy library and I like it.
I decided to pull them out of my test program and make them into a little library. Having never made a library before I[m sure I did some things wrong, but it works and that’s what I care about right now.
I doubt my manipulation method is the fastest, but I’ve tried to be economical and share resources between effects. I also included a test program so you can try out the filters. These are all written by me so far, with inspiration but no code from other sources. I’m hoping to port over some of effectv’s super cool filters as time goes on. You can click through on any of the pictures below for a 640×480 version.
Baseline
This is my normal image I get from my cheap labtec webcam.
And yes, I am that good looking.

Green
Probably the simplest effect, it just involves turning off the blue and red channels.

Mirror
Copies and flips the left side onto the right.


Monochrome
Simple like green, just sums the three channels into one value.

Corners
This one swaps the top left and bottom right corners. I’m going to swap top right and bottom left later.

Memory
This is one of the harder ones. It stores three frames in memory and then combines them with the current frame for a ghosting type of effect.


Oompa Loompa
For lack of a better name.

Pixelize
This is the only one with parameters, and one of my favorites.
The parameter is the “pixel size”.
From top down it is set at 2, 6 and 10.
![]()
![]()
![]()
You can grab the 0.01 source here (doesn’t have the invert filter) or browse the svn repo at http://svn.velvetcache.org/libcvfx/ for more current stuff.
Posted March 10th, 2008 - PermalinkI’ve been playing more with OpenCV and I think I’m missing something. I can’t do any manipulations on the image data without really screwing it up. The only thing that doesn’t seem to wash out the data out is moving pixels around without changing them. Not sure what I’m missing. Here’s the few different manipulations and what they look like when they wash out.
Monochrome
1 2 3 4 5 6 7 8 9 10 11 | for(int i = 0; i < frame->height; i++) { int offset = i*frame->width*3; for(int j = 0; j < frame->width; j++) { uchar temp = frame->imageData[offset+(j*3)]*0.114 + frame->imageData[offset+(j*3)+1]*0.587 + frame->imageData[offset+(j*3)+2]*0.299; frame->imageData[offset+(j*3)] = temp; frame->imageData[offset+(j*3)+1] = temp; frame->imageData[offset+(j*3)+2] = temp; } } |


Memory
This one just keeps five frames and then adds them in to create a faded composite, should be simple.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | if(0 == memory_frameCounter) memory_frames[0] = cvCloneImage(frame); else if(2 == memory_frameCounter) memory_frames[1] = cvCloneImage(frame); else if(4 == memory_frameCounter) memory_frames[2] = cvCloneImage(frame); else if(6 == memory_frameCounter) memory_frames[3] = cvCloneImage(frame); else if(8 == memory_frameCounter) memory_frames[4] = cvCloneImage(frame); else if(10 <= memory_frameCounter) memory_frameCounter = -1; memory_frameCounter++; for(int i = 0; i < frame->height; i++) { for(int j =0; j < frame->width*3; j++) { memory_agg = frame->imageData[(i*frame->width*3)+j]; for(int k = 0; k < 5; k++) { memory_agg = (memory_agg + memory_frames[k]->imageData[(i*frame->width*3)+j])/2; } frame->imageData[(i*frame->width*3)+j] = memory_agg; } } |

I just can’t figure out what I’m doing wrong here.
Posted March 2nd, 2008 - PermalinkI’m taking a GUI design class right now and for slightly odd reasons he is having us do mini-projects in Photoshop and Flash. For the last bit of the Photoshop one we were supposed to cut Lance Armstrong out of a picture and put him in any background we wanted. I choose the gates to Mordor and I’m quite proud of the results.
Click through for the full size, it’s worth it I think.
OpenCV is a cross platform video library I’ve been playing with. Today I coded up a horizontal mirror effect, took about 30 minutes. I worked out all the byte manipulations on a piece of paper, that took the longest. Coding was a breeze with OpenCV, and I tried out some of the built in effects too, stacking them on top of each other.
Here’s my first version source for the mirror effect, it’s rough since I just translated what I had written down into code. “frame” is a captured IplImage.
1 2 3 4 5 6 7 8 9 | int halfsies = frame->width/2; for(int i = 0; i < frame->height; i++) { int offset = i*frame->width*3; for(int j = 0; j < halfsies; j++) { frame->imageData[offset+(frame->width*3-1)-2-(j*3)] = frame->imageData[offset+(j*3)]; frame->imageData[offset+(frame->width*3-1)-1-(j*3)] = frame->imageData[offset+(j*3)+1]; frame->imageData[offset+(frame->width*3-1)-(j*3)] = frame->imageData[offset+(j*3)+2]; } } |
Here is the reformed version, cleaner by far.
1 2 3 4 5 6 7 8 9 10 11 12 | int halfFrame = frame->width/2; int frameBytes = frame->width*3-1; for(int i = 0; i < frame->height; i++) { int offset = i*frame->width*3; for(int j = 0; j < halfFrame; j++) { int jBytes = offset+frameBytes-(j*3); int ojBytes = offset+(j*3); frame->imageData[jBytes-2] = frame->imageData[ojBytes]; frame->imageData[jBytes-1] = frame->imageData[ojBytes+1]; frame->imageData[jBytes] = frame->imageData[ojBytes+2]; } } |
And here is what it looks like. The first one is without any other effects, the second is with the OpenCV effect “erode”.


You can get the full source of my fxTest.cpp here if you want it.
The Introduction to programming with OpenCV was a great resource for me.
Posted February 26th, 2008 - Permalink