Über Zeitzonen hinweg

Inzwischen ist die erste Woche schon rum. Dank Skype lässt sich der Kontakt nach Hause wunderbar aufrecht erhalten. Nur gestaltet sich die Sache mit den 8 Stunden Zeitunterschied gar nicht so einfach:

xxxxxxxx sssssssssfffffffffffffss sssssssssfffffffffffffss ssssssfwwwwwfwwwffffffss ssssssfwwwwwfwwwffffffss ssssssfwwwwwfwwwffffffss ssssssfwwwwwfwwwffffffss ssssssfwwwwwwfffffffffss sssssssssfffffffffffffss sssssssssfffffffffffffss
sssssssssffffffffffffffs sssssssssfffffffffffffss sssssssffwwwfwwwwwffffss sssssssffwwwfwwwwwffffss sssssssffwwwfwwwwwffffss sssssssffwwwfwwwwwffffss sssssssffwwwfwwwwwfffffs sssssssssffffffffffffffs sssssssssfffffffffffffss xxxxxxxx
Freizeit
gleichzeitige Freizeit
Schlafen
Arbeiten

Kulinarisches

Zum Glück mag ich ja nicht nur gerne Sushi, sondern probiere ja auch sonst alles gerne, was die internationale Küche zu bieten hat.

Heute musste ich dann feststellen, dass es schwerer sein kann, Bekanntes als Unbekanntes zu essen (ich hab’s dann auch liegen lassen):

Vorbereitungen für das erste Quartal

In weniger als einer Woche bin ich in Tōkyō. So langsam aber sicher steigt die Nervosität. Über mein Blog und das Tag Japan 2014 werde ich für alle Interessierten ab und an was zum Leben und Arbeiten in Japan schreiben.

Auf der obigen Karte sind schon mal die drei wichtigsten Stationen eingezeichnet, bei denen ich mich hauptsächlich aufhalten werde.

Custom QR codes as a plastic badge

qr-hand
laser cutted QR code badge

Vor über einem Jahr hatten wir bei scoutnet überlegt, QR-Code Aufnäher selbst herzustellen. Wie wir schnell herausfinden mussten, ist die Produktion von individuellen Aufnähern mit einem solchen Detailgrad sehr aufwändig und teuer.

Als wir bei unserem diesjährigen Treffen wieder auf das Thema zu sprechen kamen, ist mir eingefallen, dass Laser-Cutting mit Gravur eine Möglichkeit sein könnte. Das Ergebnis kann sich sehen lassen:

Wie auch für meine aktuelle 3D-Kamerahalterung habe ich die Teile bei Formulor cutten lassen. Das verwendete Material ist ein Verbund aus einem schwarzen Acrylkern und einer silbirgen Deckschicht. Durch die Gravur wird der schwarze Kern sichtbar.

Das Ergebnis ist ausgezeichnet. Die Stückkosten für die QR-Plaketten liegen bei unter 0,35 €. Und wofür das ganze gut sein soll, kann ich dann hoffentlich später hier berichten.

More than one year ago we discussed the idea of creating custom qr code badges with a embroidery machine in our scoutnet group.

As we figured out, it it was quite difficult and expensive to create a lot of custom badges with this level of detail.

When we thought about our idea during our this years gathering, it came to my mind that laser cutting and engraving could be an alternative. The result is excellent. A single badge could be produced for as little as 0,35 €.

Creating Stereo Images in SketchUp

I do use the free Version of Trimble SketchUp (formerly known as Google SketchUp) quite a lot. The free version is missing a simple way to create stereoscopic images and I didn’t find a (free) Ruby script to do the job.

Time to revive some Ruby and computer graphic lectures.

SketchUp lets you manipulate the Camera object via its API.
The camera is defined by its eye, target and up-vector. The goal to achieve a stereoscopic image is to create a new camera, which is „parallel“ to the existing one.
This „parallel“ is a little bit more complicated in 3D space.

