Idlimage — Zpracování dat STM v IDL
Petr Zimmermann, 1. ročník nmag. fyzika povrchů a
ionizovaných prostředí
Zdrojový soubor ke stažení včetně ukázkových souborů ve složce data — 3 MB
Ve Skupině tenkých vrstev (http://physics.mff.cuni.cz/kfpp/vrstvy/) používáme nekomerční řádkovací tunelový mikroskop (dále STM) pro zkoumání růstu kovových nanostruktur na površích křemíků. Výsledky se ukládají ve vlastním formátu souboru IMG, které se dále zpracovávají v IDL programu Idlimage [čti idlimáž].
Velikost souboru IMG se při rozlišení 512*512 pixelů standardně pohybuje okolo 2 MB. Chceme-li však navíc vytvořit mapu lokální hustoty stavů (LDOS), musíme použít synchronní detektor lock-in, čímž velikost souboru vzroste na 3 MB.
Soubor tvoří hlavičková data (datum, čas, rychlost skenování, různé konstanty apod.) a 3D pole typu integer typu 512*512*4 (případně *6 pokud byl použit lock-in). Za těmito daty případně následují voltampérové charakteristiky měřené v různých místech obrázku.
Význam jednotlivých datových vrstev je následující: V analogii s běžnou katodovou televizní obrazovkou hrot pomocí piezokeramických elektrod vykonává řádkovací pohyb nad vzorkem. Každý řádek hrot přečte dvakrát, a sice jednou při pohybu tam a podruhé při pohybu zpět, přičemž je možné nastavit, aby řídicí elektronika při pohybu zpět nastavila odlišné napětí.
Data se získávají v módu konstantního tunelového proudu, tedy tak, že se změnou napětí ovládá výška hrotu nad vzorkem a udržuje konstantní tunelový proud. První dvě datové vrstvy výše popisovaného pole potom obsahují rozdíl skutečného tunelového proudu od nastaveného pro směr vpřed a vzad (záleží na rychlosti zpětnovazební smyčky) a další dvě vrstvy poté obsahují informaci o výšce hrotu nad vzorkem, opět při pohybu tam a zpět.
Původní program nedokázal rozpoznat, jestli jsou
v souboru přítomna data z lockinu, a použít adekvátní šablonu pro
zpracování binárních souborů, takže pokud jsme zpracovávali soubory obsahující
data z lockinu, museli jsme přepsat šablonu a Idlimage sestavit znovu. Dalším
prohřeškem původní verze programu bylo to, že používal absolutní cesty
k souborům a byl zakomponován do rozsáhlé adresářové struktury přesahující
600 MB, takže nebyl snadno přenosný.
Program bohužel obsahoval některé chyby, které nečekaně vedly k jeho pádu. Pro příklad zde uveďme situaci, kdy si člověk při volbě souboru uvědomil, že by chtěl použít jinou funkci a dialogové okno pro volbu souboru zavře. Jelikož program tímto dostal prázdný název souboru, okamžitě byl ukončen chybovou hláškou. Tento problém byl vyřešen tak, že pomocí IF — THEN se zkontroluje, jestli název souboru není prázdný řetězec, a pokud je, pak pomocí GOTO program skočí před END procedury a tím pádem nic nevykoná.
PRO open_img, Event
...
soubor = DIALOG_PICKFILE(title='Select a file', filter='*.img)
IF (soubor eq '') THEN GOTO, label_stop
...
label_stop:
END
Chtěl bych podotknout, že toto by šlo lépe vyřešit i obráceným způsobem, viz následující kód.
PRO open_img, Event
...
soubor = DIALOG_PICKFILE(title='Select a file', filter='*.img)
IF (soubor NE '') THEN BEGIN
;--- VLASTNÍ TĚLO PROCEDURY ---;
ENDIF
END
V tomto oddílu vysvětlím některé způsoby, jakým bylo dosaženo úprav.
Absolutní cesty byly ve všech případech nahrazeny relativními následujícím způsobem. Funkce FILE_WHICH načte cestu k zadanému souboru. Dále byly vytvořeny složky dir_main pro kořenovou složku, dir_templates pro šablony na binární data a dir_data pro různé další případné soubory.
dir_main = FILE_WHICH("idlim.prj") ;nalezne hlavní
soubor idlim.prj a uloží jeho cestu do proměnné cur
cd, dir_main, current=dir_main ;do proměnné dir_templates uloží cestu
k adresáři s templates
dir_templates = dir_main+"\templates" ;do proměnné dir_templates
uloží cestu k adresáři s templates
dir_data = dir_main+"\data"
STM soubor se otevírá pomocí tlačítka Open IMG. Soubor při
otevírání spustí proceduru open_img, která v průběhu zavolá proceduru get_version,
kterou zjistí, jaká je verze zvoleného souboru a jestli soubor obsahuje data z lockinu.
Podle toho open_image soubor načte pomocí adekvátní šablony. Každá datová
vrstva se načte do dvou proměnných. Jedna je určena pro vizuální manipulace a
druhá zůstává zachována pro export profilu mezi dvěma body (vizuální úpravy
mění rozměry).
Idlimage byl rozšířen o paletu checkboxů. Po jejich zvolení se inicializuje okno a zobrazí v něm příslušná datová vrstva (Voltage BW je například mapa výšky hrotu nad povrchem), při zrušení zvolení se okno uzavře. Voltage BW je zároveň výchozí kanál, který se načítá pro manipulaci s daty.
Zvolený radiobutton (vpravo) naopak určuje, která datová vrstva je zvolena pro případné manipulace s daty. Pro manipulaci s aktivním kanálem se aktivní datová vrstva zobrazí ve speciálním okně, ve kterém probíhají úpravy.
Jelikož program byl rozšířen o možnost zobrazení
jednotlivých datových vrstev a manipulace s nimi, stalo se potřebným mít
možnost provést jednu úpravu všem datovým vrstvám zároveň. K tomuto slouží
ovládací prvek Affect All Channels. V každé proceduře, které se Affect All
Channels týká, se zavolá tento kód:
hbox_allc=widget_info(Event.top, FIND_BY_UNAME='WID_CHBOX_ALLC')
chbox_set = widget_info(chbox_allc, /button_set)
IF (chbox_set EQ 1) THEN BEGIN
...
ENDIF
Informace o tom, že byl používán lockin, je zapsána v hlavičkových datech. Jelikož stále pořád nevíme, který konkrétní bajt o tom rozhoduje, je jako přechodné řešení zvoleno zjištění na základě velikosti souboru, kdy se přeskočí veškerá hlavičková data. Je-li lockin přítomen, nastaví se globální proměnná lockin na hodnotu 1, jinak je nula.
velikost_souboru=N_ELEMENTS(READ_BINARY(file, DATA_START=134, DATA_TYPE=2))
IF (velikost_souboru GT 1200000) THEN lockin = 1 ELSE
lockin = 0
Je-li přítomen lockin, po otevření souboru procedurou Open_IMG se aktivují další ovládací prvky, které umožňují vytvořit mapu lokální hustoty stavů:
Aktivace těchto prvků se provádí pomocí následujícího
kódu:
IF LOCKIN EQ 0 THEN BEGIN
button = widget_info(Event.top, FIND_BY_UNAME="WID_BUTTON_13")
widget_control, button, sensitive = 0
...
ENDIF ELSE BEGIN
button = widget_info(Event.top, FIND_BY_UNAME="WID_BUTTON_13")
widget_control, button, sensitive = 1
...
ENDELSE
Někdy je důležité mít možnost zobrazit výškový profil mezi dvěma body v obrázku. V tom případě je nutné nejprve odečíst rovinu a vyrovnat řádky. Nejprve se v programu zvolí funkce Line Profile a myší se v aktivním okně určí počáteční a koncový bod, mezi nimiž chceme zobrazit výškový profil. (V případě proudových datových vrstev lze zobrazit i proudový profil, nicméně je nejprve nutné zjistit konstantu úměrnosti mezi daty v souboru a skutečným proudem. V čase tohoto programu ještě nebyla známa.) Níže uvádím okomentovaný kód, který se vykoná.
pro line_profile, Event
common data, bmp
common line, line, linex, liney, x_A, x_B
common scale, x_nm, sizex, step_xy, const_z
common curr, current_fw
common active_window, active_window ;proměnná, která obsahuje hodnotu datové vrstvy nastavené jako aktivní
common lineexport, scan ;proměnná scan, ve které jsou uloženy hodnoty
siz=size(bmp)
sizex=siz[1]
sizey=siz[2]
;**********načte zvolené polohy*********
wset, 0
cursor, x0, y0, /down, /normal ;/normal znamená, že se budou načítat souřadnice v rozmení O až 1
x1=long(fix(x0*sizex))
y1=long(fix(y0*sizey))
print, "x1,y1", x1,y1
x_A = [x1, y1]
cursor, x0, y0, /down, /normal
x2=long(fix(x0*sizex))
y2=long(fix(y0*sizey))
print, "x2,y2", x2,y2
x_B = [x2, y2]
points=fix(sqrt((x2-x1)^2+(y2-y1)^2)) ;vzdálenost vybraných bodů v pixlech
print, points
i_arr=indgen(points)
tempbmp=tvrd() ;načte obrázek v aktivním okně a vykreslí do něj čáru v místě, kudy vede linie
tempbmp[fix(x1+(x2-x1)*i_arr/points), fix(y1+(y2-y1)*i_arr/points)]=0
tv, tempbmp
liney= scan[fix(x1+(x2-x1)*i_arr/points), fix(y1+(y2-y1)*i_arr/points)]
liney= liney*const_z ;přepočítá hodnoty na správnou výšku
linex = i_arr*step_xy
;**********
print, fix(x1+(x2-x1)*i_arr/points), fix(y1+(y2-y1)*i_arr/points)
;print, "line", line
;line=tvrd(0,y,sizey-1,1)
;line=bmp(*,y)
;line=bmp(*,257)
window, 10, title = "Line Profile"
;print, line
IF ((active_window EQ 3) or (active_window EQ 4)) THEN BEGIN
plot, linex, liney, xtitle="[nm]", ytitle="z [nm]"
ENDIF ELSE BEGIN
plot, linex, liney, xtitle="[nm]", ytitle="arbitary units"
ENDELSE
line = MAKE_ARRAY(2,points, /double)
line[0,*] = linex
line[1,*] = liney
print, "aktivní okno", active_window
end
V následujících pár krocích bych chtěl demonstrovat práci se souborem, ve kterém jsou uložena data indiových řetízků na povrchu Si(100)—2×1.
Nejprve otevřeme libovolný soubor. Na paletě s datovými vrstvami si zvolíme například proudový a napěťový (výškový) obrázek.
Dále pak srovnáme řádky (Row Equalization). Srovnání řádků znamená, že se spočítá průměrná hodnota v jednom řádku a řádek se posune tak, aby průměru byla přidělena hodnota 128 — čili polovina rozsahu stupňů šedi (0—255). Průměr se zjistí funkcí moment, která vrací vektor, jehož 1. prvek je průměr zadaného souboru.
function row_eq, b2
b=b2
siz=size(b)
xsize=siz[1]
ysize=siz[2]
;help, b
;print, xsize, ysize
for i=0, ysize-1 do begin
; b[*,i]=(b[*,i]-min(b[*,i]))/(max(b[*,i])-min(b[*,i]))
result=moment(b[*,i])
b[*,i]=b[*,i]-result[0]+128
endfor
;print, b[*,100]
return, b
end
Dále je nutné odečíst rovinu (Plain Substraction), protože hrot nemusí vždy skenovat vzorek přesně v rovině kolmé k povrchu — může být mírně nakloněn, a proto se data proloží plochou prvního řádu (tedy rovinou) a ta se odečte. Proložení plochou n-tého řádu se dělá funkcí SFIT
data=data-SFIT(data,1, kx=koef)
Poslední důležitý krok je zvýraznění detailů získaného obrázku. K zobrazování dat se používá funkce tvscl, která zvolená data přeškáluje v rozmezí 0—255 a zobrazí je ve stupních šedi. Nicméně v každém souboru se nalezne pár hodnot — většinou chybových —, které negativně ovlivňují kontrast. Histogramové zvýraznění kontrastu (Histogram Equalization) těchto pár hodnot ořízne a znovu vykreslí.
Zájemce o kód odkazuju na zdrojový soubor, protože kód není příliš přehledný a skládá se z několika vlastních procedur a funkcí.
Nakonec je možné tyto výsledky přijmout a exportovat do JPEG, přičemž proměnná bmp je hodnota v okně nastaveném jako aktivní (okno má název IDL 0).
pro export_jpeg, Event
;common data, bmp
file = dialog_pickfile(/WRITE, FILTER = '*.jpg',PATH='c:')
IF (file EQ '') THEN GOTO, stop_export_jpeg
file = file+".jpg"
wset, 0
bmp = TVRD()
WRITE_JPEG, file, bmp
stop_export_jpeg:
END