Author Archives: chris

Using a Trendnet IP110W camera with a fixed FTP location

The TRENDnet TV-IP110W has few options when setting up an FTP transfer. It will always place the image in sub-folder based on the date such as 20110308/230117_9.jpg for the image taken on March 8, 2011 at 23:01:17.9. This makes it difficult to use the image on a static html webcam page.

To move the last picture taken to a fixed file location, a cron job is needed to find the JPG file and move/rename it to a fixed name. In this case the camera is set to take an image every 60 seconds and FTP it to the server. The following script is also set to run as a cron job every 60 seconds.

In this example trendnet is the Filename Prefix set in the Event Configuration >> General Setting tab and /path/to/ftp is the location set in Event Server

# move current picture to *_old
mv /path/to/ftp/trendnet.jpg /path/to/ftp/trendnet_old.jpg

# find where the trendnet put the image and move it to the ftp root location
find /path/to/ftp/trendnet -name '*.jpg' -exec mv {} /path/to/ftp/trendnet.jpg \;

Chumby Webcam Image Cycling


Sample Actionscript for showing 4 webcams on a single Chumby display and cycling through 9 webcams. Every second replacing one of the images with the next one in the sequence.

<br />
class CamView.Main {&lt;br&gt;<br />
  static var urls:Array = [&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam1.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam2.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam3.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam4.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam5.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam6.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam7.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam8.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam9.jpg&quot; ];&lt;br&gt;<br />
  static var numurls = 9;&lt;br&gt;<br />
  private var parent:MovieClip;&lt;br&gt;<br />
  private var tf:TextField;&lt;br&gt;<br />
  private var mcTL:MovieClip;&lt;br&gt;<br />
  private var mcTR:MovieClip;&lt;br&gt;<br />
  private var mcBL:MovieClip;&lt;br&gt;<br />
  private var mcBR:MovieClip;&lt;br&gt;<br />
  static var count:Number = 0;&lt;br&gt;<br />
  static var urlidx:Number = 4;</p>
<p>  function UpdateText(mc:MovieClip) {&lt;br&gt;<br />
    // Add time based url extension to force non cached images&lt;br&gt;<br />
    var time:Date = new Date();&lt;br&gt;<br />
    var sec:String = String(time.getSeconds());&lt;br&gt;<br />
    var cnt:String = String(time.getMinutes()) + sec;&lt;br&gt;<br />
    var urlext:String = &quot;?t=&quot; + cnt;</p>
<p>    var mlMyLoader:MovieClipLoader = new MovieClipLoader();</p>
<p>    // rotate images and urls&lt;br&gt;<br />
    switch (count) {&lt;br&gt;<br />
      case 0: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam1&quot;); break;&lt;br&gt;<br />
      case 1: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam2&quot;); break;&lt;br&gt;<br />
      case 3: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam3&quot;); break;&lt;br&gt;<br />
      case 2: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam4&quot;); break;&lt;br&gt;<br />
    }&lt;br&gt;</p>
<p>    if (++count == 4) count = 0;&lt;br&gt;<br />
    if (++urlidx == numurls) urlidx = 0;</p>
<p>    delete mlMyLoader;&lt;br&gt;<br />
  }</p>
<p>  function Main(mc:MovieClip) {&lt;br&gt;<br />
    // save reference to main movieclip&lt;br&gt;<br />
    this.parent = mc;</p>
<p>    var mlMyLoader:MovieClipLoader = new MovieClipLoader();</p>
<p>    mcTL = mc.createEmptyMovieClip(&quot;mCam1&quot;, mc.getNextHighestDepth());&lt;br&gt;<br />
    mcTR = mc.createEmptyMovieClip(&quot;mCam2&quot;, mc.getNextHighestDepth());&lt;br&gt;<br />
    mcBL = mc.createEmptyMovieClip(&quot;mCam3&quot;, mc.getNextHighestDepth());&lt;br&gt;<br />
    mcBR = mc.createEmptyMovieClip(&quot;mCam4&quot;, mc.getNextHighestDepth());&lt;/p&gt;</p>
<p>    // assume cam images are 640x480, scale by 1/4 for Chumby display&lt;br&gt;<br />
    mcTL._xscale = 25;&lt;br&gt;<br />
    mcTL._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[0], &quot;mCam1&quot;);</p>