Figure 1 shows a generic camera which is not aligned within the global 3d space (colored axes).

Figure 2 shows the coordinate system of that camera itself. The stereoscopic cameras eye must be aligned on the red x axis.

Figure 3 shows how the second camera corresponds to the first, existing one.

Therefor the transformation which has to be applied is a translation in an angle of 90 degree to the up vector. SketchUps Points provide an offset method to do exactly that.

# get a reference to the active camera
view = Sketchup.active_model.active_view
cam = view.camera

# get copies of the cameras up vector and eye point
up = cam.up.clone
eye = cam.eye.clone

# create transformation for rotating the up vector by 90 degree an apply that
rotVec = Geom::Vector3d.new 0,1,0
rotAngle = -1 * Math::PI/2
transformation = Geom::Transformation.rotation eye, rotVec, rotAngle
vec = up.transform transformation

# calculate second cameras points with the calculated vector and the offset method
newEye = cam.eye.offset vec, distance
newTarget = cam.target.offset vec, distance
# up vector remains unchanged
newUp = cam.up

# apply new eye, target and up vector to the camera
cam.set newEye, newTarget, newUp

SketchUp also provides an API call to export images and so the complete script does that as well.

# define a value for eye distance in the document active unit
# 65 mm is a good average
# see http://en.wikipedia.org/wiki/Pupillary_distance
$eyeDistance = 65

# define the size of the 3D image
$imgWidth = 1024
$imgHeight = 768

# offset for stereo view (0 or 1)
$winOffset = 0

def moveCam(distance)
  view = Sketchup.active_model.active_view
  cam = view.camera

  up = cam.up.clone
  eye = cam.eye.clone
  rotVec = Geom::Vector3d.new 0,1,0
  rotAngle = -1 * Math::PI/2
  transformation = Geom::Transformation.rotation eye, rotVec, rotAngle
  vec = up.transform transformation
  newEye = cam.eye.offset vec, distance
  newTarget = cam.target.offset vec, distance
  newUp = cam.up
  newCam = cam.set newEye, newTarget, newUp
end

def exportPng(path, width, height)
  keys = {
    :filename => path,
    :width => width,
    :height => height,
    :antialias => true,
    :transparent => false
  }
  view = Sketchup.active_model.active_view
  view.write_image keys
end

def showInterlacedView(w, h, leftImg, rightImg)
  template = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>SU-Stereo</title><meta name="copyright" content="Stefan Motz, 2013"><style type="text/css" media="screen">body {padding:0;margin:0} #display{position:relative;width:%spx;height:%spx;overflow:hidden;}#display .leftImg {position:absolute;-webkit-mask:url(\'http://stefan-motz.de/su-stereo/assets/mask-horizontal-bw.png\');top:0px;left:0px;height:100%%;}#display .rightImg {position : absolute;top:0px;left:0px;height:100%%;}</style></head><body><div id="display"><img class="rightImg" src="file://%s"><img class="leftImg" src="file://%s"></div></body></html>'
  html = sprintf(template, w, h, leftImg, rightImg)

  dlg = UI::WebDialog.new('SU-Stereo', false,'SUStereoView', w, h, 150, 150 + $winOffset, true)
  dlg.navigation_buttons_enabled = false
  dlg.set_html(html)
  dlg.show
end

### main program

view = Sketchup.active_model.active_view

# ask user for filename and setup incl. pathes
saveName = UI.savepanel 'Save 3D files', '', "Stereo"
if (saveName == nil)
  exit(0)
end
fileNames = {
  :left => saveName + "_L.png",
  :right => saveName + "_R.png"
}

# move camera to left eye position
moveCam($eyeDistance/2 * -1)
# save left picture
exportPng(fileNames[:left], $imgWidth, $imgHeight)

# move camera to right eye position
moveCam($eyeDistance)
# save left picture
exportPng(fileNames[:right], $imgWidth, $imgHeight)

