Blag

Where's the script?

Althogh I wrote some automatically running R scripts, setting working directories always caused me a headache; unless I called Rscript from the right directory (which I coundn't do with cron), all script started with
setwd("folder/containing/the/script")
This was fine untill I had to share the code with other participants, as 1.: it wouldn't run on their computer, and 2.: it exposed the path on my computer, which I found disturbing. So, I came up with a solution.

The commandArgs function returns all the arguments of the running Rscript. Among them, there is the currently used .R file: "--file=running_script.R". I just had to get rid of the --file= part and give the result to dirname to get the answer to the original question. Here's the whole function as a gist:

Now I start every script with
setwd(get_script_dir())

Hol a szkript?

Habár írtam már pár automatikusan futó R szkriptet, mindig fejtörést okozott a munkakönyvtár beállítása. Hacsak nem eleve a szükséges mappában indult el az Rscript (márpedig a cron a $HOME-ban indítja őket), minden egyes kód a
setwd("kódot/tartalmazó/mappa")
paranccsal kezdődött. Ezzel a kénylemetlenséggel egészen addig együtt tudtam élni, amíg nem kellett megosztani a kódot; ezzel a módszerrel ugyanis amellett, hogy mások gépén nem működik, azt is megmutatja, hogy én hol tartom az adott projekt fájljait, amit nem tartok szerencsésnek.

Az Rscript programnak átadhatunk különböző paramétereket, ezek közül az egyik első a futtatandó szkript elérési útja. Az argumentumokat a commandArgs függvény visszaadja, amik közül ki lehet szedni a futtatott fájl címét - így azt már csak a dirname függvénynek kell odaadni, hogy megkapjam az eredeti kérdésre a választ. Az egészet becsomagoltam egy függvénybe:

Mostmár minden szkript első sora ugyan az:
setwd(get_script_dir())

SFTP R-ből, Ubuntun

Az Andego-nál csalásdetektálással is foglalkozunk, aminek az admin (és fejlesztői) feladatait nekem van szerencsém ellátni (ezzel el is büszkélkedtem a szeptember eleji satRday konferencián). A biztosító az adatait egy ftp-szerveren adja át nekünk, ahonnan a mi R script-ünk letölti az RCurl csomag segítségével, elmenti a megfelelő helyre, majd minden folyik a maga útján tovább. Ehhez kell egy, a hozzáférés részleteit tartalmazó link, amit a getUrl függvény fog használni:

getUrl("ftp://user:pwd@server_address:port/folder")

Ez nagyon szépen működött, egész addig, amíg egy szerverkarbantartás során nem szűnt meg az ftp kapcsolat lehetősége;

azóta csak sftp-n keresztül lehet elérni az adatokat. Én ennek nem voltam ellene, sőt, meg is nyugtatott kicsit, hogy egy fokkal biztonságosabban közlekednek az adatok a világhálón. Az ügyfélnek nem okozott gondot, így csak rajtam állt, hogy megoldom-e a problémát. E probléma pedig abban állt, hogy a korábban használt parancs módosítva sem tudott lefutni:

getUrl("sftp://user:pwd@server_address:port/folder")
Protocol sftp not supported or disabled in libcurl

Az Ubuntu farigcsálása

Így heves google-bújás kezdődött, hogy hogyan lehet a curl csomagban engedélyezni az sftp protokollt, de elsőre elég ijesztő eredménye volt ennek: Ahhoz, hogy a curl tudjon sftp-vel kapcsolódni, le kell tölteni a forrást, módosítani egy szabályokat tartalmazó fájlt, majd a forrásból felépíteni az egészet. Mivel működnie kellett, nekiláttam, hevesen reménykedve, hogy ezzel nem rontok el semmit, ami eddig működött (SPOILER: nem rontottam el semmit)
Többször nekifutottam, előbb ebből az askubuntu kérdésből kiindulva, de sajnos nem jártam sikerrel (a curl_xxx.deb fájl nem jött létre valamelyik hiba miatt a sok közül). Hozzá kell tenni a történethez, hogy a telepítéskor futó tesztek eléggé zabálják az időt, így lehet mellette mással foglalkozni, ha erre adja az ember a fejét.

Végül sok tutorial, howto és for dummies átpörgetése után egy 12.10-es ubuntuhoz adott leírás mentén vágtam újra neki a féligismertnek. Én 14.04-es ubuntuval játszottam ezt a játékot, így a verziószámok természetesen mások, illetve a libssh2-1-dev helyett elég volt a libssh2-1 csomagot letölteni.

Alább részletesen, magyarul a folyamat, amit a fenti link alapján végigjártam:

1) Legyen egy mappa amibe dolgozunk. Frissítsünk, aztán töltsük le a szükséges csomagokat:
mkdir /tmp/curl
cd /tmp/curl
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential debhelper libssh2-1
apt-get source curl
sudo apt-get build-dep curl

