Yhtenäiskoulu

TimeLapse-kuvaus

Kuvien ottaminen

Raspilla on helppo ottaa kuvia. Siitä on ohjeita muualla. Kuvat siirsin scp:llä ja tietokantaan lisäsin ssh:lla.

Kuvien siirtäminen ja tallettaminen


#!/bin/bash

f=urjala.luntti.net/tdir/$1
echo "$f";
#  #nimi="${f#*/}";
nimi=$1;
#  echo $nimi;

## Siirrä tietokantaan

#Jos kamera1 tai kamera2
if [[ $f == *"Kamera1"* ]]
then
    kamera="1";
elif [[ $f == *"Kamera2"* ]]
then
    kamera="2";
else
    echo "Ei ollut kuvia. Poistun";
    exit;
fi
#Kirjoita tietokantaan
echo "INSERT INTO kuvat (kamera, nimi) \
   VALUES ($kamera, '$nimi' )" | \
   mysql -u utl --password=TÄMÄONVAIKEASALASANA \
   -h PALVELIMENOSOITE TIETOKANNANNIMI 

#Kopioi tiedostot oikeaan paikkaan ja muuta koko
mv -v "$f" urjala.luntti.net/finalDir/
convert "$f" -flip -flop urjala.luntti.net/finalDir/${nimi} 
#echo "convert finalDir/${nimi} -resize 320 finalDir/320_${nimi}"
convert urjala.luntti.net/finalDir/${nimi} \
  -resize 320 urjala.luntti.net/finalDir/320_${nimi}
convert urjala.luntti.net/finalDir/${nimi} \
  -resize 640 urjala.luntti.net/finalDir/640_${nimi}
convert urjala.luntti.net/finalDir/${nimi} \
  -resize 1280 urjala.luntti.net/finalDir/1280_${nimi}

Bash-koodin ajoin etänä Raspista ssh-putkella. Koodi tsekkaa, kameran, muuttaa nimen, tallettaa tietokantaan sekä vaihtaa kokoa.

Käytännössä ongelma oli tietokannasta luku, koska tein siihen hyvin yksinkertaisen php-skriptin, joka ei ottanut huomioon että toinen kameroista poistetaan verkosta.

Kuvista on eri kokoiset versiot, jotta lataaminen olisi nopeaa.

Flip-flop -käskyt vaihtavat kuvan suuntaa, sillä kamera saattaa olla väärinpäin.

Alivalottuneiden poistaminen


import os
import numpy as np
import pylab
import mahotas as mh


def showImage( img):
          print img.max()
          print img.min()
          print img.mean()
          print img.std()
    
          #img = np.flipud( img )
          pylab.imshow( img )
          pylab.show()

deleted = 0
files_in_dir = os.listdir('.')
for f in files_in_dir:
    #print type( f )
    if "jpg" in f:
      #do the check you need on each file
      print f
      img = mh.imread(f)
      if ( img.mean() < 100 ):
        deleted = deleted + 1
        print deleted
        os.rename(f,'black/'+f)
        #showImage( img )
      else:
        os.rename(f, 'ok/'+f)  

Pythonilla poistin alivalottuneet, eli pimeät (yöllä otetut) kuvat.