# reset view
moveCam($eyeDistance/2 * -1)

showInterlacedView($imgWidth, $imgHeight, fileNames[:left], fileNames[:right])

And here is what it looks like:

As a little extra the script displays the stereo image thru a webview in an horizontally interlaced way. This probably works on Mac only and is only useful, if you have a passive 3D monitor connected.

The exported stereo images can be post-processed by various tools, on request I might write a follow up on how to integrate a workflow with ImageMagick.

During development the SketchUp Ruby Code Editor by Alex Schreyer was quite helpful – thanks for that!

3D camera 2.0

Now it’s been 3 years since I built the first version of my 3D camera. Meanwhile we took plenty of pictures in 3D.

It always bothered me, that the previous model had no synced release. And while the landscape mode of the cameras is good for anaglyph images or 3D TVs, the portrait mode is more suitable for side-by-side images as used by paralax viewers.

By placing the cameras side-by-side in portrait mode, the whole gadget becomes a little more handy.

Key to build a new frame was the laser cutting service of Formulor. They offer laser cutting for different materials like acryl, wood, paper, etc. All one has to do is send in a vector file and you get precisely cutted profiles within a few days.

So I do hope to present some new pictures soon.

3D camera with cameras attached
3D camera acryl frame

Creating KML Image Overlay from GeoTIFF or tfw file

As stated in my previous post my wife came around with a large map from work.

After compressing the image we wanted to visualize the map within Google Earth. I was quite surprised to see, that there is no small, common tool for that, so I sat down and wrote some (php) code, which I would like to share with you. (Be warned: all the scripts only work for UTM.)

This is, what we will have in the end:

    $image = "~/Desktop/geo-image-overlay.tif";
    $geoData = getGeoTIFF($image);
    $kml = geoTiffToKML($exifData, array("title" => "GeoTIFF", "imgFile" => "~/Desktop/geo-image-overlay.tif"));
    print $kml;

 

<!--?xml version="1.0" encoding="UTF-8"?-->


GeoTIFF

    ~/Desktop/geo-image-overlay.tif

    51.5724
    51.2639
    7.1835
    6.9287



The KML-code can be copied an pasted into Google Earth.

First of all, the geographic reference must be obtained either from the image’s EXIF-Data or from the separate WorldFile, which, by convention has the same name as the original image but it’s own extension (tfw for tif images, jpw for jpg).

The easy way: use GeoTIFF information and read it with exiftool by Phil Harvey.

define("EXIFTOOL_BIN", "/usr/bin/env exiftool");
function getGeoTIFF($file) {
    $data = array();
    $cmd = sprintf(EXIFTOOL_BIN." -GeoTIFF:* -EXIF:* -json '%s'", $file);
    exec($cmd, $data, $val);
    if ($val == 0) {
        $geo = json_decode(implode("\n", $data), true);
        return $geo[0];
    } else {
        return array();
    }
}

It’s a little strange, but part of the relevant „GeoTIFF“ information is stored in an images „EXIF“ data.

If you don’t use exiftool but if you have a WorldFile for your image, you might as well use zthe following function:

define("UTM_DEFAULT_ZONE", 32);

function tfw2GeoTIFF($file, $zone=UTM_DEFAULT_ZONE) {
    $tfwPath = substr($file, 0, strrpos($file, ".")).".tfw";
    $tifPath = substr($file, 0, strrpos($file, ".")).".tif";

    $tfw = file($tfwPath);
    list($w, $h) = getimagesize($tifPath, $info);

    $data = array();
    $data['SourceFile'] = $tifPath;
    $data['Projection'] = "UTM zone ".$zone;
    $data['ModelTiePoint'] = sprintf("%f %f 0 %f %f 0", $tfw[1], $tfw[2], $tfw[4] + (0), $tfw[5]);
    $data['PixelScale'] = sprintf("%f %f 0", $tfw[0], $tfw[3]);
    $data['ImageHeight'] = $h;
    $data['ImageWidth'] = $w;

    return $data;
}