2) Mielőtt a következő adag kódra rátérnénk, érdemes egy pillantást vetni a curl-xxxx/debian/rules fájlra. Ha abban találunk olyat, hogy --without-libssh2, cseréljük ki --with-libssh2-re. A legfrisebb curl verzióban egyébként már semmi utalás nem volt a libssh2-re, én a biztonság kedvéért beleírtam a fájlba.

Ugyanebben a fájlban kell beállítani, hogy a gép ne végezze el az ilyenkor szokásos teszteket. Az SHLIBS_VERSION= sor elé kell beszúrni, hogy DO_TEST=no. Én ezt nem tettem meg, nem véletlen írták meg a teszteket.

3) Létrehozzuk a csomagfájlokat (ha nem így hívják, nem ez történik, e-mailben szabad panaszkodni. Erősen learning by doing alapon tanulom az Ubuntut.)
cd curl-xxxx
sudo dpkg-buildpackage

4) az összes létrejött .deb fájlt felinstalláltam (amíg nem kaptam "már telepítve" jelentésű üzenetet):
cd ..
dpkg -i curl_xxxx-1ubuntu*.deb
dpkg -i libcurl3_xxxx-1ubuntu*.deb
....

Ha most beírtam, hogy curl -V, a visszakapott protokollok között ott büszkélkedett az sftp \o/

R script farigcsálása

Nem volt vége a küzdelmeknek, mert át kellett írni a parancsokat is, amik természetesen teljesen máshogy működtek, mint az ftp-s verzióban:
A download.file "Unsupported URL scheme" hibát adott, a curlPerform-nak pedig nem sikerült megadnom a megfelelő get-es kifejezést, amivel leszedné a fájlokat. Végül az a Stackowerflow-n is megtalált megoldás nyert, hogy beolvasom a file-t R-be a getURL segítségével, majd kiírom a megfelelő helyre:

writeLines(
  getURL(paste0("sftp://user:pwd@server_address:port/folder/",
      file_to_download),
      userpwd="username:password",
      .encoding="UTF-8"),
  paste0("dest_file"))

Fontos a .encoding argumentum, enélkül nem sikerült semmiféle kódolásban megtalálnom az ékezetes betűket. Ha Windows-zal próbálkozik az ember, ISO-8859-1 a sztring amit a "latin1" helyett kell beírni.

A fájlokat természetesen vissza is kellett tenni a megfelelő helyre, ami szintén nem működött úgy, ahogy ftp esetén. Emiatt megpróbálkoztam az ftpUpload paranccsal, ami kellemes meglepetésként működött:
ftpUpload(local_file,
  paste0("sftp://user:pwd@server_address:port/folder/",
    path_to_upload))

Érdekes módon anélkül működik, hogy megadnám a jelszót és a felhasználónevet...

Ez a módszer viszont meghagyja az eredeti fájlokat, amiket így külön paranccsal kell letörölnöm. A DELETE ftp parancs nem működik, viszont a parancs amit keresek az rm.

curlPerform(url="sftp://user:pwd@server_address:port/",
  quote="rm folder/file_to_delete",
  userpwd="username:password")