<p>    mcTR._x = 160;&lt;br&gt;<br />
    mcTR._xscale = 25;&lt;br&gt;<br />
    mcTR._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[1], &quot;mCam2&quot;);</p>
<p>    mcBL._y = 120;&lt;br&gt;<br />
    mcBL._xscale = 25;&lt;br&gt;<br />
    mcBL._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[2], &quot;mCam3&quot;);</p>
<p>    mcBR._x = 160;&lt;br&gt;<br />
    mcBR._y = 120;&lt;br&gt;<br />
    mcBR._xscale = 25;&lt;br&gt;<br />
    mcBR._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[3], &quot;mCam4&quot;);</p>
<p>    delete mlMyLoader;</p>
<p>    var mytimer = setInterval(UpdateText, 1000, mc);<br />
  }</p>
<p>  static function main(mc:MovieClip) {&lt;br&gt;<br />
    var app = new Main(mc);&lt;br&gt;<br />
  }&lt;br&gt;<br />
}&lt;br&gt;<br />

CHDK uBasic script for Orton Effect

Working with the CHDK ( http://chdk.wikia.com/wiki/CHDK ) for Canon Powershot cameras. Here’s a sample uBasic script for taking two sequential photos to be later combined for an Orton Effect photo.

Script to be copied to your scripts folder as orton.bas

rem Savi Technologies
@title Orton
@param a Exposure Step
@default a 3
@param b Focus Step [x6]
@default b 6

if a<1 then let a=2
if b<1 then let b=1

get_focus f
rem get_av d
rem get_tv e

print "First Shot..."
click "shoot_full"

sleep 5000

for i=-b to -1
f=6*f/(b+6)
next i

set_focus f
set_av_rel 0-2
set_tv_rel a

print "Second Shot..."
click "shoot_full"

end

Webcam image refresh with jQuery

Code snippet to refresh images on a web page without refreshing the whole page.

Add the header with a reference to jquery.

<html>
<head>
   <script type="text/javascript" src="js/jquery.js">
   </script>

Set the refresh interval on document ready, calling our function reloadImages()

<script type="text/javascript">
$(document).ready(function() {
   setInterval('reloadImages()', 60000); // 60 seconds
});

Add our function reloadImages to reference our image ID of c131a in this case. Then change the src image to the same name plus some random attribute to prevent caching.

function reloadImages()
{
  $('#c131a').attr('src', 'c131a.jpg?' + Math.random());
}
</script>

In the body of the script, add a normal image reference making sure the id matches the one in the above reloadImages function.

<title>Cams</title>
</head>
<body>
  <img src="c131a.jpg" id="c131a" border="1" />
</body>
</html>

GeoSetter on photos from Nikon NEF

So you’ve done a photo shoot in RAW, spent time adjusting each photo’s composition, and saved the final results to a set of JPGs. Now you want to geo-tag the set, GeoSetter appears to correctly geo-locate each photo, all looks good until you save changes and get:

Warning: Unknown format (800) for SubIFD tag 0x0
Warning: [minor] Entries in SubIFD were out of sequence. Fixed.
Error: Bad format (800) for SubIFD entry 0

Now you can’t save the updates, you can’t GeoTag your photos. Ok, so we try Google’s GPicSync, but it also fails. If you enable storing the Lat/Lon as keywords, that does work, but neither can seem to modify the lat/long of the image.

So we need to strip out the offending unrecognized data for these programs to correctly store the location, but keep the neccessary information such as time stamps. And we need to do so in a batch process, we don’t want to have to hand edit the EXIF data for each image. Many of the high level photo management tools (including GPicSync and GeoSetter) depend on the low-level tool ExifTool.

Using the Window’s GUI interface to this tool, it will report the Nikon information as warnings, data it does not understand. Unfortunately, I saw no way to tell the tool to remove that block of data from the image, only to modify data within sections it understood.

What did work was the tool ViewNX which is provided by Nikon.  Select the images and from the File menu click Convert Files… Check the Box “Remove XMP/IPTC information”, leave the others unchecked and click Convert…

Now the unknown parts are removed, and GeoSetter will have no problems re-saving the files after geo-tagging.