The main problem is, that the WorldFile doesn’t contain any information about the reference system and units used within the file – you just have to know it. Who on earth defines such a „format“?
I just assumed to get UTM geo information and you have to assume the right zone.
For convenience the function uses the same attribute names as the GeoTIFF spec.

As said, I just assumed UTM data. As KML files have to contain coordinates as WGS84 I searched for a conversion script which I found on stack overflow.
Here is the PHP adaption:

function utm2wgs84($utmZone,$east,$north) {
  // This is the lambda knot value in the reference
  $LngOrigin = deg2rad($utmZone * 6 - 183);

  // The following set of class constants define characteristics of the
  // ellipsoid, as defined my the WGS84 datum.  These values need to be
  // changed if a different dataum is used.

  $FalseNorth = 0.;   // South or North?
  //if (lat < 0.) FalseNorth = 10000000.  // South or North?   //else          FalseNorth = 0.   $Ecc = 0.081819190842622;       // Eccentricity   $EccSq = $Ecc * $Ecc;   $Ecc2Sq = $EccSq / (1. - $EccSq);   $Ecc2 = sqrt($Ecc2Sq);      // Secondary eccentricity   $E1 = ( 1 - sqrt(1-$EccSq) ) / ( 1 + sqrt(1-$EccSq) );   $E12 = $E1 * $E1;   $E13 = $E12 * $E1;   $E14 = $E13 * $E1;   $SemiMajor = 6378137.0;         // Ellipsoidal semi-major axis (Meters)   $FalseEast = 500000.0;          // UTM East bias (Meters)   $ScaleFactor = 0.9996;          // Scale at natural origin   // Calculate the Cassini projection parameters   $M1 = ($north - $FalseNorth) / $ScaleFactor;   $Mu1 = $M1 / ( $SemiMajor * (1 - $EccSq/4.0 - 3.0*$EccSq*$EccSq/64.0 -     5.0*$EccSq*$EccSq*$EccSq/256.0) );   $Phi1 = $Mu1 + (3.0*$E1/2.0 - 27.0*$E13/32.0) * sin(2.0*$Mu1)     + (21.0*$E12/16.0 - 55.0*$E14/32.0)           * sin(4.0*$Mu1)     + (151.0*$E13/96.0)                          * sin(6.0*$Mu1)     + (1097.0*$E14/512.0)                        * sin(8.0*$Mu1);   $sin2phi1 = sin($Phi1) * sin($Phi1);   $Rho1 = ($SemiMajor * (1.0-$EccSq) ) / pow(1.0-$EccSq*$sin2phi1,1.5);   $Nu1 = $SemiMajor / sqrt(1.0-$EccSq*$sin2phi1);   // Compute parameters as defined in the POSC specification.  T, C and D   $T1 = tan($Phi1) * tan($Phi1);   $T12 = $T1 * $T1;   $C1 = $Ecc2Sq * cos($Phi1) * cos($Phi1);   $C12 = $C1 * $C1;   $D  = ($east - $FalseEast) / ($ScaleFactor * $Nu1);   $D2 = $D *  $D;   $D3 = $D2 * $D;   $D4 = $D3 * $D;   $D5 = $D4 * $D;   $D6 = $D5 * $D;   // Compute the Latitude and Longitude and convert to degrees   $lat = $Phi1 - $Nu1*tan($Phi1)/$Rho1 *     ( $D2/2.0 - (5.0 + 3.0*$T1 + 10.0*$C1 - 4.0*$C12 - 9.0*$Ecc2Sq)*$D4/24.0      + (61.0 + 90.0*$T1 + 298.257223563*$C1 + 45.0*$T12 - 252.0*$Ecc2Sq - 3.0*$C12)*$D6/720.0 );   $lat = rad2deg($lat);   $lon = $LngOrigin +     ( $D - (1.0 + 2.0*$T1 + $C1)*$D3/6.0       + (5.0 - 2.0*$C1 + 28.0*$T1 - 3.0*$C12 + 8.0*$Ecc2Sq + 24.0*$T12)*$D5/120.0) / cos($Phi1);   $lon = rad2deg($lon);   // Returns a PC_LatLon object   return array("lat" => $lat, "lon" => $lon);
}