Ezzel igazából minden komolyabb problémámat meg sikerült oldanom, a szkriptek pedig futnak tovább, és mindenki boldog.

Update:

Az egész folyamat után sikeresen működött minden, agészen a következő frissítésig. Ezt megakadályozandó az alábbi parancsot kell kiadni:
sudo apt-mark hold curl

useR! 1. nap

A jelenleg is napfényes Aalborgban zajlik az idei useR! konferencia.

Az első napon délelőtt és délután is négy-négy tutorial közül válogathattak a
résztvevők. Én (a többi magyar résztvevővel együtt) Csárdi Gábor Igraph-bemutatójára ültem be: a legfrissebb (1.0) verzóban már valósággal lubickolni lehet a különböző gráfmanipulálási módszerekben, szintaxisokban: a régiek (egyelőre) megmaradnak, viszont az egész bővült egy make_* függvénycsaláddal, ráadásul a make_ önmagában is megáll függvényként. Amióta utoljára elmélyültem a csomagban, jelentősen fejlődött az ábrázolás, lehet közösségeket is plottolni követlenül, nem kell a csúcsok attribútumait beállítani. Viszont lehet használni meglepően nagy mértékben: a csúcsok (vagy élek) attribútumaként is meg lehet adni a paramétereket (szín, alak, méret, bármi ami létezik). Hozzá lehet nyulni a gráf elemeihez már a "[" es "[[" operátorokkal is. Ami szintén egy nagy újítás számomra, hogy össze lehet hasonlítani a közösségeket a csomagban implementált függvényekkel, és nem kell külső csomagokból importálni a mérőeszközöket.

Délután az assertive és a testthat csomagokba kaptam egy kis bevezetőt: a két részre osztott gyakorlat első fele az előbbi, második az utóbbi csomagról szólt, hasonló felépítésben: a több mint egy órás szakaszok eleján egy bevezetőt mondott Richard J. Cotton, majd egy nagy adag házifeladaton kellett átrágni magunkat. Nem volt szó a testthat csomagnak a frissen elmentett függvényre való futtatásáról, amit Otti Levente mutatott az április BURN meetupon.

Este hétkor a helyi MüPában tartott a polgármester egy rövid beszédet, majd egy állófogadással ért véget a hivatalos program.

Szenvedés a hangokkal

Egy eszközt szeretnék készíteni, ami a felhasználóra reagálva generál hangot. Rájöttem, jogy ha a hullámformát kitalálom és megcsinálpm numpy-ban, a felhasználót a pygame-en keresztül tudom figyelni, a pygame.sndarray és a pygame.mixer modulok pedig előállítják és lejátszák a hangot. Nézzük az első felét.

Találtam egy jó példát arról, hogyan lehet szinusz-hullámot csinálni numpy-jal, ebből indultam ki. Definiáltam egy frekvenciát és egy mintaelemszámot, és megpróbáltam egy fűrészfogat készíteni, ami valahogy így néz ki:
[0, 0.25, 0.5, 0.75, 1, 0, 0.25, 0.5, 0.75, 1, ...]

A numply linspace függvényét használtam a fűrész egyetlen fogához, ami olyan hosszú, mint a minták számának egy frekvenciányi része:
fs = 44100
freq=440.
np.linspace(-1,1,fs/freq)

Ovasgatván a pygame documetációt arra is rájöttem, hogy mindennek a legelején a mixer modult kell inicializálni, és megnézni, milyen csatornát hozott létre:
import pygame
import pygame.mixer as mixer
import pygame.snarray as snd
#
mixer.init()
mixer.get_init()
# (22050, -16, 2)

Tehát két oszlopban, 16 bites számokból kell létrehoznom a huullámot.

Mivel egy fűrészhegy már megvan, kettőt kell belőle csinálnom. Erre a tile és a reshape függvény tökéletesnek tűnik: az egyetlen fogat megismétlem, majd a teljes tömböt félbevégom.
import numpy as np
fs = mixer.get_init()[0]
chs = mixer.get_init()[2]
tst_sound = np.tile(np.linspace(-1,1,fs/freq), chs).reshape(fs/freq, chs)