Mahotas on nopea kirjasto kuvien lataamiseen (niitä on paljon. Tämä skripti käy kaikki tiedostot läpi ja jos ne ovat jpg-kuvia, niin lukee sen kuvan ja laskee pikseleiden valoisuuden. Jos valoisuus on liian pieni (tässä 100), se poistetaan eli siirretään mustien kuvien kansioon.

Tuhraantuneiden poistaminen


import os
import numpy as np
import pylab
import mahotas as mh

def showImage( f ):
    img = mh.imread(f)
    #print img.max()
    #print img.min()
    #print img.mean()
    #print img.std()
    pylab.imshow( img )
    pylab.show()
    
    
files_in_dir = os.listdir('.')
deleted = 0
for f in files_in_dir:
    #do the check you need on each file
    
    if ".jpg" in f:
      img = mh.imread(f, as_grey=True) 
    
      p = np.asarray(img).astype('int8')
      dy, dx = np.gradient(p)
      dy = abs( dy )
      dx = abs( dx )
      summa = sum(sum(dx))+sum(sum(dy))
    
      if (summa < 5700000):      
        print "sum of gradients: " , summa
        deleted = deleted + 1
        print deleted
        os.rename( f, 'blurred/'+f)
        #showImage( f )
print deleted

Sumu, tiukka vesisade tai vesipisara linssin edessä aiheuttaa kuvien tuhraantumista. Ne erottelin automaattisesti kuten kameran autofokus, eli gradientilla (derivaatalla). Kuvasta voidaan laskea gradientti, siitä otetaan itseisarvo ja lasketaan jokainen piste yhteen.

Jos gradientin summa (integraali) on liian pieni, siinä ei ole kontrastieroja ja kuva on tuhruinen. siispä siirretään se pois.

Samanlaisten kuvien poisto I


import os
import numpy as np
import pylab
import mahotas as mh

def diffImages( i1, i2 ):
    p1 = np.asarray(i1).astype('float')  ##int8
    p2 = np.asarray(i2).astype('float')

    return sum( sum( abs( p1 - p2 ) ) )/1e6

files = os.listdir('.')
files.sort()

deleted = 0
differences = []

for i in range( len( files )-1 ):
    if ( "jpg" in files[i] and "jpg" in files[i+1] ):
         img1 = mh.imread(files[i], as_grey=True)
         img2 = mh.imread(files[i+1], as_grey=True)

         diff = diffImages( img1, img2 )
         print i

         #print diff
         #showImages( img1, img2, diff )
         #differences.insert(0, diff)
         #print differences


         if (diff < 8 ):
            deleted = deleted + 1
            print deleted
            os.rename( files[i], 'similar/'+files[i])  



#thefile = open('differences.txt', 'w')
#for item in differences:
#    thefile.write("%f\n" % item )

#f = pylab.figure()
#pylab.hist( differences, 50, normed=1 )
#pylab.show()

#print differences

Halusin tehdä videon, jossa tapahtuu jotain. Siispä poistin samanlaiset kuvat, esimerkiksi öiseen tai ilta-aikaan ei tapahtunut juuri mitään (joku lokki kyllä lentää jossain kuvassa), eikä aina päivisinkään. Lisäksi kuvia oli yhä niin paljon, että niistä ei saa vielä järkevää videota.

Samanlaiset kuvat poistin ykkösnormilla, eli Manhattan- tai taksikuskinormilla. Se on hyvin helppo ja nopea laskea tietokoneella: lasketaan kahdesta kuvasta vastaavien pikselien erotukset. Kuvat otetaan harmaasävyisinä. Jos erotus on tarpeeksi pieni, tässä 8, kuvat tulkitaan samanlaisiksi.

Tässä vaiheessa kokeilin hieman analysointia, eli ensin tarkistin, kuinka kaukana peräkkäiset kuvat on toisistaan. Viereisessä koodissa se kohta on kommentoitu.


lines = [line.rstrip('\n') for line in open('differences.txt')]
L = map(float, lines)
pylab.hist( L, 50 )

Histogrammista näkee heti, että n. kahdeksan on hyvä arvo.

Samanlaisten kuvien poisto II


import os
import numpy as np
import pylab
import mahotas as mh

def onclick(event):
    global move
    move=True
    
def showImages( i1, i2, ots ):
    f = pylab.figure()
    ax1=f.add_subplot(2,1,1)
    pylab.imshow( img1 )
    ax2=f.add_subplot(2,1,2)
    pylab.imshow( img2 )

    f.set_size_inches( 9,12, forward=True )
    f.canvas.set_window_title( ots )
    ax1.set_xticklabels([])
    ax1.set_yticklabels([])
    ax2.set_xticklabels([])
    ax2.set_yticklabels([])

    cid=f.canvas.mpl_connect('key_press_event', onclick)
    pylab.show()
    f.canvas.mpl_disconnect(cid)

def diffImages( i1, i2 ):
    p1 = np.asarray(i1).astype('float')  
    p2 = np.asarray(i2).astype('float')

    return sum( sum( abs( p1 - p2 ) ) )/1e6


files = os.listdir('.')
files.sort()
deleted = 0
move = False

for i in range( len( files )-1 ):
  if ( "jpg" in files[i] and "jpg" in files[i+1] ):
    img1 = mh.imread(files[i])
    img2 = mh.imread(files[i+1])

    move=False
    showImages( img1, img2, files[i] )
    if (move):
      deleted = deleted + 1
      print deleted
      os.rename( files[i], 'similar2/'+files[i])
    else:
     os.rename( files[i], 'ok/'+files[i])

Viimeiset 10000 kuvaa piti käsitellä käsin. Varjot ja valoisuuden erot aiheuttavat eroja, joita automatiikka ei suostunut helpolla erottamaan. Viereinen skripti näyttää kaksi peräkkäistä kuvaa allekkain ja jos painaa jotain nappia, niin toinen siirtyy pois.

Lopulta jäljelle jäi muutama tuhat kuvaa, joista tein pienen animaation.

Videon teko

Videon tein Blenderillä. Katso lisää.

Kiitos tekijöille:

Huhdin Robot ja erityisesti Aaro V, Arto V, Emil R, Rami R, Tuomas M, Vesa S, ja muut kutka unohdin.

Kunta kustansi Raspit, sähköt, netin ja asennuskulut.

Toinen kamera oli Reikonlinnassa, ja Reikonlinna hoiti sen kameran pienet sähkö- ja nettikulut. Suuri kiitos Reikonlinnalle.