Linjering av ett
mikro-CT-system
Detta examensarbete har utförts i samarbete med STH Handledare på STH: Massimiliano Colarieti-Tosti
Linjering av ett mikro-CT-system
Alignment of a micro-CT system
A l e x a n d e r L u n d a h l J a c o b T r o w a l d
Examensarbete inom medicinsk teknik Grundnivå, 15 hp Handledare på KTH: Anna Bjällmark Examinator: Lars-Gösta Hellström Skolan för teknik och hälsa TRITA-STH. EX 2015:053
Sammanfattning
P˚
a Skolan f¨or Teknik och H¨alsa (STH) vid KTH byggs en
mikro-CT och f¨or att systemet ska fungera korrekt m˚
aste det
linjari-seras. Syftet med detta examensarbete var att ta fram ett
pro-gram som, utifr˚
an bilder tagna med mikro-CT:n, kan ber¨akna alla
n¨odv¨andiga parametrar f¨or att systemet ska kunna linjariseras.
Under projektet har LabView-kod, skapad f¨or att linjera ett
CT-system, modifierats f¨or att vara kompatibel med den
mikro-CT som byggs p˚
a STH. Koden har i viss m˚
an skrivits om till det
licensbefriade och plattformsoberoende spr˚
aket Java. D˚
a
mikro-CT:n p˚
a STH ¨annu inte ¨ar funktionell har varken koden i LabView
eller Java kunnat valideras med denna. Framg˚
angsrika f¨ors¨ok har
ist¨allet genomf¨orts p˚
a bilder fr˚
an en annan mikro-CT samt p˚
a
si-mulerade bilder av en fantom i en mikro-CT.
Abstract
Inneh˚
all
1 Inledning 1 1.1 M˚al . . . 2 2 Bakgrund 3 2.1 Parameterar . . . 4 2.2 LabView . . . 4 2.3 Teori . . . 6 2.4 Grafiskt anv¨andargr¨anssnitt . . . 6 3 Metod 7 3.1 LabView . . . 7 3.1.1 Utbildning . . . 7 3.1.2 Andringar i labview-koden . . . .¨ 7 3.1.3 Validering av labview-koden . . . 73.1.4 Bilder och filformat . . . 7
3.2 Java . . . 8
3.2.1 Krav . . . 8
3.2.2 Klasser och metoder . . . 8
3.2.3 Dokumentation och tredjepartspaket . . . 9
3.2.4 Koordinater . . . 9 3.2.5 Ber¨akningar . . . 10 4 Resultat 11 4.1 LabView . . . 11 4.2 Java . . . 11 5 Diskussion 13 5.1 Begr¨ansingar . . . 13 5.2 LabView . . . 13 5.3 Val av spr˚ak . . . 13 5.4 Java . . . 14
5.5 Graphical User Interface . . . 14
INNEH˚ALL INNEH˚ALL
1 INLEDNING
1
Inledning
Datortomografi (eng. Computed Tomography, CT) ¨ar ett medicinskt bildgivande system som anv¨ander joniserande str˚alning f¨or att erh˚alla bilder av en patient eller objekt i tre dimensioner [1]. En mikro-CT ¨ar en mindre variant av en CT, som fr¨amst anv¨ands f¨or mindre objekt eller sm˚adjur. Detektor och k¨alla roterar kring objektet och detektorn tar emot data fr˚an k¨allan i form av detekterade fotoner. Denna data visar p˚a attenueringen av fotoner i objektet och sammanst¨aller denna till en bild. Detektorn kan dock vara fellinjerad i f¨orh˚allande till dess ideala position gentemot k¨allan och rotationscentrum (se Figur 1). Om detta bortses fr˚an, kan resultatet bli suddiga eller felaktiga bilder. F¨or att generera en s˚a verklighetsn¨ara och tydlig bild som m¨ojligt kr¨avs att felen i detektorns position ¨ar tillgodor¨aknade n¨ar bilden ska-pas. F¨or att detta ska vara m¨ojligt beh¨over d¨arf¨or detektorn med j¨amna mellanrum kalibreras. [2]
Figur 1: Fellinjering av detektor. RA = rotationsaxel, Rf = Avst˚and mellan k¨alla
och RA, R = Avst˚and mellan k¨alla och detektor. Bild inspirerad av: [3]
P˚a STH har ett byggprojekt av en mikro-CT n˚att slutfasen och ska snart s¨attas i bruk. Kalibreringsmjukvara f¨or detektorn finns tillhanda men har tv˚a problem:
• Koden ¨ar utformad f¨or en annan mikro-CT och beh¨over anpassas f¨or STHs mikro-CT.
1.1 M˚al 1 INLEDNING
Dessa tv˚a problem ligger till grunden f¨or examensarbetet. Koden beh¨over skrivas om f¨or att anpassas till den aktuella mikro-CT:n och i m˚an av tid skriva om ko-den fr˚an LabView till ett programmeringsspr˚ak med fri licens (ex. Java) och med funktionalitet f¨or andra operativsystem.
1.1 M˚al
Det prim¨ara m˚alet med projektet ¨ar att g¨ora den givna LabView-koden kompatibel med mikro-CT:n p˚a STH.
2 BAKGRUND
2
Bakgrund
F¨or att linjera detektorn kr¨avs bilder i 360 av ett fantomobjekt (se Figur 2). Fan-tomobjektet best˚ar av en plast med l˚ag attenuering med kulor av metall med h¨og attenuering. P˚a bilderna som framst¨alls ¨ar d¨arf¨or kontrastskillnaden mellan metall-kulorna och resterande delen av fantomen stor (se Figur 3). Vid en full rotation projiceras kulornas banor i en sammans¨attning av bilderna (se Figur 4), dessa banor ¨
ar grundl¨aggande i ber¨akningen av fellinjeringen. Med hj¨alp av fourierkoe↵ecienterna f¨or kulornas koordinater kan felplaceringen av detektorn relativt en teoretiskt ideal detektor best¨ammas. Detektorn som anv¨ands i STH’s mikro-CT ¨ar en Hamamatsu C7942CA-22 med en pixelstorlek p˚a 0.050mm och en uppl¨osning p˚a 2400⇥ 2400 pixlar. [4]
Figur 2: Fantom
Figur 3: CT-bild av fantom
2.1 Parameterar 2 BAKGRUND
2.1 Parameterar
F¨or att kunna kalibrera detektorn beh¨over programmet generera information om f¨oljande parametrar:
x Detektorns felplacering i x-led
y Detektorns felplacering i y-led
z Detektorns felplacering i z-led (Se Fi-gur 5)
Figur 5: Fellinjering i tre riktningar. (Inspire-rad av: [5])
(slant) Rotation kring z-axel
✓ (tilt) Rotation kring x-axeln
⌘ (skew) Rotation kring y-axeln (Se Fi-gur 6)
Figur 6: Fellinjering i tre vinklar. (Inspirerad av: [2])
Rf Avst˚and mellan detektorplanet och Area of rotation. (se Figur 1)
2.2 LabView
2.2 LabView 2 BAKGRUND
Figur 7: Exempel p˚a en del av en LabView-VI
2.3 Teori 2 BAKGRUND
Arizona STH
Detektorns pixelstorlek 0.048 mm 0.050 mm Bildstorlek (output) 2048⇥ 1024 2400⇥ 2400
Filformat (output) Raw 32 bit float Unsigned 16 bit int
Tabell 1: Relevanta skillnader f¨or projektet mellan STH’s och Arizonas mikro-CT
2.3 Teori
Projektionerna av kulornas bana ¨ar central f¨or ber¨akningarna av de s¨okta v¨ardena. F¨or att finna storleken p˚a felen i vinklar och riktningar anv¨ands relationen mellan dessa variabler och banornas fourierkomponenter. De s¨okta v¨ardena defineras enligt Von Smeakal m.fl. [6]: ⌘ (skew) = arctan ✓ c11 c01 ◆ ✓ (tilt) = arcsin ✓ ±B(Fk Fj) (Ek Ej)(C Fk) (Fk Fj)(A Ek) ◆ (slant) = arctan ✓ C F sin(✓)(A E)± B ◆ dx= sin (Asin✓± B) Ccos
R0y = cos (Asin✓± B) Csin dx= Acos✓
(1)
d¨ar R0y = R + dy, A, B, C, E och F v¨ardena kommer fr˚an hj¨alpvektorer som
de-finieras av punkternas fourierkoefficienter och skew. Notera att slant ¨ar odefinierad d˚a tilt ¨ar n¨ara eller lika med 0. Vid problem att ber¨akna tilt och slant ber¨aknas resterande v¨arden, givet att tilt= 0 och antaget att slant= 0, enligt f¨oljande:
R0y =⌥B dx= C
dz = A
(2)
De anv¨anda hj¨alpvektorerna defineras i Appendix B. F¨orst ber¨aknas c-v¨ardena d˚a de ligger till grund f¨or resterande hj¨alpvektorer, d¨arefter d-v¨arden. Med skew som ber¨aknas med c-v¨arden (se ekvation 1) samt c- och d-v¨arden ber¨aknas de slutliga A,B,C,E och F hj¨alpvektorerna.
2.4 Grafiskt anv¨andargr¨anssnitt
3 METOD
3
Metod
3.1 LabView
3.1.1 Utbildning
F¨or att f˚a b¨attre f¨orst˚aelse f¨or LabView tillkallades en programmerare fr˚an Na-tional Instruments som h¨oll en privat f¨orel¨asning om LabViews syntax samt gav handledning i det projekt som skulle genomf¨oras. Det konstaterades att till¨agg f¨or bildbehandling (IMAQ) kr¨avdes f¨or att kunna k¨ora koden och att detta till¨agg inte fungerar p˚a OSX. All given Labview kod k¨ors enbart p˚a Windowsmaskiner men g˚ar att l¨asa och tolka i OSX.
3.1.2 Andringar i labview-koden¨
Den fungerande delen av Labview-koden kan ej ber¨akna tilt och slant. Vid ber¨akning utan tilt kr¨avs en f¨or¨andring av pixelstorleken (RunFullCalculation.vi), bildstorleken (GetCentroidCoord.vi) samt filformatet (GetCentroidCoord.vi).
F¨or att f˚a en fungerande ber¨akning med tilt och slant kr¨avdes en f¨or¨andring av Cal-culateAllThetas.vi. Kopplingar mellan funktionerna hade brutits och beh¨ovdes bytas ut mot fungerande kopplingar, NumberOfBalls som visar p˚a antalet bollar beh¨ovde l¨aggas till och och kopplas till sub-VI:n CalculateTheta.vi och en output-array med resultaten beh¨ovde l¨aggas till. Fler f¨or¨andringar kr¨avs ocks˚a i runFullCalculationW-Tilt.vi d¨ar ett v¨arde kallat Rf som visar p˚a avst˚andet mellan objekt och detektor.
Vid en n¨armare analys av matematiken och LV-kodens hierarki (den ordning i vil-ken de olika VI’s k¨ors) visade sig flertalet av VI’s vara ¨overfl¨odiga och antalet kunde sk¨aras ner till 22 som var n¨odv¨andiga f¨or att kalibrering ska kunna genomf¨oras. Varje av dessa har noga analyserats och j¨amf¨orts med matematiken i det dokument som beskriver fysiken bakom kalibreringen [6] f¨or att finna vilka variabler som kan beh¨ova justeras f¨or att vara kompatibel med STH’s mikro-CT.
3.1.3 Validering av labview-koden
D˚a STH’s mikro-CT inte var klar kunde inte valideringen ske p˚a denna, utan fick ist¨allet testas p˚a bilder fr˚an en mikro-CT som befinner sig p˚a University of Arizona och p˚a simulerad data. Den simulerade datan skapades med f¨orbest¨amd fellinjering anv¨andes i den f¨or¨andrade labview-koden f¨or att fastst¨alla att de s¨okta v¨ardena ber¨aknades korrekt (Se Tabell 2, 3, 4). Den simulerade datan hade an uppl¨osning p˚a 512⇥ 512 pixlar och ett raw-filformat p˚a 8 bitar.
3.1.4 Bilder och filformat
D˚a filformaten mellan STH’s och Arizonas mikro-CT skiljer sig ˚at, g¨ors samtliga bilder fr˚an Arizonas CT om till bin¨ara filer. Detta gjordes i en kort matlabkod som ¨
3.2 Java 3 METOD
G). Matlab anv¨andes i detta fall d˚a java inte har n˚agot inbyggt st¨od f¨or formatet uint samt d˚a bytet av filformat bara beh¨ovdes g¨oras vid ett tillf¨alle.
3.2 Java
3.2.1 Krav
D˚a kalibrering av detektor sker s¨allan, st¨alls l˚aga krav p˚a programmets hastighet. H¨ogre krav st¨alls ist¨allet p˚a kodens precision inom bildbehandling och matematiska korrekthet s˚a de framst¨allda bilderna blir s˚a riktiga som m¨ojligt.
Anv¨andaren ska kunna, med en knapp, v¨alja f¨orsta bild-filen. Om bilden som v¨aljs inte ¨ar i korrekt format (.bin) ska programmet s¨aga ifr˚an. N¨asta steg ¨ar att v¨alja en ROI. Efter detta ska anv¨andaren bara beh¨ova trycka ”Run Calculation” och erh˚alla de n¨odv¨andiga kalibreringsv¨ardena och en bild av kulornas banor.
Bilderna som anv¨andes f¨or att testa koden var fr˚an Arizonas mikro-CT d˚a STH’s CT inte var f¨ardigst¨alld.
3.2.2 Klasser och metoder
Vid ¨overs¨attning anv¨andes Java med designm¨onstret Model-View-Controller. All kod skrevs i programmet Eclipse. F¨oljande klasser skapades:
Controller controller-klass.
CalculateValues Model-klass f¨or samtliga matematiska utr¨akningar.
ImageProcessing Model-klass f¨or processering av bilder och koordinatutr¨akning GUI View-klass f¨or interface
Trajectories Model-klass f¨or skapande av ban-bild
ROI Model-klass f¨or best¨amning av vilka delar av bilden som ska analyseras (Region of interest).
¨
Aven flertalet mindre klasser s˚asom main-klass och observer-klasser beh¨ovdes i pro-jektet.
3.2 Java 3 METOD
3.2.3 Dokumentation och tredjepartspaket
Vid hj¨alp med kodande anv¨andes Javas egen dokumentation[7], hemsidan Stackoverflow[8], D. Flanagan, Java in a nutshell: a desktop quick reference[9] och K. Arnold m.fl. The Java Programming Language, 4th edition[10].
D˚a bilderna fr˚an mikro-CT:n ¨ar i ett unsigned int-format som Java inte kan l¨asa, m˚aste tredjepartspaketet guava-18.0.jar[11] installeras i Eclipse f¨or att programmet ska vara funtionellt.
3.2.4 Koordinater
Till en b¨orjan ber¨aknades varje punkts koordinat f¨or varje bild. Detta gjordes genom att v¨alja ett region of interest (ROI) med enbart det antal kulor som ¨onskas m¨ata p˚a, on¨odig information utanf¨or regionen skalas ocks˚a bort f¨or att e↵ektivisera koden. Kontrastjusteringar genomf¨ors och bilden unders¨oks genom att g˚a igenom pixel f¨or pixel tills en skillnad i kontrast hittas. Programmet finner kulorna och r¨aknar ut deras centrum. Samtliga koordinater f¨or alla kulor i alla bilder samlas i en array. Koordinaterna i Java definieras dock inte p˚a samma vis som i den matematiska teorin. Enligt teorin ska origo vara placerat i mitten av detektorplanet, i Java ¨ar det definierat som det ¨ovre v¨ansta h¨ornet och m˚aste d¨arf¨or flyttas (se Figur 8). Koordinaterna skiftas enligt:
x = x0 xmax 2 z = zmax 2 z0 (3)
d¨ar xmax, zmax ¨ar de maximala v¨ardena f¨or x och z innan skiftningen och x0, z0 ¨ar
de ursprungliga koordinaterna. Denna skiftning g¨ors f¨or koordinaterna till samtliga kulor i alla bilder innan ber¨akningar kan ske.
3.2 Java 3 METOD
3.2.5 Ber¨akningar
Programets ber¨akningar av s¨oka parametrar efter att koordinaterna har skiftats g¨ors i f¨oljande ordning:
Fourierkoefficienter Baserade p˚a koordinaterna d-v¨arden baserade p˚a fourierkoefficienterna
c-v¨arden baserade p˚a fourierkoefficienterna och d-v¨ardena skew baserad p˚a c-v¨arden
A,B,C,E,F Baserade p˚a d-v¨arden, c-v¨arden och skew
Resterande s¨okta parametrar Baserade p˚a A,B,C,E och F
4 RESULTAT
4
Resultat
4.1 LabView
Samtliga parametrar som beh¨over justeras f¨or att kalibrera mikro-CT’s detektor tros vara identifierade. Samtliga on¨odiga VI’s har tagits bort och de resterande har validerats med den data som erh˚allits fr˚an University of Arizona. Den modifierade koden har ¨aven validerats p˚a bilder som skapats med hj¨alp av simuleringar av en mikro-CT. Med simulerad data har f¨oljande v¨arden uppm¨atts:
Tabell 2: Ber¨akningar p˚a simulerad data 1 Uppm¨att v¨arde Faktiskt v¨arde di↵erens
skew 1 1 0
dz 0.76 mm 0.81 mm 0.05 mm dx -1.31 mm -1.31 mm 0 mm
Tabell 3: Ber¨akningar p˚a simulerad data 2 Uppm¨att v¨arde Faktiskt v¨arde di↵erens
slant -1.25 1.25 2.5
dz 0.82 mm 0.81 mm 0.01 mm dx 1.30 mm 1.31 mm 0.01 mm
Tabell 4: Ber¨akningar p˚a simulerad data 3 Uppm¨att v¨arde Faktiskt v¨arde di↵erens
skew 0.77 0.75 0.02
tilt -1.5 1.25 2.75
slant -1 1 2
dz 0.83 mm 0.81 mm 0.05 mm dx 1.30 mm 1.31 mm 0.01 mm
En ytterligare m¨atning gjordes utan resultat d˚a kulorna i simuleringen ej var tillr¨ackligt synliga.
4.2 Java
4.2 Java 4 RESULTAT
s.1I detta ¨ar inte ber¨akningar av tilt, slant och skew inr¨aknat. Sammanlagt inneh˚aller programmet 3300 rader kod och d˚a saknas fortfarande sammankoppling mellan den matematiska delen av koden med den resterande delen.
Den framst¨allda bilden av kulornas banor ser korrekt ut, med undantag f¨or den plats i banan d¨ar f¨orsta och sista kulan m¨ots, d¨ar det ser ut att ha blivit n˚agon sorts f¨orskjutning (se Figur 9). Samma f¨orskjutning kan dock ses i LV-koden.
Figur 9: F¨orskjutning mellan kulorna i f¨orsta och sista projektionen
5 DISKUSSION
5
Diskussion
5.1 Begr¨ansingar
I programmen har bara tv˚a typer av bilder anv¨ants. Dessa bilder, fr˚an Arizona Uni-versity och de simulerade bilderna, inneh˚aller sju kulor och har i stort sett samma kontrastskillnad mellan fantomens metallkulor, platsh¨olje och omliggande luft. Det ¨
ar sv˚art att avg¨ora hur programmet skulle fungera med bilder d¨ar kontrastskillnaden ¨
ar mindre. Problem kan d˚a uppst˚a med att hitta kulorna och d¨armed deras koordi-nater. Saknas f¨or m˚anga kulor i ber¨akningar av deras banor kommer felaktiga v¨arden erh˚allas. Det ¨ar sv˚art att s¨aga om koden fungerar med STH’s mikro-CT d˚a dessa bilder kan skilja sig markant fr˚an de som anv¨ants hittills.
5.2 LabView
Den modifierade LabView-koden har enbart validerats med bilder fr˚an mikro-CT:n fr˚an Arizona och simulerad data. F¨or att kunna s¨akerst¨alla att teorierna om vilka variabler som b¨or justeras ¨ar korrekta, kr¨avs att bilder erh˚alls fr˚an den mikro-CT programmet ¨ar byggt f¨or. Detsamma g¨aller f¨or vilka v¨arden vissa variabler b¨or anta, d˚a bara variablerna sj¨alva kan avg¨oras utan tester, inte deras v¨arden. Resultaten fr˚an simuleringarna ger en negativ tilt och slant, en teori ¨ar att det beror p˚a att rummet ¨ar definierat annorlunda i simuleringen.
5.3 Val av spr˚ak
Vid spr˚akval f¨or ¨overs¨attning avgjorde f¨oljande punkter: • Licensfritt
• Stabilitet
• Matematisk funktionalitet • Kan skapa exekverbara filer • Platformsoberoende
• Bra debugging
Java har funnits i ¨over 20 ˚ar och erbjuder d¨arf¨or stabilitet tillsammans med ett stort bibliotek av funktioner f¨or bildbehandling och matematik. Med programmet Eclipse kan enkelt exekverbara filer skapas och kommer med bra debugging-m¨ojligheter. Java har ¨aven m¨ojligheten att k¨oras p˚a de flesta plattformer, s˚asom Windows, OSX och Linux [12][9].
5.4 Java 5 DISKUSSION
inte accepterar det filformat bilderna fr˚an mikro-CT’n ¨ar i. Program som Matlab, Python eller LabView har fler inbyggda funktioner f¨or att l¨asa bin¨ara filformat och f¨ors¨ok att skriva kod i Matlab som kan l¨asa in bilderna var okomplicerat. MatLab och Pythons enkla syntax, inbyggda matematiska funktioner och st¨od f¨or de flesta filformat pekar p˚a att de kan vara b¨attre spr˚ak ¨an Java att skriva denna sorts pro-gram i. I slut¨andan kunde dock tredjeparts-paket (JAR) inkluderas i koden f¨or att l¨osa filformatsproblemen i Java [11].
5.4 Java
F¨orskjutningen mellan banans f¨orsta och sista boll kan bero av flera saker. En f¨orklaring kan vara att koden inneh˚aller en bugg som f¨orskjuter koordinaterna n˚agot i y-led f¨or varje bild. D˚a denna f¨orskjutning ¨aven g˚ar att se i den givna LabView-koden ¨ar en rimligare f¨orklaring att n˚agot skett under CT-scanningen. Exempelvis kan vibrationer under processen f¨orflyttat fantomen vilket skulle ge upphov till ett s˚adant resultat.
Det har visat sig att den ¨oversatta matematiska koden inte ¨ar fullt kompatibel med resten av koden, d˚a LabView behandlar bilderna och koordinaterna p˚a ett annat s¨att ¨
an det skrivna Java-programmet. Denna kommer till viss del beh¨ova skrivas om f¨or att kunna hantera bildbehandlingens input.
Det kan ocks˚a konstateras att skriftliga programmeringsspr˚ak ger st¨orre friheter ¨an grafiska d˚a utvecklaren ges st¨orre m¨ojlighet att manipulera detaljer i koden. Detta gav exempelvis m¨ojligheten att l˚ata programmet sj¨alv r¨akna ut flera parametrar i bilderna ist¨allet f¨or att anv¨andaren manuellt m˚aste skriva in dem och riskera fel-aktiga v¨arden. Ett exempel p˚a detta ¨ar att LV-koden kr¨aver en input av antalet projektioner och kulor i respektive projektion, medan den ¨oversatta Java-koden kan r¨akna ut detta p˚a egen hand.
5.5 Graphical User Interface
Gr¨anssnittet f¨or LV-koden ¨ar r¨origt och sv˚art att anv¨anda utan att f¨orst f¨orst˚a den bakomliggande koden. En av anledningarna till detta ¨ar att programmet har m˚anga parametrar som anv¨andaren sj¨alv m˚aste st¨alla in, samt att interfacet in-neh˚aller m˚anga detaljer och v¨arden som ¨ar irrelevanta f¨or anv¨andaren.
D˚a kalibrering beh¨over ske med j¨amna mellanrum ¨ar det f¨ordelaktigt om anv¨andaren p˚a egen hand kan sk¨ota s˚a stora delar av kalibreringen som m¨ojligt, utan att beh¨ova ta in en extern part.
5.6 Framtiden
5.6 Framtiden 5 DISKUSSION
Java-koden inneh˚aller ¨aven ett antal buggar som det inte fanns tid f¨or att ta bort. Exempel p˚a dessa ¨ar:
• Koden g˚ar inte att exekvera om bilden inte ¨ar i ”liggande” l¨age och inte har dimensionerna 2048*1024 pixlar. Detta m˚aste d˚a korrigeras i koden. D˚a filerna ¨
ar i formatet .bin, som enbart best˚ar av en array, ¨ar det en om¨ojlighet att l˚ata programmet sj¨alv ber¨akna storleken p˚a bilderna. F¨or att l¨osa detta beh¨ovs en ruta d¨ar anv¨andaren sj¨alv f˚ar skriva in dimensionerna.
• F¨or varje g˚ang Region of interest v¨aljs sparas en vit ruta i bilden ¨aven om anv¨andaren inte ¨ar n¨ojd och ¨onskar g¨ora en ny (se Figur 10). Detta inneb¨ar att f¨or varje f¨ors¨ok blir det fler rutor och d¨armed sv˚arare och sv˚arare att skapa en bra ROI. En l¨osning ¨ar att placera en osynlig ”JFrame”¨over bilden d¨ar markeringen kan ritas och suddas ut utan att p˚averka den underliggande bilden.
Figur 10: Val av Region of interest. Den vita rutan har anv¨andaren ritat ut. Allt utanf¨or rutan behandlar inte programmet.
Det finns ¨aven andra m¨ojligheter att f¨orb¨attra programmet. Exempelvis kan koden beh¨ova e↵ektiviseras d˚a den allokerar mycket av datorns minne, vilket kan leda till att programmet kraschar om det k¨ors p˚a en ¨aldre dator. Det kan ¨aven kontrolleras huruvida kulorna p˚a vissa bilder f¨orsvinner ur bild eller korsar varandras banor och i dessa fall ta bort dessa ur ber¨akningarna. Detta skulle inneb¨ara att valet av ROI inte l¨angre ¨ar n¨odv¨andigt och att anv¨andaren enbart beh¨over v¨alja .bin-fil och trycka p˚a en knapp f¨or att f˚a resultaten.
5.6 Framtiden 5 DISKUSSION
6 SLUTSATS
6
Slutsats
Modifiering av Labview-koden visade sig vara r¨attningar i matematiken f¨or ber¨akning av slant och tilt samt ¨andringar f¨or bildernas filformat och pixel- till millimeterra-tion. Dock ¨ar projektet, som det ser ut nu, inte f¨ardigst¨allt. LabView-koden m˚aste valideras med STH’s mikro-CT och kan komma beh¨ova ytterliggare modifieringar efter f¨ors¨ok. D˚a den givna datan fr˚an University of Arizona samt den simulerade datan g˚ar att k¨ora i LabView med goda resultat, antyder det att inga fler stora ¨
andringar ¨ar n¨odv¨andiga f¨or att uppn˚a m˚alet.
Den hittills skrivna Java-koden fungerar n¨ast intill optimalt, med undantag f¨or ett par buggar. De matematiska delarna ¨ar ej kopplade till varandra men fungerar v¨al separat.
Att anv¨anda ett annat spr˚ak f¨or ¨overs¨attning med fler inbyggda funktioner f¨or detta ¨
7
Referenser
[1] D. J. Brenner and E. J. Hall, “Computed Tomography — An Increasing Source of Radiation Exposure,” New England Journal of Medicine, vol. 357, no. 22, p. 2277, Nov. 2007, accessed: 2015-05-28. [Online]. Available: http://www.nejm.org/doi/abs/10.1056/NEJMra072149
[2] L. von Smekal, M. Kachelriess, E. Stepina, and W. A. Kalender, “Geometric mi-salignment and calibration in cone-beam tomography,” Medical Physics, vol. 31, no. 12, p. 3246, Dec. 2004.
[3] J. W. Moore, “Adaptive X-ray Computed Tomography,” The University of Ari-zone, p. 45, 2011.
[4] “Flat panel sensor C7942ca-22,” accessed: 2015-05-21. [Online]. Avai-lable: http://www.hamamatsu.com/jp/en/product/alpha/F/4158/C7942CA-22/index.html
[5] L. von Smekal, M. Kachelriess, E. Stepina, and W. A. Kalender, “Geometric mi-salignment and calibration in cone-beam tomography,” Medical Physics, vol. 31, no. 12, p. 3244, Dec. 2004.
[6] ——, “Geometric misalignment and calibration in cone-beam tomography,” Me-dical Physics, vol. 31, no. 12, pp. 3242–3266, Dec. 2004.
[7] O. Corporation, “JavaTM Platform, Standard Edition 7
API Specification,” accessed: 2015-04-20. [Online]. Available: http://docs.oracle.com/javase/7/docs/api/
[8] stack exchange inc, “Stackoverflow,” accessed: 2015-04 till 2015-06. [Online]. Available: www.stackoverflow.com
[9] D. Flanagan, Java in a nutshell: a desktop quick reference, 3rd ed., ser. The Java series (O’Reilly). Beijing ;Cambridge: O’Reilly, 1999, kap. 1.2 - Key Benefits of Java, Kap. 8 - Java Development Tools (The Java Debugger).
[10] K. Arnold, J. Gosling, and D. Holmes, The Java programming language, 4th ed. Upper Saddle River, NJ: Addison-Wesley, 2006.
[11] Hamamatsu, “guava-libraries google, inc,” accessed: 2015-04-15. [Online]. Available: https://code.google.com/p/guava-libraries/
Appendix
A Banbild
Bild av kulornas banor (Trajectory)
B Teori
Enligt Von Smekal m.fl. [6] kr¨avdes bara den diskreta fouriertransformen av k 3 givet av: Uk= 2 N N X n=1 uncos(kan), k = 0, ..., N/2 e Uk = 2 N N X n=1 unsin(kan), k = 1, ..., N/2 1 (4)
d¨ar Uk ¨ar den reella fourierkoefficienten, eUk ¨ar den komplexa fourierkoefficienten, N
¨
ar antalet projektioner, an¨ar punktens position p˚a cirkeln och un¨ar x-koordinaterna.
Samma ber¨akning skedde f¨or y-koordinaterna och betecknades Vk f¨or den reella
calculateValues.java 1public class calculateValues {
2 private int sense = 1; 3
4 public calculateValues(int[][][] in, int x, int y) { 5 fullCalculation(in, x, y);
6 }
7
8 publicint[][][] shiftCoords(int[][][] in, int xSize, int ySize) { 9 int[][][] temp = new int[in.length][in[0].length][in[0][0].length]; 10 for (int i = 0; i < temp.length; i++) {
11 for (int j = 0; j < temp[0][0].length; j++) { 12 temp[i][1][j] = (ySize / 2) - in[i][1][j]; 13 temp[i][0][j] = in[i][0][j] - (xSize / 2); 14 } 15 } 16 returntemp; 17 } 18
19 publicdouble[] getSkew(double[][] cVals, int ballsize) { 20 double[] skew = new double[ballsize];
21 for (int i = 0; i < ballsize; i++) {
22 skew[i] = Math.atan(-cVals[3][i] / cVals[1][i]);
23 }
24 returnskew; 25 }
26
27 publicvoid fullCalculation(int[][][] in, int xSize, int ySize) { 28 int[][][] shifted = shiftCoords(in, xSize, ySize);
29 double[][] uComplexArray = getComplexFourierCoeffs(in[0][0].length, 30 shifted, in.length, 0);
31 double[][] uRealArray = getRealFourierCoeffs(in[0][0].length, shifted, 32 in.length, 0);
33 double[][] vComplexArray = getComplexFourierCoeffs(in[0][0].length, 34 shifted, in.length, 1);
35 double[][] vRealArray = getRealFourierCoeffs(in[0][0].length, shifted, 36 in.length, 1);
37 double[][] dValues = getDvalues(uRealArray, uComplexArray, vRealArray, 38 vComplexArray, in[0][0].length, in.length);
39 double[][] cValues = getCvalues(dValues, uRealArray, uComplexArray, 40 vRealArray, vComplexArray, in.length);
41 double[] skew = getSkew(cValues, in.length);
42 double[] Rprime = getRprime(cValues, skew, in.length); 43 double[] dx = getDx(dValues, skew, in.length); 44 double[] dz = getDz(dValues, skew, in.length); 45 doublerad = 180 / Math.PI;
46 doublemm = 0.048; 47 doublesumSkew = 0; 48 doublesumRprime = 0; 49 doublesumDx = 0; 50 doublesumDz = 0;
51 for (int i = 0; i < in.length; i++) { 52 sumSkew = sumSkew + skew[i]; 53 sumRprime = sumRprime + Rprime[i]; 54 sumDx = sumDx + dx[i];
55 sumDz = sumDz + dz[i];
56 }
57 doublefixSkew = sumSkew * rad / in.length; 58 doublefixRprime = sumRprime * mm / in.length; 59 doublefixDx = sumDx * mm / in.length; 60 doublefixDz = sumDz * mm / in.length; 61 }
62
Page 1
calculateValues.java
63 public double[] getDz(double[][] dVals, double[] skew, int ballSize) { 64 double[] dz = new double[ballSize];
65 for (int i = 0; i < ballSize; i++) {
66 dz[i] = -dVals[3][i] * Math.sin(skew[i]) - dVals[6][i]
67 * Math.cos(skew[i]);
68 }
69 return dz;
70 }
71
72 public double[] getDx(double[][] dVals, double[] skew, int ballSize) { 73 double[] dx = new double[ballSize];
74 for (int i = 0; i < ballSize; i++) {
75 dx[i] = dVals[8][i] * Math.sin(skew[i]) - dVals[5][i]
76 * Math.cos(skew[i]);
77 }
78 return dx;
79 }
80
81 public double[] getRprime(double[][] cVals, double[] skew, int ballSize) { 82 double[] Rp = new double[ballSize];
83 for (int i = 0; i < ballSize; i++) {
84 Rp[i] = cVals[1][i] * Math.cos(skew[i]) - cVals[3][i]
85 * Math.sin(skew[i]);
86 }
87 return Rp;
88 }
89
90 public double[][] getCvalues(double[][] dValues, double[][] ur, 91 double[][] uc, double[][] vr, double[][] vc, int ballSize) { 92 double[][] cValues = new double[4][ballSize];
93 for (int i = 0; i < ballSize; i++) { 94 cValues[0][i] = dValues[0][i] 95 * (0.5 * (dValues[0][i] * ur[2][i] + dValues[1][i] 96 * uc[1][i]) + ur[1][i]) 97 + dValues[1][i] 98 * (0.5 * (dValues[0][i] * uc[1][i] - dValues[1][i] 99 * ur[2][i]) + uc[0][i]) + ur[0][i] / 2; 100 cValues[1][i] = dValues[1][i] 101 * (0.5 * (dValues[0][i] * ur[2][i] + dValues[1][i] 102 * uc[1][i]) + ur[1][i]) 103 - dValues[0][i] 104 * (0.5 * (dValues[0][i] * uc[1][i] - dValues[1][i] 105 * ur[2][i]) + uc[0][i]); 106 cValues[2][i] = dValues[0][i] 107 * (0.5 * (dValues[0][i] * vr[2][i] + dValues[1][i] 108 * vc[1][i]) + vr[1][i]) 109 + dValues[1][i] 110 * (0.5 * (dValues[0][i] * vc[1][i] - dValues[1][i] 111 * vr[2][i]) + vc[0][i]) + vr[0][i] / 2; 112 ; 113 cValues[3][i] = dValues[1][i] 114 * (0.5 * (dValues[0][i] * vr[2][i] + dValues[1][i] 115 * vc[1][i]) + vr[1][i]) 116 - dValues[0][i] 117 * (0.5 * (dValues[0][i] * vc[1][i] - dValues[1][i] 118 * vr[2][i]) + vc[0][i]); 119 } 120 return cValues; 121 } 122
calculateValues.java 125 double dvalues[][] = new double[9][ballSize]; 126 for (int i = 0; i < ballSize; i++) {
127 // d20 128 dvalues[0][i] = (ur[1][i] - ur[3][i]) * ur[2][i] 129 - (uc[2][i] - uc[0][i]) * uc[1][i]; 130 // d21 131 dvalues[1][i] = (ur[1][i] + ur[3][i]) * uc[1][i] 132 - (uc[2][i] + uc[0][i]) * ur[2][i]; 133 // d22 134 dvalues[2][i] = 0.5 * (Math.pow(ur[3][i], 2)
135 - Math.pow(ur[1][i], 2) + Math.pow(uc[2][i], 2) + Math.pow(
136 uc[0][i], 2)); 137 // d00 138 dvalues[3][i] = 0.5 * ((ur[0][i] + ur[2][i]) * dvalues[0][i] 139 + uc[1][i] * dvalues[1][i] + 2 * ur[1][i] * dvalues[2][i]); 140 // d01 141 dvalues[4][i] = 0.5 * (uc[1][i] * dvalues[0][i] 142 + (ur[0][i] - ur[2][i]) * dvalues[1][i] + 2 * uc[0][i] 143 * dvalues[2][i]); 144 // d02 145 dvalues[5][i] = 0.5 * (ur[1][i] * dvalues[0][i] + uc[0][i] 146 * dvalues[1][i] + ur[0][i] * dvalues[2][i]); 147 // d10 148 dvalues[6][i] = 0.5 * ((vr[0][i] + vr[2][i]) * dvalues[0][i] 149 + vc[1][i] * dvalues[1][i] + 2 * vr[1][i] * dvalues[2][i]); 150 // d11 151 dvalues[7][i] = 0.5 * (vc[1][i] * dvalues[0][i] 152 + (vr[0][i] - vr[2][i]) * dvalues[1][i] + 2 * vc[0][i] 153 * dvalues[2][i]); 154 // d12 155 dvalues[8][i] = 0.5 * (vr[1][i] * dvalues[0][i] + vc[0][i] 156 * dvalues[1][i] + vr[0][i] * dvalues[2][i]); 157 } 158 return dvalues; 159 } 160
161 public double[][] getComplexFourierCoeffs(int noOfProjections, 162 int[][][] coordinateVector, int ballSize, int UorV) { 163 double[][] complexSum = new double[3][ballSize];
164 double[][] Complex1 = new double[noOfProjections][ballSize]; 165 double[][] Complex2 = new double[noOfProjections][ballSize]; 166 double[][] Complex3 = new double[noOfProjections][ballSize]; 167 double radians = Math.PI / 180;
168 double ProjectionAngle = 360 / noOfProjections; 169 for (int i = 0; i < noOfProjections; i++) { 170 for (int j = 0; j < ballSize; j++) {
171 Complex1[i][j] = Math.sin(1 * radians * ProjectionAngle * i
172 * sense);
173 Complex2[i][j] = Math.sin(2 * radians * ProjectionAngle * i
174 * sense);
175 Complex3[i][j] = Math.sin(3 * radians * ProjectionAngle * i
176 * sense);
177 }
178 }
179 for (int i = 0; i < noOfProjections; i++) { 180 for (int j = 0; j < ballSize; j++) { 181 complexSum[0][j] = complexSum[0][j]
182 + coordinateVector[j][UorV][i] * Complex1[i][j] * 2
183 / noOfProjections;
184 complexSum[1][j] = complexSum[1][j]
calculateValues.java 187 complexSum[2][j] = complexSum[2][j]
188 + coordinateVector[j][UorV][i] * Complex3[i][j] * 2
189 / noOfProjections; 190 } 191 } 192 return complexSum; 193 } 194
195 public double[][] getRealFourierCoeffs(int noOfProjections, 196 int[][][] coordinateVector, int ballSize, int UorV) { 197 double[][] realSum = new double[4][ballSize];
198 double[][] Real1 = new double[noOfProjections][ballSize]; 199 double[][] Real2 = new double[noOfProjections][ballSize]; 200 double[][] Real3 = new double[noOfProjections][ballSize]; 201 double[][] Real4 = new double[noOfProjections][ballSize]; 202 double radians = Math.PI / 180;
203 double ProjectionAngle = 360 / noOfProjections; 204 for (int i = 0; i < noOfProjections; i++) { 205 for (int j = 0; j < ballSize; j++) {
206 Real1[i][j] = Math.cos(0 * radians * ProjectionAngle * i
207 * sense);
208 Real2[i][j] = Math.cos(1 * radians * ProjectionAngle * i
209 * sense);
210 Real3[i][j] = Math.cos(2 * radians * ProjectionAngle * i
211 * sense);
212 Real4[i][j] = Math.cos(3 * radians * ProjectionAngle * i
213 * sense);
214 }
215 }
216 for (int i = 0; i < noOfProjections; i++) { 217 for (int j = 0; j < ballSize; j++) {
218 realSum[0][j] = realSum[0][j] + Real1[i][j]
219 * coordinateVector[j][UorV][i] * 2 / noOfProjections; 220 realSum[1][j] = realSum[1][j] + Real2[i][j]
221 * coordinateVector[j][UorV][i] * 2 / noOfProjections; 222 realSum[2][j] = realSum[2][j] + Real3[i][j]
223 * coordinateVector[j][UorV][i] * 2 / noOfProjections; 224 realSum[3][j] = realSum[3][j] + Real4[i][j]
225 * coordinateVector[j][UorV][i] * 2 / noOfProjections;
226 }
227 }
228 return realSum;
229 }
Controller.java 1import java.awt.image.BufferedImage; 3
4public class Controller implements Observe { 5 imageJFileChooser iJCF; 6 GUI gui; 7 ImageProcessing IP; 8 ROI roi; 9 Trajectories traj; 10 int width = 2400; 11 int height = 2400; 12 int noOfImages; 13 int noOfBalls; 14 int[][][] allCoordsArray; 15 int contrastInt = 1; 16 int mx1, mx2, my1, my2 = 0; 17
18 // CONSTRUCTOR
19 public Controller() {
20 iJCF = new imageJFileChooser();
21 try {
22 gui = new GUI(iJCF); 23 } catch (IOException e) {
24 // TODO Auto-generated catch block
25 e.printStackTrace();
26 }
27 iJCF.AddObserver(this); 28 gui.AddObserver(this); 29 }
30
31 publicvoid runImageProcess() { 32 noOfBalls = 0;
33 noOfImages = roi.getNoOfImages(iJCF.getFileName()); 34 boolean firstImageRun = true;
35 String filename = iJCF.getFileName().replace("001.bin", ""); 36 String tempFilename;
37 for (int i = 1; i <= noOfImages; i++) { 38 if (firstImageRun) {
39 IP = new ImageProcessing(iJCF.getFileName(), contrastInt, mx1,
40 mx2, my1, my2);
41 allCoordsArray = new int[getNoOfBallsFromImage()][2][noOfImages]; 42 allCoordsArray[0][0][0] = IP.getCoordList().get(0).get(0); 43 allCoordsArray[0][1][0] = IP.getCoordList().get(0).get(1); 44 firstImageRun = false;
45 } else {
46 if (i < 10) {
47 tempFilename = filename + "00" + i + ".bin"; 48 } elseif (i < 100) {
49 tempFilename = filename + "0" + i + ".bin";
50 } else {
51 tempFilename = filename + i + ".bin";
52 }
53 System.out.printf("Image processed: %d\n", i); 54 gui.setNoOfImagesLabel(i);
55 IP = new ImageProcessing(tempFilename, contrastInt, mx1, mx2,
56 my1, my2);
57 new Thread() {
58 public void run() {
59 setNoOfBallsLabelView();
60 }
61 }.start();
62 // Show image in Frame
63 new Thread() {
Page 1
Controller.java
64 public void run() {
65 BufferedImage panelImage = IP.scaleBufferedImage(
66 IP.getBufferedImage(), 0.2);
67 gui.setImageToGoPanel(panelImage);
68 }
69 }.start();
70 for (int j = 0; j < IP.getCoordList().size(); j++) { 71 if (IP.getCoordList().size() <= allCoordsArray.length) {
72 // Find the closest one and put it there
73 // NOOFBALLS IS SET TO 7, MAKE SURE IT CHANGES TAKES IN
74 // A NOOFBALLS-VARIABLE
75 // Check if none of the balls in the previous image was 76 if (IP.getCoordList().size() == noOfBalls) { 77 allCoordsArray[j][0][i - 1] = IP.getCoordList() 78 .get(j).get(0); 79 allCoordsArray[j][1][i - 1] = IP.getCoordList() 80 .get(j).get(1); 81 } else { 82 System.out.printf("noOfBalls: %d\n", IP 83 .getCoordList().size());
84 intyCoord = IP.getCoordList().get(j).get(0);
85 intxCoord = IP.getCoordList().get(j).get(1);
86 if (yCoord == 0) {
87 }
88 intdist = (int) Math.sqrt(Math.pow(
89 Math.abs(yCoord
90 - allCoordsArray[0][0][i - 2]), 2)
91 + Math.pow(Math.abs(xCoord
92 - allCoordsArray[0][1][i - 2]), 2));
93 intidy = 0;
94 for (int k = 1; k < IP.getCoordList().size(); k++) {
95 int cDist = (int) Math.sqrt(Math.pow(
96 Math.abs(yCoord 97 - allCoordsArray[k][0][i - 2]), 98 2) 99 + Math.pow(Math.abs(xCoord 100 - allCoordsArray[k][1][i - 2]), 101 2)); 102 System.out
103 .printf("Distance for ball %d = %d\n",
104 k, cDist); 105 if (cDist < dist) { 106 dist = cDist; 107 idy = k; 108 } 109 }
110 System.out.printf("idy = %d\n", idy);
111 allCoordsArray[idy][0][i - 1] = IP.getCoordList()
112 .get(j).get(0);
113 allCoordsArray[idy][1][i - 1] = IP.getCoordList()
114 .get(j).get(1);
115 }
116 }
117 }
118 }
119 noOfBalls = noOfBalls + getNoOfBallsFromImage();
120 }
121 noOfBalls = noOfBalls / noOfImages; 122 // Make Trajectory
123 gui.enableTrajectoryButton();
124 calculateValues calcVal = new calculateValues(allCoordsArray, width,
Controller.java
126 traj = new Trajectories(allCoordsArray, width, height, noOfBalls);
127 }
128
129 public BufferedImage getCurrentImage() { 130 return IP.getBufferedImage();
131 }
132
133 public int getNoOfBallsFromImage() { 134 return IP.getNoOfBalls();
135 }
136
137 public void setNoOfBallsLabelView() {
138 gui.setBallLabel(getNoOfBallsFromImage());
139 }
140
141 public void setCoordLabelView() { 142 gui.setCoordLabel(IP.getCoordList());
143 }
144
145 public void setImageToContentPane() {
146 gui.setImageToContentPane(IP.getBufferedImage());
147 }
148
149 public void setDirectoryString(String directoryString) { 150 gui.setTextFieldString(directoryString);
151 }
152
153 public void setROIimgGui() {
154 }
155
156 @Override
157 public void Update() {
158 // TODO Auto-generated method stub 159 runImageProcess();
160 gui.setRunButtonText("Run Calculation");
161 }
162
163 @Override
164 public void UpdateTextFieldString(String s) { 165 // TODO Auto-generated method stub 166 gui.setTextFieldString(s);
167 }
168
169 @Override
170 public void UpdateROIimg() {
171 // TODO Auto-generated method stub
172 try {
173 roi = new ROI(iJCF.getFileName(), width, height); 174 roi.AddObserver(this);
175 roi.startROIimg();
176 gui.loadROIimgFrame(roi.getAfterPic()); 177 gui.setROIButtonText("Select ROI"); 178 gui.disposeDialog();
179 } catch (IOException e) {
180 // TODO Auto-generated catch block 181 e.printStackTrace();
182 }
183 }
184
185 @Override
Controller.java 188 contrastInt = contrast;
189 }
190
191 @Override
192 public void UpdateROIimgRotation() { 193 // TODO Auto-generated method stub 194 gui.disposeROI();
195 try {
196 roi = new ROI(iJCF.getFileName(), width, height); 197 } catch (IOException e) {
198 // TODO Auto-generated catch block 199 e.printStackTrace();
200 }
201 roi.AddObserver(this); 202 roi.startROIimgWithRotation();
203 gui.loadROIimgFrame(roi.getAfterPic()); 204 gui.setRunButtonEnabled();
205 gui.setROIButtonText("Select ROI"); 206 gui.disposeDialog();
207 }
208
209 @Override
210 public void UpdateRIO(int mx1, int mx2, int my1, int my2) { 211 // TODO Auto-generated method stub
212 this.mx1 = mx1; 213 this.mx2 = mx2; 214 this.my1 = my1; 215 this.my2 = my2;
216 }
217
218 @Override
219 public void UpdateTrajectoryFrame() { 220 // TODO Auto-generated method stub 221 traj.startTrajectory();
222 }
GUI.java 1import java.awt.BorderLayout;
36
37public class GUI implements Subject { 38 JLabel coordLabel;
39 JPanel contentPane = new JPanel(); 40 JPanel imageContentPane = new JPanel(); 41 JPanel middlePanel;
42 JTextField imageDirectoryTextField; 43 private JFrame mainFrame;
44 private JFrame contrastFrame;
45 private JPanel controlPanel = new JPanel(); 46 private JPanel goPanel = new JPanel(); 47 private JLabel ballLabel;
48 private JPanel contrastImagePanel = new JPanel(); 49 private JFrame ROIFrame;
50 private ImageIcon imageIcon; 51 private JLabel noOfImagesLabel;
52 private JPanel buttomPanel = new JPanel(); 53 JDialog dialog;
54 JPanel dialogPanel; 55 JLabel dotLoadingLabel; 56 private JLabel imageLabel;
57 private JButton runButton = new JButton("Run Calculation"); 58 private JButton ROIButton = new JButton("Select ROI"); 59 private JButton settingsButton = new JButton("Settings"); 60 private JButton trajectoryButton = new JButton("Show Trajectory"); 61 staticfinal int contrastMax = 30;
62 staticfinal int contrastMin = 0; 63 staticfinal int contrastInit = 10; 64 private int mx1, mx2, my1, my2; 65 JSlider contrastSlider; 66 imageJFileChooser iJFC;
67 private ArrayList<Observe> observers; 68
69 public GUI() { 70 }
71
72 public GUI(imageJFileChooser iJFC) throws IOException { 73 this.iJFC = iJFC;
74 observers = new ArrayList<Observe>(); 75 runButton.setForeground(Color.blue); 76 prepareGUI(); 77 showImageButtonJPanel(); 78 showDirectoryJPanel(); 79 showBallJPanel(); 80 showGoJPanel();
81 mainFrame.add(controlPanel); 82 mainFrame.add(goPanel); 83 mainFrame.add(buttomPanel); 84 mainFrame.setVisible(true); 85 }
86
87 private void prepareGUI() {
88 mainFrame = new JFrame("Detector Calibration"); 89 mainFrame.setSize(1800, 1000);
90 mainFrame.setLayout(new GridLayout(3, 1)); 91 mainFrame.addWindowListener(new WindowAdapter() { 92 publicvoid windowClosing(WindowEvent windowEvent) {
93 System.exit(0);
94 }
95 });
96 BufferedImage firstBuffered = new BufferedImage(200, 400,
Page 1
GUI.java 97 BufferedImage.TYPE_USHORT_GRAY); 98 imageIcon = new ImageIcon();
99 imageIcon.setImage(firstBuffered);
100 imageDirectoryTextField = new JTextField("No directory chosen"); 101 ballLabel = new JLabel("Number of balls: ");
102 controlPanel.setLayout(new GridLayout(1, 3)); 103 goPanel.setLayout(new GridLayout(1, 3)); 104 buttomPanel.setLayout(new GridLayout(1, 3)); 105 JPanel rightButtomPanel = new JPanel();
106 rightButtomPanel.setLayout(new GridLayout(5, 2)); 107 runButton.setEnabled(false);
108 trajectoryButton.setEnabled(false);
109 ROIButton.addActionListener(new ActionListener() {
110 @Override
111 public void actionPerformed(ActionEvent e) {
112 if (imageDirectoryTextField.getText().contains(".bin")) { 113 ROIButton.setText("Loading ROI");
114 new Thread() {
115 public void run() {
116 dialog = new JDialog();
117 dialogPanel = new JPanel();
118 JPanel dialogPanel2 = new JPanel(); 119 dialog.setLayout(new GridLayout(2, 1));
120 dialog.add(dialogPanel);
121 dialog.add(dialogPanel2);
122 dialog.setUndecorated(true);
123 dotLoadingLabel = new JLabel("");
124 Dimension screenSize = Toolkit.getDefaultToolkit()
125 .getScreenSize();
126 Double dwidth = screenSize.getWidth(); 127 Double dheight = screenSize.getHeight();
128 intwidth = dwidth.intValue();
129 intheight = dheight.intValue();
130 dialog.setPreferredSize(new Dimension(400, 200)); 131 dialog.setBounds(width / 2 - 200, height / 2 - 100,
132 dialog.getWidth(), dialog.getHeight());
133 dialog.pack();
134 JLabel loadingLabel = new JLabel("Loading ROI");
135 loadingLabel.setFont(loadingLabel.getFont()
136 .deriveFont(30.0f));
137 dotLoadingLabel.setFont(loadingLabel.getFont()
138 .deriveFont(30.0f));
139 dialogPanel.add(loadingLabel);
140 dialogPanel2.add(dotLoadingLabel);
141 dialog.setVisible(true);
142 Timer timer = new Timer();
143 timer.schedule(new UpdateLoadingROILabel(), 0, 500);
144 }
145 }.start();
146 new Thread() {
147 public void run() {
148 NotifyObserversROIimg();
149 }
150 }.start();
151 } else {
152 final JFrame exceptionFrame = new JFrame(); 153 JButton exitButton = new JButton("OK");
154 exceptionFrame.getContentPane().setLayout(
155 new GridLayout(2, 1));
156 exceptionFrame
157 .getContentPane()
GUI.java
159 "Please choose the first file. Ends with 001.bin")); 160 exceptionFrame.getContentPane().add(exitButton);
161 exceptionFrame.setBounds(mainFrame.getWidth() / 2 - 150, 162 mainFrame.getHeight() / 2 - 50, 300, 100);
163 exceptionFrame.setVisible(true);
164 exitButton.addActionListener(new ActionListener() {
165 @Override
166 public void actionPerformed(ActionEvent e) {
167 exceptionFrame.dispose(); 168 } 169 }); 170 } 171 } 172 });
173 runButton.addActionListener(new ActionListener() {
174 @Override
175 public void actionPerformed(ActionEvent e) {
176 if (imageDirectoryTextField.getText().contains(".bin")) { 177 runButton.setText("Running...");
178 new Thread() {
179 public void run() {
180 NotifyObservers();
181 }
182 }.start();
183 } else {
184 final JFrame exceptionFrame = new JFrame(); 185 JButton exitButton = new JButton("OK");
186 exceptionFrame.getContentPane().setLayout(
187 new GridLayout(2, 1));
188 exceptionFrame
189 .getContentPane()
190 .add(new JLabel(
191 "Please choose the first file. Ends with 001.bin")); 192 exceptionFrame.getContentPane().add(exitButton);
193 exceptionFrame.setBounds(mainFrame.getWidth() / 2 - 150, 194 mainFrame.getHeight() / 2 - 50, 300, 100);
195 exceptionFrame.setVisible(true);
196 exitButton.addActionListener(new ActionListener() {
197 @Override
198 public void actionPerformed(ActionEvent e) {
199 // TODO Auto-generated method stub
200 exceptionFrame.dispose();
201 }
202 });
203 }
204 runButton.setText("Run Calculation");
205 }
206 });
207 settingsButton.addActionListener(new ActionListener() {
208 @Override
209 public void actionPerformed(ActionEvent e) { 210 // TODO Auto-generated method stub 211 JFrame settingsFrame = new JFrame();
212 settingsFrame.getContentPane().setLayout(new GridLayout(2, 1)); 213 contrastSlider = new JSlider(JSlider.HORIZONTAL, contrastMin,
214 contrastMax, contrastInit);
215 contrastSlider.addChangeListener(new ChangeListener() {
216 @Override
217 public void stateChanged(ChangeEvent e) { 218 if (contrastSlider.getValueIsAdjusting()) {
GUI.java
221 }
222 });
223 contrastSlider.setMajorTickSpacing(10); 224 contrastSlider.setMinorTickSpacing(1); 225 contrastSlider.setPaintTicks(true); 226 contrastSlider.setPaintLabels(true);
227 contrastImagePanel.setPreferredSize(new Dimension(2000, 2000)); 228 contrastImagePanel.setVisible(true);
229 contrastFrame = new JFrame("Settings");
230 contrastFrame.getContentPane().setLayout(new GridLayout(3, 1)); 231 contrastFrame.add(contrastSlider);
232 contrastFrame.add(contrastImagePanel); 233 contrastFrame.pack();
234 contrastFrame.setVisible(true);
235 }
236 });
237 trajectoryButton.addActionListener(new ActionListener() {
238 @Override
239 public void actionPerformed(ActionEvent e) { 240 NotiifyObserverShowTrajectory();
241 }
242 });
243 noOfImagesLabel = new JLabel("Images Processed: "); 244 imageLabel = new JLabel();
245 Border border = BorderFactory.createLineBorder(Color.darkGray, 5); 246 JPanel imagePanel = new JPanel();
247 JPanel insideImagePanel = new JPanel(); 248 insideImagePanel.setLayout(new BorderLayout()); 249 imagePanel.setLayout(new GridLayout(1, 4)); 250 imagePanel.add(new JPanel());
251 imagePanel.add(new JPanel()); 252 imagePanel.add(new JPanel()); 253 imagePanel.add(insideImagePanel);
254 insideImagePanel.add(noOfImagesLabel, BorderLayout.NORTH); 255 insideImagePanel.add(imageLabel, BorderLayout.CENTER); 256 imageLabel.setBorder(border);
257 goPanel.add(imagePanel); 258 buttomPanel.add(new JPanel()); 259 buttomPanel.add(new JPanel()); 260 buttomPanel.add(rightButtomPanel); 261 rightButtomPanel.add(trajectoryButton); 262 rightButtomPanel.add(new JPanel()); 263 rightButtomPanel.add(new JPanel()); 264 rightButtomPanel.add(new JPanel()); 265 rightButtomPanel.add(new JPanel()); 266 rightButtomPanel.add(new JPanel());
267 }
268
269 public void updateContrastFrame(BufferedImage bI) {
270 contrastFrame.getContentPane().add(new JLabel(new ImageIcon(bI))); 271 contrastImagePanel.add(new JLabel(new ImageIcon(bI)));
272 }
273
274 private void showBallJPanel() { 275 JPanel panel = new JPanel(); 276 panel.setLayout(new FlowLayout()); 277 panel.add(ballLabel);
278 controlPanel.add(panel);
279 }
280
GUI.java 283 panel.setLayout(new FlowLayout()); 284 panel.add(iJFC, FlowLayout.LEFT); 285 controlPanel.add(panel);
286 }
287
288 private void showDirectoryJPanel() { 289
290 JPanel panel = new JPanel(); 291 panel.setLayout(new FlowLayout());
292 imageDirectoryTextField.setPreferredSize(new Dimension(400, 30)); 293 panel.add(imageDirectoryTextField, FlowLayout.LEFT);
294 controlPanel.add(panel);
295 }
296
297 private void showGoJPanel() { 298 middlePanel = new JPanel();
299 middlePanel.setLayout(new FlowLayout()); 300 middlePanel.add(runButton);
301 middlePanel.add(ROIButton); 302 middlePanel.add(settingsButton); 303 goPanel.add(middlePanel);
304 }
305
306 public void loadROIimgFrame(BufferedImage ROIImage) { 307 final Graphics2D g2d = ROIImage.createGraphics(); 308 ROIFrame = new JFrame();
309 JButton okButton = new JButton("OK"); 310 JButton cancelButton = new JButton("Cancel");
311 JButton rotateButton = new JButton("Rotate Clockwise"); 312 final JLabel xAxisLabel = new JLabel("X:");
313 final JLabel yAxisLabel = new JLabel("Y:"); 314 final JLabel xAxisLabel2 = new JLabel("X:"); 315 final JLabel yAxisLabel2 = new JLabel("Y:");
316 rotateButton.addActionListener(new ActionListener() {
317 @Override
318 public void actionPerformed(ActionEvent e) {
319 new Thread() {
320 public void run() {
321 dialog = new JDialog();
322 dialogPanel = new JPanel(); 323 JPanel dialogPanel2 = new JPanel(); 324 dialog.setLayout(new GridLayout(2, 1));
325 dialog.add(dialogPanel);
326 dialog.add(dialogPanel2); 327 dialog.setUndecorated(true); 328 dotLoadingLabel = new JLabel("");
329 Dimension screenSize = Toolkit.getDefaultToolkit()
330 .getScreenSize();
331 Double dwidth = screenSize.getWidth(); 332 Double dheight = screenSize.getHeight();
333 int width = dwidth.intValue();
334 int height = dheight.intValue();
335 dialog.setPreferredSize(new Dimension(400, 200)); 336 dialog.setBounds(width / 2 - 200, height / 2 - 100, 337 dialog.getWidth(), dialog.getHeight());
338 dialog.pack();
339 JLabel loadingLabel = new JLabel("Loading new Image"); 340 loadingLabel.setFont(loadingLabel.getFont().deriveFont(
341 30.0f));
342 dotLoadingLabel.setFont(loadingLabel.getFont()
GUI.java
345 dialogPanel2.add(dotLoadingLabel);
346 dialog.setVisible(true);
347 // Set timer for loading dialog box
348 Timer timer = new Timer();
349 timer.schedule(new UpdateLoadingROILabel(), 0, 500);
350 }
351 }.start();
352 new Thread() {
353 public void run() {
354 NotifyObserverRotate();
355 }
356 }.start();
357 }
358 });
359 cancelButton.addActionListener(new ActionListener() {
360 @Override
361 public void actionPerformed(ActionEvent e) { 362 // TODO Auto-generated method stub 363 System.out.println("Cancel"); 364 ROIFrame.dispose();
365 }
366 });
367 final JLabel test = new JLabel(new ImageIcon(ROIImage)); 368 JScrollPane scrollPane = new JScrollPane(test);
369 scrollPane
370 .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
371 scrollPane
372 .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 373 Dimension preferredSize;
374 if (ROIImage.getWidth() <= 1500 && ROIImage.getHeight() <= 700) { 375 preferredSize = new Dimension(ROIImage.getWidth(),
376 ROIImage.getHeight());
377 } else if (ROIImage.getWidth() <= 1500 && ROIImage.getHeight() > 700) { 378 preferredSize = new Dimension(ROIImage.getWidth(), 700);
379 } else if (ROIImage.getWidth() > 1500 && ROIImage.getHeight() <= 700) { 380 preferredSize = new Dimension(1500, ROIImage.getHeight());
381 } else {
382 preferredSize = new Dimension(1500, 700);
383 }
384 scrollPane.setPreferredSize(preferredSize); 385 scrollPane.setMaximumSize(preferredSize); 386 test.addMouseListener(new MouseAdapter() { 387 private Boolean clicked = false; 388
389 @Override
390 public void mousePressed(MouseEvent e) { 391 if (clicked == false) {
392 mx1 = e.getX();
393 my1 = e.getY();
394 xAxisLabel.setText("X: " + mx1); 395 yAxisLabel.setText("Y: " + my1); 396 System.out.println(mx1 + ", " + my1);
397 clicked = true;
398 } else if (clicked == true) {
399 mx2 = e.getX();
400 my2 = e.getY();
401 System.out.println(mx2 + ", " + my2);
402 clicked = false;
GUI.java
407 g2d.setColor(Color.BLACK);
408 test.paint(g2d);
409 xAxisLabel2.setText("X: " + mx2); 410 yAxisLabel2.setText("Y: " + my2);
411 }
412 ROIFrame.repaint();
413 }
414
415 @Override
416 public void mouseReleased(MouseEvent e) {
417 }
418
419 });
420
421 okButton.addActionListener(new ActionListener() { 422
423 @Override
424 public void actionPerformed(ActionEvent e) { 425 // TODO Auto-generated method stub 426 System.out.println("OK");
427 NotifyObserverROI();
428 System.out.println(mx1 + ", " + my1 + " and " + mx2 + ", "
429 + my2); 430 ROIFrame.dispose(); 431 setRunButtonEnabled(); 432 } 433 }); 434
435 JPanel rightPanel = new JPanel(new GridLayout(20, 1)); 436
437 rightPanel.add(new JLabel("First Coordinates:")); 438 rightPanel.add(xAxisLabel);
439 rightPanel.add(yAxisLabel);
440 rightPanel.add(new JLabel("Second Coordinates:")); 441 rightPanel.add(xAxisLabel2);
442 rightPanel.add(yAxisLabel2); 443 rightPanel.add(new JPanel()); 444 rightPanel.add(new JPanel()); 445 rightPanel.add(new JPanel()); 446 rightPanel.add(new JPanel()); 447 rightPanel.add(new JPanel()); 448 rightPanel.add(new JPanel()); 449 rightPanel.add(new JPanel()); 450 rightPanel.add(new JPanel()); 451 rightPanel.add(new JPanel()); 452 rightPanel.add(new JPanel()); 453 rightPanel.add(new JPanel()); 454 rightPanel.add(rotateButton); 455 rightPanel.add(okButton); 456 rightPanel.add(cancelButton); 457
458 ROIFrame.getContentPane().add(rightPanel, BorderLayout.EAST); 459 ROIFrame.getContentPane().add(scrollPane, BorderLayout.CENTER); 460 ROIFrame.setResizable(false);
461 ROIFrame.pack();
462 ROIFrame.setVisible(true); 463
464 }
465
GUI.java
469 System.out.printf("Width: %d\nHeight: %d\n\n", image.getWidth(),
470 image.getHeight());
471
472 }
473
474 public void setBallLabel(int noOfBalls) {
475 String ballString = String.valueOf(noOfBalls); 476 ballLabel.setText("Number of Balls: " + ballString); 477
478 }
479
480 // Is this supposed to be in this class?
481 private String convertIntegerListToString(List<List<Integer>> list) { 482 int[][] tempToStringArray = new int[list.size()][2];
483 String outputString = "<html>"; 484 int ballInt = 0;
485
486 for (int i = 0; i < list.size(); i++) {
487 tempToStringArray[i][0] = list.get(i).get(0); 488 tempToStringArray[i][1] = list.get(i).get(1);
489 ballInt++;
490
491 outputString += "Ball " + ballInt + ":<br>x:" 492 + tempToStringArray[i][0] + "<br>y: " 493 + tempToStringArray[i][1] + "<br><br> "; 494 } 495 outputString += "</html>"; 496 ballInt = 0; 497 498 return outputString; 499 } 500
501 public void setImageToContentPane(BufferedImage image) { 502
503 // System.out.println("FRAME");
504 BufferedImage scaledImage = new BufferedImage(image.getWidth(), 505 image.getHeight(), BufferedImage.TYPE_INT_ARGB); 506 AffineTransform at = new AffineTransform();
507 at.scale(0.4, 0.4);
508 at.translate(image.getWidth() * 0.5, image.getHeight() * 0.5); 509 // at.rotate(Math.PI/2);
510 at.translate(-image.getWidth() * 0.5, -image.getHeight() * 0.5); 511 AffineTransformOp scaleOp = new AffineTransformOp(at,
512 AffineTransformOp.TYPE_BILINEAR); 513 scaledImage = scaleOp.filter(image, scaledImage); 514 setImageToGoPanel(scaledImage);
515 }
516
517 public void setCoordLabel(List<List<Integer>> ballCoordArray) { 518
519 coordLabel.setText(convertIntegerListToString(ballCoordArray)); 520 coordLabel.setBounds(20, 20, 50, 550);
521 controlPanel.add(coordLabel); 522
523 }
524
525 public void setTextFieldString(String directoryString) { 526 imageDirectoryTextField.setText(directoryString);
527 }
528
GUI.java
531 }
532
533 public void openImageInFrame(BufferedImage image) { 534 JScrollPane scrollPane = new JScrollPane(new JLabel( 535 new ImageIcon(image)));
536 scrollPane
537 .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
538 scrollPane
539 .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 540
541 JButton contrastButton = new JButton("Cancel"); 542
543 contrastButton.addActionListener(new ActionListener() { 544
545 @Override
546 public void actionPerformed(ActionEvent e) { 547 // TODO Auto-generated method stub 548
549 }
550 });
551
552 JFrame frame = new JFrame();
553 frame.getContentPane().setLayout(new GridLayout(1, 2)); 554 frame.getContentPane().add(scrollPane);
555 frame.pack();
556 frame.setVisible(true); 557
558 }
559
560 public void setROIButtonText(String text) { 561
562 ROIButton.setText(text);
563 }
564
565 public void setRunButtonText(String text) { 566
567 runButton.setText(text);
568 }
569
570 public void disposeROI() { 571 ROIFrame.dispose();
572 }
573
574 public void setNoOfImagesLabel(int noOfImages) {
575 noOfImagesLabel.setText("Images processed: " + noOfImages);
576 }
577
578 public void disposeDialog() { 579 dialog.dispose();
580 }
581
582 public void enableTrajectoryButton() { 583 trajectoryButton.setEnabled(true);
584 }
585
586 // OBSERVERS 587
588 @Override
GUI.java 593
594 @Override
595 public void RemoveObserver(Observe o) { 596 // TODO Auto-generated method stub 597 observers.remove(o);
598 }
599
600 @Override
601 public void NotifyObservers() { 602 // TODO Auto-generated method stub
603 for (int i = 0; i < observers.size(); i++) { 604 observers.get(i).Update();
605 }
606
607 }
608
609 @Override
610 public void NotifyObserversTextField() { 611 // TODO Auto-generated method stub 612
613 }
614
615 @Override
616 public void NotifyObserversROIimg() { 617 // TODO Auto-generated method stub
618 for (int i = 0; i < observers.size(); i++) { 619 observers.get(i).UpdateROIimg();
620 }
621 }
622
623 @Override
624 public void NotifyObserversContrast(int contrast) { 625 // TODO Auto-generated method stub
626 for (int i = 0; i < observers.size(); i++) { 627 observers.get(i).UpdateContrast(contrast);
628 }
629 }
630
631 @Override
632 public void NotifyObserverRotate() { 633 // TODO Auto-generated method stub
634 for (int i = 0; i < observers.size(); i++) { 635 observers.get(i).UpdateROIimgRotation();
636 }
637
638 }
639
640 @Override
641 public void NotifyObserverROI() { 642 // TODO Auto-generated method stub
643 for (int i = 0; i < observers.size(); i++) { 644 observers.get(i).UpdateRIO(mx1, mx2, my1, my2);
645 }
646 }
647
648 // Subclasses
649 class UpdateLoadingROILabel extends TimerTask { 650 public void run() {
651 if (dotLoadingLabel.getText() == "") { 652 dotLoadingLabel.setText(".");
GUI.java 655
656 } else if (dotLoadingLabel.getText() == "..") { 657 dotLoadingLabel.setText("...");
658
659 } else if (dotLoadingLabel.getText() == "...") { 660 dotLoadingLabel.setText("....");
661
662 } else if (dotLoadingLabel.getText() == "....") { 663 dotLoadingLabel.setText(""); 664 665 } 666 667 } 668 } 669 670 @Override
671 public void NotiifyObserverShowTrajectory() { 672 // TODO Auto-generated method stub
673 for (int i = 0; i < observers.size(); i++) { 674 observers.get(i).UpdateTrajectoryFrame();
675 }
676 }
ImageProcessing.java 1import java.awt.FlowLayout;
21
22public class ImageProcessing { 23
24 private int[][] resultImgMatrix; 25 private int minContrast = 40000;
26 private List<List<Integer>> ballCoordArray = new ArrayList<List<Integer>>(); 27 private BufferedImage image;
28 29 // Size of images 30 int width = 1024; 31 int height = 2048; 32 33 int contrastInt; 34 35 // Constructor
36 public ImageProcessing(String imageName, int contrastInt, intmx1, intmx2, 37 int my1, int my2) {
38
39 this.contrastInt = contrastInt; 40
41 try {
42 resultImgMatrix = getDataArray(width, height, imageName); 43
44 } catch (IOException e) {
45 // TODO Auto-generated catch block
46 e.printStackTrace();
47 }
48
49 // CALCULATE MAX AND MIN VALUES
50 int max = resultImgMatrix[0][0]; 51 int min = resultImgMatrix[0][0]; 52
53 for (int i = 0; i < resultImgMatrix.length; i++) { 54 for (int j = 0; j < resultImgMatrix[0].length; j++) { 55 if (resultImgMatrix[i][j] > max) {
56 max = resultImgMatrix[i][j]; 57 } elseif (resultImgMatrix[i][j] < min) { 58 min = resultImgMatrix[i][j];
59 }
60 }
61 }
62 // END CALC MAX/MIN
63
64 // ContrastCorrection
65 resultImgMatrix = contrastArrayCorrection(resultImgMatrix, 1); 66 resultImgMatrix = contrastArrayCorrection(resultImgMatrix, 2); 67 resultImgMatrix = fullContrastCorrection(resultImgMatrix); 68
69 if (mx1 != 0 && mx2 != 0) {
ImageProcessing.java
82 public int[][] getDataArray(int x, int y, String filename) 83 throws IOException {
84
85 FileInputStream is = new FileInputStream(filename); 86 byte[] bytes = ByteStreams.toByteArray(is); 87
88 int[][] data = new int[x][y]; 89 int c = 0;
90
91 for (int i = 0; i < x; i++) { 92 for (int j = 0; j < y; j++) {
93 data[i][j] = (bytes[c] & 0xff) | (short) (bytes[c + 1] << 8); 94 if (data[i][j] < 0) { 95 data[i][j] = 65536 + data[i][j]; 96 } 97 c = c + 2; 98 } 99 100 } 101 102 is.close(); 103 return data; 104 } 105
106 public int[][] contrastArrayCorrection(int[][] imageArray, 107 double contrastValue) {
108 int min = imageArray[0][0]; 109 int max = min;
110 // Calc min and max values
111 for (int i = 0; i < imageArray.length; i++) { 112 for (int j = 0; j < imageArray[0].length; j++) { 113 if (imageArray[i][j] < min) {
114 min = imageArray[i][j];
115 } else if (imageArray[i][j] > max) { 116 max = imageArray[i][j];
117 }
118 }
119 }
120
121 int middleValue = max / 2;
122 for (int i = 0; i < imageArray.length; i++) { 123 for (int j = 0; j < imageArray[0].length; j++) { 124
125 if ((imageArray[i][j] - (middleValue - imageArray[i][j]))
126 * contrastValue < 0) {
127 imageArray[i][j] = min;
128 } else if ((imageArray[i][j] + (imageArray[i][j] - middleValue))
129 * contrastValue >= max) {
130 imageArray[i][j] = 65000;
131 } else if (imageArray[i][j] < middleValue) {
132 imageArray[i][j] = (int) ((imageArray[i][j] - (middleValue - imageArray[i][j])) * contrastValue);
133 } else if (imageArray[i][j] > middleValue) {
ImageProcessing.java
142 public BufferedImage scaleBufferedImage(BufferedImage image, 143 double scaleFactor) {
144
145 Double dwidth = image.getWidth() * scaleFactor; 146 int width = dwidth.intValue();
147 Double dheight = image.getHeight() * scaleFactor; 148 int height = dheight.intValue();
149
150 BufferedImage scaledImage = new BufferedImage(width, height, 151 BufferedImage.TYPE_INT_RGB);
152
153 Graphics g = scaledImage.createGraphics(); 154 g.drawImage(image, 0, 0, width, height, null); 155 g.dispose();
156
157 return scaledImage;
158 }
159
160 public int[][] fullContrastCorrection(int[][] array) { 161
162 for (int i = 0; i < array.length; i++) { 163 for (int j = 0; j < array[0].length; j++) { 164 if (array[i][j] >= minContrast) { 165 array[i][j] = 65000; 166 } else { 167 array[i][j] = 0; 168 } 169 } 170 } 171 172 return array; 173 } 174
175 private int[][] contrastCorrection(int[][] matrix) { 176
177 BufferedImage bufferedImage = new BufferedImage(width, height, 178 BufferedImage.TYPE_BYTE_GRAY);
179 for (int i = 0; i < matrix.length; i++) { 180 for (int j = 0; j < matrix[0].length; j++) { 181 int pixel = matrix[i][j];
182
183 bufferedImage.setRGB(i, j, pixel); 184
185 }
186 }
187
188 final byte[] pixels = ((DataBufferByte) bufferedImage.getRaster() 189 .getDataBuffer()).getData();
190
191 final boolean hasAlphaChannel = bufferedImage.getAlphaRaster() != null; 192
193 int[][] result = new int[width][height]; 194 if (hasAlphaChannel) {
195 final int pixelLength = 4;
196 for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
197 int argb = 0;
ImageProcessing.java 203 col++; 204 if (col == width) { 205 col = 0; 206 row++; 207 } 208 } 209 } else {
210 final int pixelLength = 3;
211 for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
212 int argb = 0;
213 argb += -16777216; // 255 alpha
214 argb += ((int) pixels[pixel] & 0xff); // blue
215 argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green 216 argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red 217 result[row][col] = argb;
218 col++; 219 if (col == width) { 220 col = 0; 221 row++; 222 } 223 } 224 } 225 226 return result; 227 228 } 229
230 public BufferedImage getImage(String picName) {
231 try {
232 image = ImageIO.read(getClass().getResource(picName)); 233
234 // Contrast
235 RescaleOp rescaleOp = new RescaleOp(1.8f, -190, null); 236 RescaleOp rescaleOp2 = new RescaleOp(4f, 0, null); 237 rescaleOp.filter(image, image);
238 rescaleOp2.filter(image, image); 239 240 return image; 241 242 } catch (IOException e) { 243 e.printStackTrace(); 244 245 return null; 246 } 247 248 } 249
250 private int[][] flipMatrixClockwise(int[][] inputMatrix) {
251 int[][] outputMatrix = new int[inputMatrix[0].length][inputMatrix.length]; 252 int[][] tempMatrix = new int[inputMatrix[0].length][inputMatrix.length]; 253
254 // Transpose
255 for (int i = 0; i < inputMatrix.length; i++) { 256 for (int j = 0; j < inputMatrix[0].length; j++) { 257 tempMatrix[j][i] = inputMatrix[i][j];
258 }
259 }
260 // Flip each row