Már van egy nagyon rövid hangom, már csak meg kell ismételni freq-szer hogy egy másodpercig tartson. Bökkenő, hogy a tile függvény az oszlopok számát sokszorozza meg, ami most probléma, mégha érthető is. Ennek orvoslására találtam meg a transpose függvényt. Felcseréltem a sorokat és az oszlopokat, az új oszlopokat megsokszoroztam, majd visszacseréltem őket.
tst_second = np.transpose(np.tile(np.transpose(tst_sound), freq))
tst_snd = snd.make_sound(tst_second)

Ha a hang alakja megfelelő, a make_sound függvény elvileg nem ad hibát, nekem legalábbis amint megoldottam az oszlop-problémát, működött, s csak le kellett játszanom a moxer modullal:
tst_snd.play()

Fúúj. Nem ezt a hangot akartam. Nem véletlen mondja meg a mixer.get_init(), milyen típusú számokat vár. nekem ilyen típusúra kell konvertálnom a számokat:
new_type = "int"+str(abs(mixer.get_init()[1]))

A konvertáláshoz pedig kétezik egy astype metódus:
tst_snd = tst_snd.astype(new_type)

Javítás

Még mélyebbre ásva magamat a dokumentációban megkáttam, hogy a tile függvény több, mint egy dimenziót is elfogad, tehát sokkal egyszerűbben így néz ki a kód:
tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))

Ráadásul transzponálás helyett használhatom az axis argumentumát a repeat() függvénynek további egyszerűsítésként:
tst_second = np.repeat(tst_sound, frequency, 1)

Ezt midn össze tudjuk fűzni egy függvénnyé, ami a mintagyakoriságot és a csatornák számát közvetlenül a mixer modultót szerzi:
def create_snd(frequency=440., duration=.3):
    # mixer init props:
    fs = mixer.get_init()[0]
    new_type = "int"+str(abs(mixer.get_init()[1]))
    chs = mixer.get_init()[2]
    # one wave
    tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))
    # make in one second
    tst_second = np.repeat(tst_sound, frequency*duration, 1)
    # convert it to a sound and return it
    return(snd.make_sound(tst_second.astype(new_type)))

Ehhez azonban már rendesen be kell tölteni a pygame modult (nem tudom miért, mondjuk ha elolvasnám a leírását valószínűleg kiderülne :D ). A teljes folyamat az alábbi kódban található, az eredeti példából vett szinusz-hullám-gyártó függvénnyel:

def create_sin(frequency=440., duration=.3, amplitude=100):
    # mixer init props
    fs = mixer.get_init()[0]
    chs = mixer.get_init()[2]
    snd_type = "int"+str(abs(mixer.get_init()[1]))
    # time
    t = np.arange(0, duration, 1./fs)
    freq = np.array((frequency, frequency))
    sig = np.sin(2 * np.pi * freq * t.reshape(-1, 1))*amplitude
    return(snd.make_sound(sig.astype(snd_type)))
 
# program for changing notes
def int_snd_test():
    # program for changing notes
    #
    pg.init()
    pg.display.set_mode([50,50])
    mixer.pre_init(frequency=44100)
    mixer.init(frequency=44100, channels=1)
    #
    freq=440.
    done = False
    print("press w to increase, s to decrease frequency")
    #
    while not done:
        e = pg.event.wait()
        print("Waiting for user event")
        # create sound and play it:
        create_sin(frequency=freq, duration=1, amplitude=250).play()
        print("sound began to play...")
        # check user's events:
        if e.type == pg.KEYDOWN:
            if e.key == pg.K_ESCAPE:
                done = True
                pg.display.quit()
                break
            elif e.key == pg.K_w:
                freq += 100
                print("frequency up; ", freq)
            elif e.key == pg.K_s:
                freq -= 100
                print("frequency down;", freq)

Hmmm... minden szép és jó, de bizonyos frekvenciák esetén változik a kiadott hang magassága. Van bárkinek bármi ötlete? Nem találtam ilyet a Stack Overflow-n.

