Tuesday, August 1, 2017

Wednesday, June 28, 2017

Disk Temizliği

Arada sırada Ubuntu / Unix'imizde "bahar temizliği" iyi olabilir; ardı ardına kurulan paketler, işletilen programların yarattığı onbellek (cache) dosyaları arkada kalıp diski bitirebilir. Temizlik için bazı programlar:

En basiti komut satırında du,

sudo du -hx --max-depth=1 /

Dizin bazında kullanım raporu verir. Benzer bir raporu görsel araç üzerinden dosya idarecisi Nemo ile görebiliriz, herhangi bir dizin üzerinde sağ tıklama ve Open With | Disk Usage Analyzer.

Otomatik temizleme icin bleachbit: Kurulum apt-get install bleachbit ile, gksudo ile başlatıp solda temizlenecek şeyler seçilir. APT temizliği iyi oluyor, diğer bir sürü seçenek te var.

Thursday, June 8, 2017

GraphHopper: Pür Java ile Haritada Yol Bulmak

Yol bulmak için OSRM projesinden bahsettik. OSRM çok hızlı çalışır, sonuçları kullanışlı, fakat C++ ile yazılmış, eğer pür Java bazlı bir çözüm istiyorsak (ki böylece kodları mesela Android'e rahatça koyabilelim) o zaman Graphhopper projesi var. GH ismi bir kelime oyunu, grasshopper bilinebileceği gibi çekirge demek, graph (yani çizit) hopper "bir çizitte sağ sola zıplayan" bir görüntü zihinde canlandırıyor, ki bu görüntü pek gerçekten uzak değil, haritada yol bulma algoritmaları hakikaten bilgisayar bilimdeki çizit yapısını temel alıyorlar. Kod şurada,

https://github.com/graphhopper/graphhopper

Kodun derleme sistemi gradle / Maven bazlı, fakat o kadar çetrefil derleme işlemlerine gerek yok, önce GH içinden gerekli java kodlarını çıkartalım, sonra çok baz bir derleme sistemi ile ve ek birkaç jar ile derlemeyi kendimiz dışarıda halledelim. Alternatif bir proje / dizin yaratılır, altında lib, src, resources dizinleri olur. GH indirilir, şimdi alternatif dizin altında

cp -r [GH]/core/src/main/java/* src/
cp -r [GH]/core/src/main/resources/* resources
cp -r [GH]/reader-osm/src/main/java/* src/

ile sadece gerekli Java kodlarını alırız. Şimdi gereken jarları alalım, 


Bu jarları nasıl bulduk? Ya derlerken olmayan jar için gelen hata mesajlara bakıp bu class ismini "vs.vs.Class maven jar" ile Google'da ararız, ya da [GH]/pom.xml ya da hangi pom.xml var ise versiyon sayısını oradan buluruz ve yine Google'da ararız. Mesela jackson-databind lazım, "jackson-databind maven jar" bize gerekli bağlantıyı verir. Zaten görüldüğü gibi bağlantılarda belli bir kalıp da var, class ismi ve versiyon ile bağlantı tahmin de edilebilir.

Jar'lar lib altına gidiyor tabii. Derleme için Ant build.xml

<project name="gh" default="dist" basedir=".">
  <property name="src" location="src"/>
  <property name="build" location="build"/>
  <property name="dist" location="bin"/>
  <target name="init">
    <tstamp/>
    <mkdir dir="${build}"/>
  </target>
  <path id="build.classpath">
    <fileset dir="lib">
      <include name="**/*.jar"/>
    </fileset>
  </path>
  <path id="compile.classpath">
    <pathelement location ="${build}"/>
    <fileset dir="lib">
      <include name="**/*.jar"/>
    </fileset>
  </path>
  <target name="compile" depends="init"
        description="compile the source">
    <javac destdir="${build}">
      <src path="${src}"/>
    <classpath refid="build.classpath"/>
  </javac>
  </target>
  <target name="resources">
    <copy todir="${build}" includeEmptyDirs="no">
      <fileset dir="resources">
        <patternset>
          <include name="**/*.*"/>
        </patternset>
      </fileset>
    </copy>
  </target>
  <target name="dist" depends="compile" description="generate the distribution">
    <mkdir dir="${dist}"/>
    <jar jarfile="${dist}/ghopper.jar" basedir="${build}"/>
  </target>
    <target name="test" depends="compile">
      <java fork="yes" classname="Test" failonerror="true">
        <classpath refid="compile.classpath"/>
      </java>
  </target>
  <target name="clean"
        description="clean up">
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

Bu kadar, ant ile derleriz. Kodların işleyişi için OSM harita dosyaları gerekli, bu dosyalar daha önce de bahsedilen 


sitesinden alınabiliyor. Mesela Türkiye için 


Bu dosyayı bunzip2 ile açıp gzip ile tekrar zipleyelim, çünkü GH .gz dosyası istiyor. Ya da .osm dosyası olduğu gibi bırakılır, GH onu da işler.

GH sürekli direk .gz dosyasını da kullanmaz bu arada, onu işleyip bazı ara dosyalar üretmesi lazım (daha önce bahsedilen çizit dosyaları bunlar, GH OSM formatını alıp daha hızlı işleyiş için çizit yapısına geçiyor). Ara dosyaların üretilmesi ve yol bulma testi çin src altında Test.java yaratalım, 

import com.graphhopper.*;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.reader.osm.GraphHopperOSM;

public class Test {

    static String tmpGraphFile = "/tmp/gh";

    public static void createGraph() {
        String fin = "[DB DIZINI]/turkey-latest.osm.gz";
        GraphHopper gh = new GraphHopperOSM().setStoreOnFlush(true).
            setEncodingManager(new EncodingManager("foot")).setCHEnabled(false).
            setGraphHopperLocation(tmpGraphFile).
            setDataReaderFile(fin);
        gh.importOrLoad();
    }

    public static void testPath() {
        GraphHopper gh = new GraphHopperOSM().
            setEncodingManager(new EncodingManager("foot")).setCHEnabled(false).
            setGraphHopperLocation(tmpGraphFile);
        gh.importOrLoad();
        GHResponse res = gh.route(new GHRequest(40.987659, 29.036428, 
                                                40.992186, 29.039228).
                                 setVehicle("foot"));
        System.out.println(""+res );
    }

    public static void main(String[] args) throws Exception{
        createGraph();
        testPath();
        testPath();
    }
}

Isletmek icin ant test. Araba tarifleri için setEncodingManager ve setVehicle çağrılarında car kullanılır. Kodda createGraph'ın sadece bir kez çağırılması yeterli, bu çağrı /tmp/gh altında gerekli çizit dosyalarını yaratır (çağrı birkaç dakika sürebilir), diğer tüm yol bulma çağrıları bundan sonra çizit dosyalarını kullanıp hızlı şekilde cevabı üretir. Artık elimizde pür Java bazlı, çok hızlı işleyen, mobilde İnternet bağlantısız çalışabilecek harita tarif kodları var. Üstteki kodların kendisi fazla yer tutmuyor zaten (biraz da bunun için daha büyük GH içinden sadece gerekli java ve jar dosyalarını çekip çıkardık, az sayıda java ve 10 tane jar dosyasi ile elimize yol tarifi yapabilen çekirdek bir kod geçti), çizit dosyaları ise bir şehrin büyüklüğüne göre 10-20 MB arasında olur.

Kodun teorik, algoritmik yapısını merak edenler için: kısa yol algoritmaları çoğunlukla Dijkstra algoritmasını kullanırlar (Dijkstra'yı bilgisayar bilim notlarında isledik), OSRM ve Graphhopper da böyle yapıyor, yanlız Dijkstra yaklaşımına bazı ekler yapıyorlar, yaklaşımın adı "kısalan hiyerarşiler (contracting hierarchies)"; yol ağ yapısına bazı noktalarda haritaya dışarıdan kısa yol / zıplamalar ekliyorlar, böylece Dijkstra daha hızlı, verimli işliyor.

Dil, derleme hakkında bazı bilgiler:  

Monday, June 5, 2017

Zaman Serilerinde Tepe Noktası Bulmak (Peak Detection)

Matlab / Octave kullananlar zaman serisi tepe noktası analizinde peakutils adlı bir aracı kullanıyorlar. Bu kod Python'a taşınmış,

https://github.com/atjacobs/PeakUtils

Kod içinden gerekli fonksiyonu çıkarttık,

import numpy as np

def indexes(y, thres, min_dist):
    thres *= np.max(y) - np.min(y)
    dy = np.diff(y)
    peaks = np.where((np.hstack([dy, 0.]) < 0.)
                     & (np.hstack([0., dy]) > 0.)
                     & (y > thres))[0]

    if peaks.size > 1 and min_dist > 1:
        highest = peaks[np.argsort(y[peaks])][::-1]
        rem = np.ones(y.size, dtype=bool)
        rem[peaks] = False

        for peak in highest:
            if not rem[peak]:
                sl = slice(max(0, peak - min_dist), peak + min_dist + 1)
                rem[sl] = True
                rem[peak] = False

        peaks = np.arange(y.size)[~rem]

    return peaks

Parametreler tepe noktalarının arasında en az ne kadar mesafe, ayrıca noktaların en az ne kadar genliğe (y yönünde) sahip olmaları gerektiği. Eldeki bir veri üzerinde indexes(d,thres=0.3,min_dist=3) çağrısı sonrası

Java için benzer bir çağrı, alttaki kütüphaneden,

https://github.com/JorenSix/TarsosDSP

import java.util.*;

public static LinkedList<Integer> indexes(double[] data,
                                          int width,
                                          double threshold,
                                          double decayRate,
                                          boolean isRelative) {

    LinkedList<Integer> peaks = new LinkedList<Integer>();
    int maxp = 0;
    int mid = 0;
    int end = data.length;
    double av = data[0];
    while (mid < end) {
        av = decayRate * av + (1 - decayRate) * data[mid];
        if (av < data[mid])
            av = data[mid];
        int i = mid - width;
        if (i < 0)
            i = 0;
        int stop = mid + width + 1;
        if (stop > data.length)
            stop = data.length;
        maxp = i;
        for (i++; i < stop; i++)
            if (data[i] > data[maxp])
                maxp = i;
        if (maxp == mid) {
            if (overThreshold(data, maxp, width, threshold, isRelative,av)){
                peaks.add(new Integer(maxp));
            }
        }
        mid++;
    }
    return peaks;
}

public static boolean overThreshold(double[] data, int index, int width,
                                    double threshold, boolean isRelative,
                                    double av) {

    int pre = 3;
    int post = 1;

    if (data[index] < av)
        return false;
    if (isRelative) {
        int iStart = index - pre * width;
        if (iStart < 0)
            iStart = 0;
        int iStop = index + post * width;
        if (iStop > data.length)
            iStop = data.length;
        double sum = 0;
        int count = iStop - iStart;
        while (iStart < iStop)
            sum += data[iStart++];
        return (data[index] > sum / count + threshold);
    } else
        return (data[index] > threshold);
}

Güzel. Fakat bu tepe noktası çağrıları toptan şekilde çalışıyor, verinin tamamını alıyorlar, fonksiyon verinin tamamına bakiyor. Soru şu: canlı zamanda, sürekli akan veri üzerinde tepe noktası nasıl buluruz? En son N tane noktayı hatırlamak mümkün olsun diyelim (ama verinin tamamı değil).

İlk akla gelebilecek basit çözüm bir genlik alt limiti ymin'i aşan tepe noktasını seçmek, bu nokta ardından xmin kadar bekleriz, ylim'den üstte yeni bir tepe nokta var ise, bunu da alırız, böyle devam ederiz. Fakat basit çözüm biraz aceleci aslında, belki xmin sonrası bulunan tepe noktasına çok yakın "daha iyi (yani daha yukarıda)" bir nokta daha var, tipik olarak hemen bir önceki noktanın üzerine atlamak istemiyoruz, daha yukarıdaki noktayı istiyoruz.

Bu durum için yine ilk akla gelen çözüm xmin'i arttırmak olabilir, fakat global bir parametreyle bu şekilde oynamak çoğunlukla iyi sonuç vermez, büyük ihtimalle analizin diğer yerlerinde başka yanlışlara yol açar.

Çözüm yaklaşımı değiştirip veriler için bir kayan pencere kullanmak. Tepe noktası "en son N öğenin içinde orta noktanın maksimum olması demek" diye bir tanım getirebiliriz. Kayan pencere  için CircularFifoQueue kullanılabilir, bu bir org.apache.commons sınıfı, bir tür sınırlandırılmış kuyruk (queue). N öğe tutsun diye tanimlarsak N öğe ardından verilen N+1'inci öğe için 1. öğe dışarı atılır. O zaman tepe noktası bulan kod şöyle olabilir

import java.io.*;
import java.util.*;
import java.util.Queue;
import org.apache.commons.collections4.queue.CircularFifoQueue;

public class TestPeak {

    public static class PeakFinder {
        double xmin = 0;
        double ymin = 0;
        int mid = 0;
        CircularFifoQueue idxs = null;
        CircularFifoQueue vals = null;
        // "null" olmayan degeri temsil etmek icin kullaniyoruz
        // elinde process'e verecek id'si olmayan cagrilar icin
        // kullanisli olabilir
        public static int DUMMY = 1; 
        public PeakFinder(int xmin, double ymin){
            this.xmin = xmin;
            this.ymin = ymin;
            idxs = new CircularFifoQueue(xmin);
            vals = new CircularFifoQueue(xmin);
            this.mid = (int)vals.size() / 2;
        }
        // process hem id hem deger aliyor, fakat id aslinda
        // tutulup oldugu gibi geri veriliyor, cagri yapana
        // yardimci olmasi icin bunu yapiyoruz, fonksiyonun ic
        // isleyisi icin onemli degil.
        public int process(double val, int idx) {
            vals.add(new Double(val));
            idxs.add(new Integer(idx));
            if (vals.size() < xmin) {
                return Integer.MIN_VALUE;
            }
            if (vals.get(mid) > this.ymin && vals.get(mid) == Collections.max(vals)) {
                return idxs.get(mid);
            }

           // Integer.MIN_VALUE null degeri yerine kullanildi, 
           // cunku int yerel tip, obje degil
            return Integer.MIN_VALUE;
        }
    }
        
    public static void main(String[] args) throws Exception{
        java.util.Random r = new java.util.Random();
        r.setSeed(0);
        double samples[] = new double[1024];
        for ( int n = 0; n < samples.length; n++ )
            {
                double noise = r.nextGaussian() * Math.sqrt(10);
                samples[n] = Math.sin( n * Math.PI / 18. ) + noise;
            }
            
        // uretilen veriyi dosyaya yazalim, ustteki Python kodu ayni veriyi
        // kullanacak.
        PrintWriter writer = new PrintWriter("out.txt");
        for (int i = 0; i < samples.length; i++) writer.println(samples[i]);
        writer.close();

        ArrayList res = new ArrayList();
        PeakFinder pf = new PeakFinder(6, 6.0);
        for ( int n = 0; n < samples.length; n++ ) {
            int idx = pf.process(samples[n], n);
            if (idx > Integer.MIN_VALUE) res.add(idx);
        }

        System.out.println(""+res );
    }
}

Sonuc,
Tekrarlamak gerekirse: tepe noktası "sağında ve solunda vadiler olan orta noktadır" tanımı üzerinden  sonucu aldık. Pencereyi sürekli kaydırıyoruz (daha doğrusu her yeni veriyi N tane sınırlı öğe içerebilen bir kuyruğa sürekli sondan ekliyoruz), ve bu sırada orta noktanın maksimum olup olmadığına bakıyoruz. 

JAMA - Java ile Matris Islemleri

Python ile prototip hatta servis tarafı sayısal işlem kodları rahatça yazılıyor; fakat bazen lineer cebir yapan kodları Java'ya çevirmek gerekebilir, mesela Android üzerinde işlemesi gereken kodlar. Pür Java ile yazılmış kullanışlı liner cebir kütüphanelerinden biri JAMA:

http://math.nist.gov/javanumerics/jama/#Package

Matrix Class Doc


İhtiyaç olan en önemli özellikler temel işlemler, toplama, çıkartma, ve matris arası çarpım, matris tersi (matrix inversion) ve devriği (transpose) Ek iyi olabilecek özellikler en az kareler (least squares) çözümü, SVD, LU, Cholesky ayrıştırması, vs. Bu özelliklerin hepsi Jama'da var.

Üstteki ilk bağlantıdan jar indirilir, altta kısa bir test,

import Jama.*;

public class Test {

    public static void main(String[] args) throws Exception{

        Matrix A = new Matrix(3, 3);

        A.set(1,0,10.0);
        A.set(1,1,10.0);
        A.set(1,2,10.0);

        System.out.println("A\n"+toString(A));

        System.out.println("A get\n"+A.get(1,1));

        double[][] bvals = {{1.,2.,3},{4.,5.,6.},{7.,8.,10.}};
        Matrix B = new Matrix(bvals);
        System.out.println("B\n"+toString(B));

        Matrix C = A.times(B);

        System.out.println("Carpim\n"+toString(C));

        Matrix D = A.plus(B);

        System.out.println("Toplam\n"+toString(D));

    }

    public static String toString(Matrix m) {
        String s = "";
        for (int i=0;i<m.getRowDimension(); i++){
            for (int j=0;j<m.getColumnDimension(); j++) s += "" + m.get(i,j) + " ";
            s += "\n";
        }
        return s;
    }
}

Sonuclar

A
0.0 0.0 0.0 
10.0 10.0 10.0 
0.0 0.0 0.0 

A get
10.0
B
1.0 2.0 3.0 
4.0 5.0 6.0 
7.0 8.0 10.0 

Carpim
0.0 0.0 0.0 
120.0 150.0 190.0 
0.0 0.0 0.0 

Toplam
1.0 2.0 3.0 
14.0 15.0 16.0 
7.0 8.0 10.0 

Bazı püf noktaları:

Android ortamı her ne kadar tam gömülü (embedded) bir ortam sayılmasa da, servis tarafı kodlanıyormuş gibi davranmaktan kaçınmak iyi olur, mesela telefon ivmeölçerinden gelen verileri hızlı bir şekilde işlemek gerekiyor, her işlem için bir matris çarpımı lazım, bu durumda her yeni veri iletimi için yeni matrisler yaratıp onları çarpmaya gerek yok, her seferinde hafızaya obje ekleme, onun çöp toplayıcısı tarafından silinmesi işleri ağırlaştırır. En iyisi sadece iki Matrix objesi yaratıp ama değerlerini her seferinde değiştirip çarpımı bu aynı objeler üzerinde yapmak.

Ek kodlar, mesela Jama kodlarında çapraz çarpım verilmemiş, bu benzeri ekler,

import Jama.Matrix;
import java.net.*;
import java.util.*;
import java.io.*;
import java.text.*;

public class Util {

    // Helper class hidden here to provide additional matrix methods
    // taken from https://github.com/thorstenwagner/ij-ellipsesplit
    public static class Matrix2  {
        private double[][] A;
        private int m, n;
        public Matrix2(int m, int n) {
            this.m = m;
            this.n = n;
            A = new double[m][n];
        }
        public Matrix2(double[][] A) {
            m = A.length;
            n = A[0].length;
            for (int i = 0; i < m; i++) {
                if (A[i].length != n) {
                    throw new IllegalArgumentException("All rows must have the same length.");
                }
            }
            this.A = A;
        }

        public Matrix2 crossProduct(Matrix2 B) {
            if (m != B.m || n != B.n) {
                throw new IllegalArgumentException("Matrix dimensions must agree");
            }
            Matrix2 X = new Matrix2(m, n);
            if (m == 1 && n == 3) {
                X.A[0][0] = A[0][1] * B.A[0][2] - A[0][2] * B.A[0][1];
                X.A[0][1] = A[0][2] * B.A[0][0] - A[0][0] * B.A[0][2];
                X.A[0][2] = A[0][0] * B.A[0][1] - A[0][1] * B.A[0][0];
            } else if (m == 3 && n == 1) {
                X.A[0][0] = A[1][0] * B.A[2][0] - A[2][0] * B.A[1][0];
                X.A[1][0] = A[2][0] * B.A[0][0] - A[0][0] * B.A[2][0];
                X.A[2][0] = A[0][0] * B.A[1][0] - A[1][0] * B.A[0][0];
            } else {
                throw new IllegalArgumentException(
                                                   "Matrix must be a 3-element row or column vector");
            }
            return X;
        }

        public double[][] getArray() {
            return A;
        }
    }

    public static String toString(Matrix m) {
        String s = "";
        for (int i=0;i<m.getRowDimension(); i++){
            for (int j=0;j<m.getColumnDimension(); j++){
                s += "" + m.get(i,j) + " ";
            }
            s += "\n";
        }
        return s;
    }

    // project u onto plane with normal n
    public static Matrix proj (double[] u, double[] n) {
        Matrix um = new Matrix(u, 3);
        Matrix un = new Matrix(n, 3);
        Matrix tmp1 = um.transpose().times(un);
        Matrix tmp2 = un.transpose().times(un);
        Matrix tmp3 = un.times(tmp1.get(0,0) / tmp2.get(0,0));
        Matrix res = um.minus(tmp3);
        return res;
    }

    public static Matrix cross(Matrix a, Matrix b) {
        Matrix2 aa = new Matrix2(a.getArray());
        Matrix2 bb = new Matrix2(b.getArray());
        Matrix2 res = aa.crossProduct(bb);
        return new Matrix(res.getArray());
    }
}

Friday, April 28, 2017

Derin Öğrenimle 3D Uzaklık Bilgisi, Görüntü Bölümleri Bulmak

DO ile dış dünyadaki objelerin derinliğini, tek kamera kullanarak ayrıca kameranın hareketini hesaplayabilen bir ağ yapısı surada paylaşılmış. Yazarlardan biri David Lowe: bu araştırmacı DO'dan önce de yapay görüş alanında ünlüydü, görüntülerden özellik çıkartmak (feature extraction) alanında mesela SIFT buluşu vardır... Ilginç bir hikaye: Lowe SIFT'i keşfeder ama bu keşif hakkındaki yazıyı hiçbir bilimsel yayın kabul etmez, o da SIFT'i patentler.

Neyse, genç bir diğer bilimci Zhou'nun başlattığı ve diğerlerinin katıldığı bu en son makalede ilginç bir diğer özellik DO'nun eğitiminin tamamen denetimsiz (unsupervised) olması, yani veri için etiket yok. DO hakkında yapılan eleştirilerden biri çok fazla etiketli veriye ihtiyacı olması, denetimsiz çalışmaması. Bu yaklaşım denetimsiz, ama aslında dolaylı olarak denetimli, çünkü etiketleri eğitim sürecinin kendisi üretiyor. Denetimli eğitim bilindiği gibi, mesela regresyon tekniğinde verili X için bir y'ye bir model uyduruyoruz, y'ler etiket ya da hedef verileri oluyorlar. DO'lar model uydurmayı çok boyutlu ve çok daha esnek şekilde yapabiliyorlar.

Denetimsizlik icin etiket üretimi perspektif geometri kullanarak yapılıyor. Perspektif geometride  bir imajdan diğerine geçerken mesela kamera duruşunun nasıl değiştiğini biliyorsak 1. imajı yamultup (warping) 2. imaja çevirebiliriz, ya da ters yönde ters yamultma ile geriye gidebiliriz. Bunu biliyoruz. Arkadaşlar iki ağ yapısı kurmuşlar, biri derinlik için diğeri duruş için; Bir video'daki tüm kareleri işlerken önceki, sonraki, mevcut imaj kullanıp duruş, sonraki imaj kullanılarak mevcut resimdeki derinlik tahmin edilmeye uğraşılıyor. Sonra her iki ağın çıktısı kullanılarak önceki imaja doğru ters yamultma yapıyorsunuz, eğer yamultma iyi olmadıysa önceki imaja uymayacaktır tabii ki ve bu "hata" bir gradyan inişle her iki ağ üzerinde düzeltme amaçlı kullanılabilir. Geriye doğru yamultma işleminin türevi alınabilir halde olması için özen gösterilmiş ki DO ile eğitim yapılabilsin. Fikir müthiş. Yani perspektif geometri üzerinden verinin kendisi dolaylı denetimli eğitim sağlamış oluyor. 

Egitim KITTI adli bir veri seti üzerinde yapılmış. KITTI saatlerce bir arabanın yolda giderken kamerasından kaydedilmiş görüntülerini içerir.

Kod şurada


DO için Tensorflow kullanılmış, kurmak için

sudo pip install tensorflow==1.1

Eğitilmiş modeli models/download_model.sh ile indirebiliyoruz. Biz modeli modeli test ettik, şu imaj üzerinde, 

from __future__ import division
import os, time
import numpy as np
import scipy.misc
import tensorflow as tf
from SfMLearner import SfMLearner
from utils import *
import matplotlib.pyplot as plt

mode = 'depth'
img_height=240; img_width=320
ckpt_file = 'models/model-145248'
I = scipy.misc.imread('ins.jpg')
I = scipy.misc.imresize(I, (img_height, img_width))

sfm = SfMLearner(batch_size=1,img_height=img_height, img_width=img_width)
sfm.setup_inference_graph(mode=mode)
saver = tf.train.Saver([var for var in tf.trainable_variables()])
with tf.Session() as sess:
    saver.restore(sess, ckpt_file)
    pred = sfm.inference(I[None,:,:,:], sess, mode=mode))
p = pred['depth'][0,:,:,0]
plt.imshow(normalize_depth_for_display(p))
plt.savefig('out2.png')

Sonuç

Daha aydınlık pikseller daha yakın demek. Ortadaki engel açık şekilde görülüyor. Metre olarak biz bazı seçilmiş pikselleri kontrol ettik, 

Çoğu piksel iyi, alt soldaki daha iyi olabilirdi. Muhakkak bu yaklaşımda ilerlemeler olacaktır, ayrıca zaten üstteki türden resimleri kullanarak eğitim yapılsa sonuçlar daha iyi olabilirdi. Yapılan az buz iş değil; tek resme bakılarak tüm piksellerin derinlik bilgisini bulmak! 

Peki birkaç resme bakarak (video mesela) o resimlerdeki tüm objeleri üç boyutta bulmak, takip etmek amacında neredeyiz? Bu bağlamda daha gidecek yol var. Şimdilik en iyi seçenek DO (ya da diger, mesela Felzenswalb) kullanarak iki boyutta görüntü bölmesi (segmentation) yapmak, sonra bu iki boyuttaki görüntü parçalarını 3D takibi yapacak filtrelere geçmek. Filtreler kalman ya da parçacık filtreleri olabilir. Takip etme işlemi de zor bir iş yapıyor, bir zorluk takip edilen objeler görüntüden çıkıyorlar, yeni objeler tekrar giriyorlar. Bir digeri imaj parçalarının doğru filtre ile eşleşmesi lazım, bu eşleşmeyi direk yapan yaklaşım var, olasılıksal yapan yaklaşım var (imaj parçası 'tüm' filtrelere verilir, ama her filtrenin bir hipoteze bağlı olma olasılığı vardır). Filtrelemede iyi bilinen bir diğer  numara aynı ölçümün (bu durumda 2D imaj parçası) iki farklı filtreye verilmesi, mesela bir filtre objenin sola doğru gitmesi, bir diğeri üzerimize doğru gelmesi. Hangi filtre / hipotezin artığı / hatası daha az ise, o baskın haldedir, ve onun hipotezi kabul edilir. Ama arka planda olan filtreye hala ölçüm geçilmeye devam edilir. Tüm bu işlemlerin pür DO yaklaşımı ile yapılması çok zor.

Pür 2D imaj bölmesinden bahsetmişken, bu alanda DO ile bir diğer ilerleme SegNet. 


SegNet anlamsal (semantic) bölme yapıyor, bir görüntüde yol, araba, direk gibi temel bölümleri bulup onları etiketliyor. 

Microsoft'un COCO veri setini kullanarak (denetimli) şekilde resimde DO ile obje bulma çok ilerledi. COCO verisi etiketli, bol veri var, bir imajdaki objenin nerede olduğunu Amazon'un Mekanik Türk servisi üzerinden gerçek insanlara etiket verdirerek kaydetmişler. Bu yer  bir "maske" üzerinden DO'ya veriliyor, ayrıca ham imaj verisi de sağlanıyor. Bu veriyi kullanan ağ yapısı mesela DeepMask var.

Yaklaşımlar

DO etrafında bir "yan sanayi" ya da "paylaşma kültürü" oluşmaya başladı. Zaten makalelerin neredeyse hepsi artık açık bir ortam olan arXiv'de yayınlanıyor (para ile makale servisi yapan şirketler üzerinden değil),  ve DO özelinde ağların kendisi paylaşılıp direk olduğu gibi kullanılıyor. Mesela obje bulma, görüntü işleme yapan DO'lar imajın tamamını sınıflama amaçlı hazırlanmış VGG-16 modelini baz alıyorlar. Ve sadece ağ yapısını değil, eğitilmiş ağın ağırlıklarını bile olduğu gibi kullananlar var. Bu önceden eğitilmiş (pre-trained) model kullanma tekniği. Sonra kendi istediği yeni birkaç katman ekleyebilir, ya da mevcut bir katmanı atıp yerine yenisini koyabilir, vs. Sonra eğitimi mevcut ağırlıkların olduğu yerden "devam ettiriyorlar", böylece mevcut modelden faydalanıyorlar.. Bu mantıklı aslında çünkü VGG-16 imaj sınıflaması için eğitilmiş, o zaman ağırlıklarının içinde imajı anlaması için gerekli bazı ayarlar oluşmuş olmalıdır. Takip eden araştırmacılar bu ayarlardan istifade ediyorlar.

Kapatmadan önce bir noktadan daha bahsedelim; DO'nun sadece denetimli olması bir kısıtlayıcı faktördü fakat bize göre eğer problem alanı hakkında temel matematik bilgisi devreye sokulabilirse denetimli problemler denetimsiz hale çevirilebilir. Mesela paylaşılan ilk makalede bu temel bilgi perspektif geometridir. Arkadaşlar kamera duruşu, yamultma, homografi, vs. gibi pek çok temel bilgi devreye sokarak sonuca ulaşmışlar. DO alaninda bazen bir beklenti "pür pikselleri vereyim her şeyi öğrensin" türünde olabiliyor. Fakat eğer işimizi kolaylaştıracaksa, "hipotez alanını daralatacaksa" eldeki pek çok diğer bilgiyi devreye sokabiliriz. Bilimin temeli modellemedir ne de olsa. Yazının başındaki makalenin yazarlarından Kendall surada benzer vurguyu yapmış.

EK:

Sehir ici etiketlenmis goruntuler icin Cityscapes veri seti ilginc.

Tuesday, March 28, 2017

OpenCV 3.0

OpenCV en son sürüm kurmak için

sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev 
sudo apt-get libavformat-dev libswscale-dev python-dev python-numpy libtbb2 
sudo apt-get install libtbb-dev libjpeg-dev libpng-dev libtiff-dev 
sudo apt-get install libjasper-dev libdc1394-22-dev

Sonra

https://github.com/opencv/opencv

adresinden kod indirilir, dizine gidip 

mkdir release
cd release/
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make -j2
sudo make install

işletilir.

OpenCV ile C++ kodu derlemek icin tipik ornek,

g++ -c -O3 -Wall `pkg-config --cflags opencv` -D LINUX -fPIC videostab.cpp
g++ -o videostab.exe videostab.o `pkg-config --libs opencv`

Bazi yararli olabilecek OpenCV kodlari surada.

Friday, March 24, 2017

Bulut Uzerinde Unix: GCloud

Bilgisayarı almak, taşımak, üzerinde işletim sistemi kurmak, sonra onu İnternet'e açmak için uğraşmak.. Bunlar saç yoldurucu olabilir. Amazon, Google gibi şirketler bu alanda uzun süredir servis sağlıyorlar. İnternet üzerinde isteğe göre, istenen hacimde, istenen işletim sistemini taşıyan bir makinayı yaratmak bulut servisleri ile çok kolay. Amazon EC2 sisteminden önce bahsetmiştik. Eğer Amazon'un servisi memnun etmiyorsa, Google seçilebilir. İlk hesap açımında $300 dolarlık hediye de veriyorlar - rekabet güzel şey! Amazon müşterilerini kendilerine çekmeye uğraşıyorlar.

Bu yazıda komut satırından SSH ile erişilebilecek servis tarzı bir makina yaratmayı işliyoruz.  Google bu servise "hesaplama motoru (compute engine -CE-)" diyor. Makina yaratmak için önce hesap açmak lazım. 

https://cloud.google.com

Kredi kartı, ya da banka bilgileri verildikten sonra (hediye miktarı tüketilmeden para alınmayacak tabii) giriş tamamlanır. Ardından Google servislerine komut satırı erişimi sağlayan SDK lazım,  

https://cloud.google.com/sdk/

Linux icin 

https://cloud.google.com/sdk/docs/quickstart-linux

Indirdikten sonra cd google-cloud-sdk, ve ./install.sh. 

Sonra gcloud init ile kurulum tamamlanır, sorulan sorulara evet deyin, sisteme girme (login) sorusuna evet deyince tarayıcıya aktarılıyorsunuz, google şifresi ile giriş yapınca bağlantı kuruluyor. 

Yeni makina yaratmak gcloud komut satırı ile olabilir, ama en rahatı başta GUI kullanmak. Bir proje yaratılıyor, sonra proje seçiliyor. Ana konsoldan "Go to compute engine dashboard" ile CE'ye gidiliyor. Burada


Benim daha önce başlattığım makina burada görülüyor. Yeni makina için "create instance" tıklanır, seçenekler hangi tür işletim sistemi (bizde Debian), makina donanımı (machine type), belleği, mikroişlemci sayısı, vs., makinanın hangi bölgede başlatılması istendiği (zone) seçiliyor. Bölge mesela makina ABD'de mi, Asya'da mı, Avrupa'da mı başlatılsın? Eğer müşteriye dönük bir uygalama servis edilecekse müşteriye yakın makinalar daha iyi olur, cevap hızının optimal olması için. 


Bu seçimler yapılırken sağ yanda tahmini aylık bedel gösteriliyor. Makina yaratıldıktan sonra iki üstteki resimde SSH seçeneği var, bu seçenekten "view gcloud command" ile bağlanmak için gereken gcloud komut satırı komutu gösteriliyor. Bunu kopyala, yapıştır ile yerel makinamizin Unix konsoluna verip, buluttaki makinamiza ilk girişi yapabiliriz. 

Makinaların ana diski kalıcıdır (persistent), yani makinayı kapatıp açınca disk üzerine yazılmış şeyler hatırlanır. Tabii sanal makina silinirse, dosyalar da silinecektir. 

CE makinalarına statik IP adresi atanması mümkün, yani bu makina üzerinde Web servisi (apache gibi) başlatılınca makina o IP üzerinden erişilebilir hale gelir. DNS ile isim eşlemesi yapılınca, Google bulutu üzerinden tam işlevli Web servisi sunmak mümkün olur. 

Monday, March 13, 2017

Baz Istasyonu ile Yer Bulma, Android

Bu bolume bazi ekler:

Altta baz istasyonlari hakkinda veri tabanindan bahsedildi. Telefonun o anda bagli oldugu baz istasyonun verisini Android'den almak ve kaydetmek icin surada bahsettigimiz olcum toplayici kodlara ekler yaptik. Artik cell.txt adli bir text dosyasinda o anda bagli olunan (servis veren) baz istasyonunun bilgileri yaziliyor. Mesela biraz once bir ornek topladik, bir satirda

262:02:15:13227508:

Bunlar sirasiyla mcc, mnc, lac, cid kodlari. Bu kodlari kullanarak opencellid verisine bakiyoruz, ve

UMTS,262,2,15,13227508,167,13.321302,52.502313,185,9,1,1458941010,1478909517,-101

satirini buluyoruz, yani enlem/boylam 52.502313,13.321302 imis (hakikaten de oradaydik). Bu en yakin baz istasyonunun yeri.

GPS ile algilayici fuzyonu (sensor fusion) yapilarak bu veri oldugumuz yeri daha iyi hesaplamak icin kullanilabilir. GPS bilindigi gibi duvarlardan yansima sonucu bazi sinyallerin yolunun uzamasi sebebiyle (multipath problem) bazen yanlis sonuclar verebiliyor.  GPS baglanti da kuramayabilir bazen, ya da  baglanma uzun surebilir. Tum bu sebeplerden dolayi baz istasyonlari yer bulmak icin takviye amacli kullanilabilir.

Tum dunyadaki baz istasyon kodlari ve yerlerini kayitli tutan bir acik yazilim projesi,

opencellid.org

Baz istasyonu kodu uzerinden bulunur, ve tabanda istasyonun enlem  / boylami da vardir, 3 veya daha fazla istasyon ile ucgenleme yapilabilir. Istasyon tabanini almak icin, once Database | request for API key secilir, email / sifre ile kayit olduktan sonra ile bir arayuz anahtari alinir; Sonra Database | Download Database ile ve API anahtari girilerek taban indirilir.

Tum taban oldukca buyuk, sıkıştırılmış hali bile 600 MB civari - eh normal, tum dunyadaki istasyonlar kayitlanmaya ugrasiliyor. Bu dosyayi kucultmek icin icinde oldugumuz sehir disindakileri filtreleyebiliriz. Mesela sadece istenen enlem / boylam yakininda olanlar icin alttaki kod, diyelim icinde 13 enlem 52 ile baslayan baz istasyonlarini alip ekrana basiyor (komut satirinda > ile cikti dosyarina yonlendirilir)

import re
with open(...) as f:
    for line in f:
        if re.search("13\.\d+,52\.\d+",line): print line.strip()


Not: Kaynaklarda telefonun etrafindaki birden fazla olabilecek baz istasyonunun bilgisini almak icin TelephonyManager.getNeighboringCellInfo cagrisindan bahsediliyor. Bu cagri "kizaga alinmis (deprecated)" bir cagri, artik onun yerine TelephonyManager.getAllCellInfo tavsiye ediliyor, API 17'den itibaren bu cagri Android API kod bazinda var. Fakat tum telefonlar bu cagriyi halen desteklemiyor, biz Android 6.0 (API 23) destekli olan bir telefonda denedik (ucuz bir marka) ve getAllCellInfo bos bir liste geri getirdi.