And finally here comes the main function to produce the KML.
(Sorry for the lame tempting mechanism…)

define("KML_TEMPLATE", '<!--?xml version="1.0" encoding="UTF-8"?-->


%s

    %s

    %f
    %f
    %f
    %f


');

function geoTiffToKML($geoTiff, $options = array()) {
    $scale = isset($options['imgScale'])?1/$options['imgScale']:1;
    $file = isset($options['imgFile'])?$options['imgFile']:$geoTiff['SourceFile'];
    $title = isset($options['title'])?$options['title']:"No Name";

    // calculate n, s, w, e

    // projection analysis
    if (substr($geoTiff['Projection'], 0, 8) == "UTM zone") {
        $zone = intval(substr($geoTiff['Projection'], 8));

        list($rot1, $rot2, $u1, $eUTM, $nUTM, $u2) = explode(" ", trim($geoTiff['ModelTiePoint']));
        list($scaleNS, $scaleWE, $u) = explode(" ", trim($geoTiff['PixelScale']));

        $iw = $geoTiff['ImageWidth'];
        $ih = $geoTiff['ImageHeight'];

        $wUTM = abs($scale * $scaleWE * $iw) + $eUTM;
        $sUTM = $nUTM - abs($scale * $scaleNS * $ih);

        $ne = utm2wgs84($zone,$eUTM,$nUTM);
        $n = $ne['lat'];
        $e = $ne['lon'];
        $sw = utm2wgs84($zone,$wUTM,$sUTM);
        $s = $sw['lat'];
        $w = $sw['lon'];

        return sprintf(KML_TEMPLATE, $title, $file, max($n, $s), min($n, $s), max($e, $w), min($e, $w));

    } else {
        die('No support for other projections than UTM');
    }
}

Converting/scaling large TIFF images from a GIS

My wife works as in a land surveying office. This weekend she brought a compressed TIFF image, which was a rasterized version of a cities GIS data.

In compression mode, the file took about 90MB space, but as the image is 56706 px by 47255 px in 8Bit greyscale, it holds a little less than 2.5 GB of information.

There was no chance opening it in Photoshop, nor was ImageMagick able to create a scaled version.

Finally vips (VIPS – VipsWiki) did the job:

vips im_affinei_all input.tif output.tif vsqbs 0.4 0 0 0.4 0 0

Vips offers different interpolators: nearest, bilinear, bicubic, lbb, nohalo, vsqbs; the latest worked best for our original material.

DropBox Gallery Demo

It’s been a while since I first mentioned my Dropbox Gallery Plugin.

Up to today I did not release it for two major reasons:

  1. You have to get your own dropbox developer key to use the plugin . That’s a barrier for most users.
  2. I did not manage to (and didn’t really try to) optimize memory consumption when the images are cached. Right now you have to reload the page several times, until all images are cached. That’s a real show stopper.

After all I do know, that some people might like to use the plugin nevertheless and perhaps they might even optimize it.

Please drop me a line if you use the Dropbox gallery , enhanced it or even pushed it to github or google code.

dropbox-gallery.tar.gz

If you ant to use the plugin, make sure to configure it and to get a Dropbox API-Key.
It’s recommended to use WP-prettyPhoto along with the dropbox gallery.

[dbgallery path=/Photos/Blog/DBG-Screenshots]

3d, coding, diy, japan, scouting …