Ráadásul beleraktam egy időtartam és egy amplitúdó argumentumot, mert az eredeti hang halk.

Messing with sounds

I want to create a simple tool for generating sound, altered on user input. I figured out that I can generate wave with numpy, I can get user input with pygame and use pygame.sndarray and pygame.mixer to make the noise. Lets see part one, that

We are the noise generation!

I found a nice tutorial about generating sine wave with numpy, and I used that as a starting point. I defined a frequency and a sample rate, but instead of sine, I tried to make a simple saw, like this:
[0, 0.25, 0.5, 0.75, 1, 0, 0.25, 0.5, 0.75, 1, ...]

I used the linspace function from numpy to create one edge of the saw, which is as long as the frequencieth part of the sample:
fs = 44100
freq=440.
np.linspace(-1,1,fs/freq)

After reading the pygame documetation, I realized I should initialize the mixer module first, and check its properties:
import pygame
import pygame.mixer as mixer
import pygame.snarray as snd

mixer.init()
mixer.get_init()
# (22050, -16, 2)
So I needed to create an array with two coloumns. How?

First, I had to find a way to create two parallel saw edge. I achieved this with the tile and reshape funcion: a made two edges in line, thed cut it half.
import numpy as np
fs = mixer.get_init()[0]
chs = mixer.get_init()[2]
tst_sound = np.tile(np.linspace(-1,1,fs/freq), chs).reshape(fs/freq, chs)

I had already one short instance of sound, I only needed to repeat it freq times to make it last 1 sec. The problem is that the tile function repeats the number of coloumns, which is understandable, and made me found the transpose function. I swapped the rows and coloumns, repeated the new columns, and swapped them back.
tst_second = np.transpose(np.tile(np.transpose(tst_sound), freq))
tst_snd = snd.make_sound(tst_second)

If the shape of he sound is OK, the make_sound function should not return any problem. After I sorted out coloumn issue, it worked for me. Then I only needed to play it with the mixer module:
tst_snd.play()

Hell no! It's not the sound I want. It turns out, the mixer.get_init() returns three values with a purpose: it expects integers with the given numer of bits. So the type in which we want the array is:
new_type = "int"+str(abs(mixer.get_init()[1]))

To convert between types, there is the astype method:
tst_snd = tst_snd.astype(new_type)

Improvement

Getting more into documenttion, I found out that last argument of tile() accepted more than one dimension, so the code looks like this:
tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))

Also, instead of transposing, I can use the axis argument of repeat(), to make it much more simple:
tst_second = np.repeat(tst_sound, frequency, 1)

We can pack all this into a function which takes the sample frequency and the number of channels directly from the mixer module:
def create_snd(frequency=440., duration=.3):
    # mixer init props:
    fs = mixer.get_init()[0]
    new_type = "int"+str(abs(mixer.get_init()[1]))
    chs = mixer.get_init()[2]
    # one wave
    tst_sound = np.tile(np.linspace(-1.,1.,fs/frequency), (chs, 1))
    # make in one second
    tst_second = np.repeat(tst_sound, frequency*duration, 1)
    # convert it to a sound and return it
    return(snd.make_sound(tst_second.astype(new_type)))

It cannot be played without a proper initializaton os pygame (I don't know why, probably reading the docs would help :D), so here are two short functions for playing a sine wave and manipulate its frequency:

def create_sin(frequency=440., duration=.3, amplitude=100):
    # mixer init props
    fs = mixer.get_init()[0]
    chs = mixer.get_init()[2]
    snd_type = "int"+str(abs(mixer.get_init()[1]))
    # time
    t = np.arange(0, duration, 1./fs)
    freq = np.array((frequency, frequency))
    sig = np.sin(2 * np.pi * freq * t.reshape(-1, 1))*amplitude
    return(snd.make_sound(sig.astype(snd_type)))
 
# program for changing notes
def int_snd_test():
    # program for changing notes
    #
    pg.init()
    pg.display.set_mode([50,50])
    mixer.pre_init(frequency=44100)
    mixer.init(frequency=44100, channels=1)
    #
    freq=440.
    done = False
    print("press w to increase, s to decrease frequency")
    #
    while not done:
        e = pg.event.wait()
        print("Waiting for user event")
        # create sound and play it:
        create_sin(frequency=freq, duration=1, amplitude=250).play()
        print("sound began to play...")
        # check user's events:
        if e.type == pg.KEYDOWN:
            if e.key == pg.K_ESCAPE:
                done = True
                pg.display.quit()
                break
            elif e.key == pg.K_w:
                freq += 100
                print("frequency up; ", freq)
            elif e.key == pg.K_s:
                freq -= 100
                print("frequency down;", freq)

However, some of the waves consist of two different sounds. Anyone has any idea why? I did not found in ot stack overflow.

Note that I added a duration argument to the function to be able make it shorter than one sec and amplitude variable becaouse the original was too silent.

Folyamatos hangot kicsikarni

A mai napom annak áldozon, hogy úgy csikarjak ki a gépől pythonnal folyamatos hangot, hogy az közben változik. A változás képességét egy for ciklussal ellenőrzöm.

Pygame.midi

A pygame midi modulját kezdtem el használni elsőre a kinecthez, ezzel azonban nem sikerült megfelelő eredményt elérnem. A baj talán ott van, hogy a midi hangok eleje más, mint a későbbi részei, így ha újra és újra elkezdem lejátszani az adott hangot, akkor remegős lesz, mint az alábbi videón is látható:

PyAudio

Ahogy a pygame-nél, itt is a probléma a változás. Találtam egy szkriptet ami szép szinusz hullámot ad ki, viszont mindig újra elő kell készítenie a hangeszközt ehhez. Ennek következtében mindig eltelik egy kis idő amíg megszólal az új hang, nem lehet folyamatosan játszani vele.

Később rájöttem, hogy ha csak a hangok összerakása előtt nyitom meg a csatornát és utánuk csukom be, akkor működik, amit szeretnék, a hangok szünet nélkül követik egymást. Ami azonban még mindig nem jó, hogy a lehető legrövidebb intervallumként "1"-et lehet megadni, viszont ez hosszú ahhoz, hogy a folyamatosan frissülő Kinect-adatokra reagáljon. Bár lehet, hogy egész egyszerűen belerakom így.

Kipróbáltam.

Minden élőlény elrohant a közelből, mert hangos és kellemetlen volt. Nagyon.

A probléma az továbbra is az időintervallummal van: a program csak az után kezdi kiszámolni a következő hangot, ha az előző végig ment. Emiatt lassan, szakadozva változott csak.

Aztán most találtam egy pygame-es megoldást, ami használja a numpy-t.

PyGame v2

Ez egy alapvetően tök szép, és érthető megoldás, teljesen szimpatikus is. Külön előny, hogy nem játsza végig a hangot, hanem ha kell, abbahagyja a régit és elkezdi az újat, így tulajdonképp vezérelhető. Azonban (legnagyobb bánatomra) ez is kattog, kikapcsol-bekapcsol. Viszont elfogad nem egész időtartamot is, de ez sajnos nem segít nekem.A trükk valahol a sndarray.make_sound() környékén lehet elrejtve, de persze az is lehet, hogy egyszerűen a lejátszandó hang generálása ilyen "eszméletlenül lassú". Mára mindenesetre feladom, nincs több ötletem.

Teremin Kinectből

Működik a tereminem. Bár dallamot játszani még nem tudok rajta, a tudat, hogy hangot tudok kiadni a kezeim mozgatásával, felettébb megnyugtató, bár nem volt egyszerű.

Beüzemelés

Ahogy utána néztem a szükséges programoknak, eszközöknek, nyílt ki a világ ezen szeglete: egyrészt Kinectből van XBOX-os és Windows-os, mindkettőből készült már egy új, korszerűbb verzió, és természetesen mindegyikhez más-más illesztőprogram kell.

Miután megtaláltam a megfelelő verziójú Kinect SDK-t, feltettem egy 64 bites Win 8.1-re. Majd nagy örömömre megtaláltam elvileg mindent, ami ahhoz kell, hogy pythonnal nyúlhassak a hardverhez. Ehhez mindössze annyit kellett tenni, hogy telepíteni kell a
- megfelelő Kinect SDK-t,
- a pythont,
- a Python Tools for Visual Studiot (ami kéri a Visual Studio-t, szóval azt is),
- a pykinect python csomagot,
- erősen ajánlott a pygame csomag.

Én ezen a folyamaton legalább 6-szor átverekedtem magamat, de nem siekrült stabilan működésre bírnom a Kinectet. Pedig találtam, és fel is telepítettem egy, a célomhoz igen közel álló programot, ami még működött is, azonban nem váltotta be a hozzá fűzött reményeket. Mivel valami sosem volt kerek, újra és újra megnéztem a telepítendő programok listáját, és akkor szúrta ki hirtelen a szememet, hogy a PVTS-t 32bites rendszerekhez ajánlják.

Miután mindezt előadtam egy 32 bites Win7 telepítéssel is, letöltöttem a KinectDemos kódot, mondván, lássuk mi van már eleve benne a rendszerben. Megörültem: a kód viszonylag érthető, a program kirajzol egy választott kameraképet, majd ha kérkük, rárajzolja a képre a megtalált csontvázakat. Mi több, tud egy egyelőre nem túl hasznosnak tartott fícsört: mozgatni is tudja a kamerát fel-le, így adott esetben valamilyen szinten a kép közepén tarthatjuk a legtöbb embert, hiába mennek közel a készülékhez. A mozgató motor azonban kifejezetten lassú, nem fogja folyamatos mozgással követni minden mozdulatunkat, főleg amiatt, hogy mozgás után előről kezdi a tér feltérképezését.

Hekkelés

Abból indultam ki, hogy a "játék" demója a képre tudta rajzolni a csontvázat: ha valahogy egyszerűen meg tudom adni, hogy hangot adjon ki a gép a kód hatására, akkor a hangerő és hangmagasság adatait már kivehetem úgy a csontvázból, ahogy a rajzoló részleg teszi.

A pygame-nek van egy midi modulja, ami úgy tud hangot generálni, hogy csak a hang erejét (0-127) és a hang magasságát (0-127) kell megadni, meg a csatornát, amin játszani kell neki. A hangszert szintén egy egész szám adja meg, amit még korábban kell deklarálni, ha nem az alapértelmezett versenyzongora (0) hangját akarja hallani az ember.

Létrehoztam egy változót, amivel be- és kikapcsolhatom a hangot, majd elkezdtem megkeresni azt a szekciót, ami a kép frissítéséért felel. Miután meglett, elkezdhettem azon ügyködni, hogy megkeressem, hogyan számolja ki az egyes hajlatok koordinátáit a program. Ez viszonylag hamar megvolt: a skeleton_to_depth_image függvény számolja ki a pozíciót, ha megadja neki a csontváz kívánt részét, és a képernyő szélességét, magasságát. Ezt például egy bal csukló esetén a csontváz[JointId.WristLeft] tartalmazza, a képernyőt én esetemben 127*127-esre állítottam, nagyobb képre úgy sincs szükségem.

Ami ennél nagyobb kihívás volt, hogy ezt nem tudtam leellenőrizni: sehogy sem akart hangot adni. nagyjából másfél óra kódban turkálás után kezdett leesni, hogy a rajzoló végigszalad az objektumon, amit megkap a Kinect-től: a megtalált összes csontvázban egyesével megrajzolja az összes ízületet. én viszont azt szerettem volna, hogy ha folyamatosan egy csontvázat követ a rendszer - ehhez előbb ki kell találni, melyik csontvázat ismeri meg, merthogy mindig másikat azonosított. Az azonosítást végül nem a TrackingState állapot vizsgálatával csináltam, egyszerűen csak annyit vártam, hogy legyen értéke a csontváz bal csuklójának.

Mouse under control

In the last few weeks I was tinkering on my new laptop to make it more comfortable. It's not easy at all. I mostly passed solved the problem of the sleeping (the lenovo thinkpad does not have display after waking up, and it is idle until I switch it off. The solution is to disable USB3.0, after that the machine will wake up, althoug the lights will signal something else). Anyway, the next task was to create a right mouse button on the upper side of the touchpad.

The problem basically is that on this ThinkPad there is no physical mouse buttons next to the rubber pointer, instead they are part of the touchpad. Thus when I disable that, I don't have mouse buttons. But the default settings was that both sides of the upper part of the taouchapd works as the left mouse button. After some digging, I found a nice question with good comments and answer on askubuntu, which describes exactly my problem. The only difference was that the user here wats more: to disable mouse movements induced by the touchpad (if I get it right, it was not working, so it is still an open question). Summary? copy the code on the site to a file names anyithing which begins with "99-" and ends with ".conf", e.g. "99-makerightmousebutton.conf", and place it in the /usr/share/X11/xorg.conf.d/ directory.

Code is under the section, and make sure to check the linked site above as this site will not show indentation.

Section "InputClass"
Identifier "t440 top buttons"
MatchDriver "synaptics"
Option "SoftButtonAreas" "60% 0 0 0 40% 60% 0 0" #Emulate right and midle buttons
Option "Synaptics Area" "0 0 0 1" #disable moving but not buttons
EndSection

And if someone would like to get one step closer to disable touchpad movements, copy this exact file to /etc/X11/xorg.conf.d/.

Megzabolázni az egeret

Az utóbbi időben azon ügyködök, hogy a frissen birtokba vett laptopon használhatóra hangoljam a 14-es Ubuntut. Nem könnyű. Azon a problémán már kvázi sikeresen túllendültem, hogy elalvás után nem kapom vissza a képernyőt, csak egy teljes leállítás után. (megoldás: valamivel nem tökéletesen kompatibilis a lenovo thinkpad és az ubuntu, és ez aúgy jelenik meg, hogy az usb3.0 elérhetősége mellett nem tud felébredni. Ha kikapcsolod az usb3.0-t, akkot pedig fel tud ébredni.) Szóval jöhetett a a következő feladat: a trackpoint és a touchpad összehangolása.

A gond abból adódott alapvetően, hogy ezen a laptopon a radírhoz már nem tartoznak külön fizikai gombok, hanem a touchpad felső sávjának egyes részeit lehet akként használni. Azonban az Ubuntu erről nem tudott, így volt a gép szélénél (alul) rendesen egy jobb és egy bal egérgombom, a felső részen lévő mezők azonban mindkét oldalon balgombként működtek. Kis túrkálás után sikerült megtalálnom egy leírást az askubuntu-n, ami pontosan erről szól, annyi különbséggel, hogy ott még a touchpad által való mozgatást is meg akarta szüntetni a kérdező (amit, ha jól olvasok a sorok közt, végül nem sikerült tökéletesen). A lényeg, hogy a jobbgomb beállításához a linken megtalálható kódot kell a
/usr/share/X11/xorg.conf.d/ mappába tenni, és olyan nevet adni, ami úgy kezdődik hogy "99-" és úgy ér véget hogy ".conf", pl "99-jobbgombkeszito.conf"

A kód itt alább, ami fontos lehet, az a tabulátorok, az itt nem fog látszani, ajánlom a fenti linket tanulmányozásra.

Section "InputClass"
Identifier "tp only with clickpad buttons"
MatchDriver "synaptics"
Option "SoftButtonAreas" "60% 0 0 0 40% 60% 0 0" #Középső és jobbgomb emulálása
Option "AreaBottomEdge" "1" #mozgatás kikapcsolása, de a gombok meghagyása
EndSection

Aki pedig a touchpad mozgatásának kikapcsolását is el szeretné érni, az ugyanezt a fájlt tegye be a /etc/X11/xorg.config.d/ mappába másolnia.

Pages