Keskihajontaa käytetään algoritmissa kuvan muutosten mittarina.
Keskihajonta lasketaan pikselikohtaisesti kaikista puskurin kuvista kuvan \ref{fig_dev} mukaisesti.

\begin{figure}[htb]
\centering
\newcommand{\pixel}[3][]{
    \pgfmathparse{#3/255*100}
    \pgfmathsetmacro{\temp}{\pgfmathresult}
    \pgfmathparse{(#3<127)*255}
    \pgfmathsetmacro{\tempb}{\pgfmathresult}
    \node (#1) [
        fill=white!\temp!black,
        text=white!\tempb!black
    ] at (#2) {#3};
}
\begin{tikzpicture}
[slanted/.style={x=7mm, y=7mm, yslant=0.7, xscale=0.5,
    every node/.style={yslant=0.7, xscale=0.5, draw, font=\scriptsize, minimum size=7mm}}]

{
\tikzset{slanted}
\pixel{0,0}{255} \pixel{1,0}{255} \pixel{2,0}{100}
\pixel{0,1}{255} \pixel[eka]{1,1}{255} \pixel{2,1}{255}
\pixel{0,2}{255} \pixel{1,2}{255} \pixel{2,2}{255}

\begin{pgfonlayer}{background}
\draw [shift only,thick,dashed] (eka.center)--($(eka.center) - (1cm,0)$);
\end{pgfonlayer}
\draw [shift only,thick,dashed] (eka.center)--($(eka.center) + (2cm,0)$);

\tikzset{shift only, xshift=2cm, slanted}
\pixel{0,0}{255} \pixel{1,0}{100} \pixel{2,0}{0}
\pixel{0,1}{255} \pixel[toka]{1,1}{255} \pixel{2,1}{100}
\pixel{0,2}{255} \pixel{1,2}{255} \pixel{2,2}{255}

\draw [shift only,thick,dashed] (toka.center)--($(toka.center) + (2cm,0)$);

\tikzset{shift only, xshift=2cm, slanted}
\pixel{0,0}{100} \pixel{1,0}{0} \pixel{2,0}{0}
\pixel{0,1}{255} \pixel[vika]{1,1}{100} \pixel{2,1}{0}
\pixel{0,2}{255} \pixel{1,2}{255} \pixel{2,2}{100}

\draw [shift only,thick,dashed] (vika.center)--($(vika.center) + (2cm,0)$);

\tikzset{shift only, xshift=3cm, slanted}
\pixel{0,0}{89} \pixel{1,0}{129} \pixel{2,0}{58}
\pixel{0,1}{0} \pixel[var]{1,1}{89} \pixel{2,1}{129}
\pixel{0,2}{0} \pixel{1,2}{0} \pixel{2,2}{89}

\tikzset{shift only}
\draw [thick,->] (vika)--($(var) - (1cm,0)$);
\draw [thick,dashed] (vika)--(var);
}

\node [below=2cm, text depth=0ex] at (toka) {Puskurissa olevat kuvat};
\node [below=2cm, text depth=0ex] at (var) {Keskihajonnat};
\end{tikzpicture}
\caption{Pikselikohtaiset keskihajonnat lasketaan kaikista puskurissa olevista kuvista.
Tulos kertoo sen, paljonko kukin pikseli on muuttunut.}
\label{fig_dev}
\end{figure}

\section{Keskihajonnan matemaattinen kuvaus}
\label{dev_math}
\addmglos{$n$}{Keskihajonnan laskennassa käytettävä kuvapuskurien lukumäärä.}
\addmglos{$i$}{Kuvapuskurin indeksi keskihajonnassa.}
Keskihajonta määritellään matemaattisesti keskiarvon $µ$ perusteella. Tämän
algoritmin tapauksessa keskihajonta $\sigma(x, y)$ lasketaan puskurissa olevista kuvista $g_i(x, y)$. Kuvan indeksi $i$ on välillä $[0, n-1]$, jossa $n$ on alustuksessa määritelty puskurien määrä.
\begin{align}
µ(x, y) &= \frac{1}{n}\; \sum_{i} g_i(x, y)\\
\sigma(x, y) &= \sqrt{\frac{1}{n}\; \sum_{i} (g_i(x, y) - µ(x, y))^2} \label{dev_maar}
\end{align}

Joissakin yhteyksissä käytetään myös otoskeskihajontaa, jossa jakaja on $n-1$, mutta tämän algoritmin tapauksessa näin ei tehdä.
Suoraan määritelmän perusteella laskettaessa tarvitaan jokaista laskentakertaa kohti $w \cdot h \cdot c \cdot n$ kertolaskua, $w \cdot h \cdot c$ neliöjuurta sekä $2 \cdot w \cdot h \cdot c$ jakolaskua. Laskutoimitusten määrää voidaan kuitenkin vähentää usealla tavalla.



\subsection{Neliöjuurioperaation välttäminen}
\label{dev_sqrt}
Neliöjuuren laskeminen on esimerkiksi kertolaskuun verrattuna huomattavan raskasta.
Operaatio voidaan välttää, kun laskennassa käytetään sisäisesti varianssia $\sigma(x, y)^2$.

Rajapinnassa on aiemmin käytetty keskihajontaa, joten siirtyminen kokonaan varianssiin vaatisi muutoksia algoritmia käyttävään sovellukseen.
Esimerkiksi käytetyt kynnysarvot täytyisi korottaa neliöön.
Tämä muunnos voidaan kuitenkin tehdä suoraan algoritmin rajapinnassa.

Toteutus korottaa syötetyn kynnysarvon neliöön ja vastaavasti ottaa neliöjuuren tuloksena annettavasta prosenttipisteestä.
Nämä operaatiot täytyy suorittaa vain kerran jokaiselle kuvalle, joten niiden tehokkuus ei ole kriittinen.
Jos sovellus tarvitsee pikselikohtaiset keskihajonnat taulukossa, joudutaan neliöjuurioperaatio kuitenkin suorittamaan jokaiselle pikselille.
Normaalissa käyttötilanteessa tätä toimintoa ei kuitenkaan yleensä käytetä.

\subsection{Laskenta liukuvalla ikkunalla}
\label{dev_sliding}
Keskihajonnan määritelmässä olevat summat voidaan avata käyttäen yhteyttä
$var(x) = E\big[x^2\big] - E\big[x\big]^2$ \cite[s.32]{tnlmoniste}. Tällöin saadaan seuraava esitysmuoto:
\begin{equation}
\sigma(x, y)^2 = \frac{1}{n}\; \left[ \left(\sum_{i} g_i(x, y)^2\right) - \frac{1}{n}\left(\sum_{i} g_i(x, y)\right)^2 \right]
\label{dev_sliding}
\end{equation}

Tässä muodossa on se etu, että summien sisällä olevat termit riippuvat vain kunkin kuvan pikseleistä, eivät kaikista puskureista lasketusta keskiarvosta.
Tällaiset summat voidaan laskea liukuvan ikkunan avulla kuvan \ref{fig_sliding} mukaisesti.
Jokainen syötetty kuva lisätään summiin, ja vastaavasti jokainen puskurista poistuva kuva vähennetään summista.
Algoritmi pitää muistissaan siis kaksi taulukkoa: pikseliarvojen summan, sekä pikseliarvojen neliöiden summan.

\begin{figure}[htb]
\centering
\begin{tikzpicture}
[delay/.style={draw,rectangle,minimum size=7mm,font=\small,inner sep=0mm},
 sq/.style={draw,rectangle,minimum size=7mm,font=\small,inner sep=0mm},
 div/.style={draw,regular polygon, regular polygon sides=3,minimum size=11mm,font=\small,inner sep=-2mm},
 sum/.style={draw,circle,minimum size=7mm,inner sep=0mm},
 node distance=1cm]

\node [text width=5em] (uusin) {Pikseliarvo\\uusimmassa\\kuvassa};
\node [delay, node distance=1.5cm, right=of uusin] (puskuri1) {$z^{-1}$};
\node [delay, node distance=5mm, right=of puskuri1] (puskuri2) {$z^{-1}$};
\node [delay, right=of puskuri2] (puskuri3) {$z^{-1}$};
\draw [thick,->] (uusin)--(puskuri1);
\draw [thick,->] (puskuri1)--(puskuri2);
\draw [thick,dotted,->] (puskuri2)--(puskuri3);
\node [node distance=1mm, below=of puskuri2, xshift=3mm] {Kuvapuskuri};

\node [sum, node distance=1.5cm, above=of puskuri1.west, xshift=-5mm] (uadd) {$+$};
\node [sum, node distance=1.5cm, above=of puskuri3.east, xshift=5mm] (usub) {$-$};
\node [delay, right=of usub] (ustore) {$z^{-1}$};
\draw [thick,->] (uusin)-|(uadd);
\draw [thick,->] (uadd)--(usub) node [at end, below, xshift=-1mm] {\scriptsize$+$};
\draw [thick,->] (usub)--(ustore);
\draw [thick,->] (puskuri3)-|(usub) node [at end, right, yshift=-1mm] {\scriptsize$-$};
\draw [thick,->] (ustore) -| ($(ustore.east)+(5mm,1cm)$) -| ($(uadd.west)-(5mm,0)$) -- (uadd);

\node [sq, below=of puskuri1.west, xshift=-5mm] (sq1) {$x^2$};
\node [sq, below=of puskuri3.east, xshift=5mm] (sq2) {$x^2$};
\node [sum, node distance=5mm, below=of sq1] (ladd) {$+$};
\node [sum, node distance=5mm, below=of sq2] (lsub) {$-$};
\node [delay, right=of lsub] (lstore) {$z^{-1}$};
\draw [thick,->] (uusin)-|(sq1);
\draw [thick,->] (sq1)--(ladd);
\draw [thick,->] (puskuri3)-|(sq2);
\draw [thick,->] (sq2)-- (lsub) node [at end, right, yshift=1mm] {\scriptsize$-$};
\draw [thick,->] (ladd)-- (lsub) node [at end, above, xshift=-1mm] {\scriptsize$+$};
\draw [thick,->] (lsub)--(lstore);
\draw [thick,->] (lstore) -| ($(lstore.east)+(5mm,-1cm)$) -| ($(ladd.west)-(5mm,0)$) -- (ladd);

\node [node distance=0mm, above=of ustore] {\small$\sum x$};
\node [node distance=0mm, below=of lstore] {\small$\sum x^2$};

\node [sq, node distance=5mm, below=of ustore.east, xshift=5mm] (usq) {$x^2$};
\node [div, shape border rotate=180, node distance=5mm, below=of usq] (udiv) {$\frac{1}{n}$};
\node [sum, node distance=5mm, below=of udiv] (fsub) {$-$};
\node [div, shape border rotate=-90, node distance=5mm, right=of fsub] (fdiv) {$\frac{1}{n}$};
\node [node distance=7mm, right=of fdiv, text width=5em] (result) {Varianssi\\puskurin\\pikseleistä};
\draw [thick,->] (ustore)-|(usq);
\draw [thick,->] (usq)--(udiv);
\draw [thick,->] (udiv)--(fsub) node [at end, right, yshift=1mm] {\scriptsize$-$};
\draw [thick,->] (lstore)-|(fsub) node [at end, right, yshift=-1mm] {\scriptsize$+$};
\draw [thick,->] (fsub)--(fdiv);
\draw [thick,->] (fdiv)--(result);

\end{tikzpicture}
\caption{Liukuvan ikkunan käyttö varianssin laskennassa.
Kuvassa on esitetty summamuuttujien päivitys sekä varianssin laskenta summien arvoista yksittäiselle pikselille.
Todellisuudessa summat tallennetaan taulukoihin, eli algoritmissa on kaksi summamuuttujaa jokaista pikseliä varten.}
\label{fig_sliding}
\end{figure}

Saavutettu etu on se, että laskutoimitusten määrä ei riipu puskurin pituudesta.
Liukuvaa ikkunaa käytettäessä tarvitaan jokaista kuvaa varten $2 \cdot w \cdot h \cdot c$ kertolaskua.
Lisäksi jokaista keskihajonnan laskentakertaa kohti tarvitaan vielä $w \cdot h \cdot c$ kertolaskua ja $2 \cdot w \cdot h \cdot c$ jakolaskua.
Laskutoimitusten määrää on verrattu perustoteutukseen taulukossa \ref{tbl_dev_compare}.

\begin{table}[htb]
\centering
\caption{Keskihajonnan laskemiseen tarvittujen laskutoimitusten eri määrillä
kuvia. Perustoteutus on kohdassa \ref{dev_math} esitetty kaava, ja liukuva
ikkuna kohdan \ref{dev_sliding} kaava. Neliöjuurta ei kohdan \ref{dev_sqrt}
mukaisesti lasketa.}
\label{tbl_dev_compare}
\begin{tabular}{rrrp{1cm}rr}
\toprule
&\multicolumn{2}{c}{Perustoteutus}&&\multicolumn{2}{c}{Liukuva ikkuna}\\
Kuvia & Kerto & Jako && Kerto & Jako \\
{\small (kpl)} & \multicolumn{5}{c}{\small(laskutoimituksia per pikseli ja värikanava)} \\
\midrule
2 & 2 & 2 && 3 & 2 \\
3 & 3 & 2 && 3 & 2 \\
50 & 50 & 2 && 3 & 2 \\
\bottomrule
\end{tabular}
\end{table}

Kahden puskurin tapauksessa liukuvalla ikkunalla tarvitaan enemmän laskutoimituksia kuin kohdassa \ref{dev_math}.
Ero on pieni verrattuna algoritmin muiden osien viemään aikaan, joten erillistä toteutusta kahden puskurin erikoistapausta varten ei tehty.
Kun puskurien määrä on yli kolme, on liukuvan ikkunan menetelmä selvästi tehokkaampi.

Liukulukuja käytettäessä tässä menetelmässä esiintyy numeerisia ongelmia, sillä toisistaan vähennettävien summien ero on usein pieni \cite{anscombe}.
Liukulukujen tarkkuus riippuu luvun koosta, joten suurien summien tapauksessa tarkkuus ei välttämättä riitä summien erotuksen tarkkaan laskemiseen.
Erityisesti tästä syystä algoritmissa käytetään keskihajonnan laskennassa kokonaislukuja tarkkuuden säilyttämiseksi.

\section{Toteutustapa}
Keskihajonnan laskenta koostuu kahdesta osuudesta: summataulukoiden päivityksestä, sekä keskihajonnan laskemisesta niiden perusteella.
Summataulukoiden päivitys tehdään kuvan syötön yhteydessä ja keskihajonnan laskenta erillisessä funktiossa.

Keskihajonnan toteutuksessa hyödynnettiin pääosin samoja optimointeja kuin konvoluution tapauksessa: SSE2-käskykantaa, rinnakkaistusta ja erillistä käännöstä eri pikselityypeille.

\subsection{Lukualueet}
\addmglos{$s$}{Kokonaislukulaskennassa käytettävä skaalauskerroin konvoluution ja keskihajonnan yhteydessä.}
Pikseliarvot ovat 8-bittisiä, joten kaavan \ref{dev_sliding} sisältämät pikseliarvojen neliöt ovat 16 bittisiä.
Summataulukoille tarvittu tila puolestaan riippuu halutusta kuvapuskurien enimmäismäärästä.
Esimerkiksi pikseliarvojen summa 10 puskurista on enimmillään $10\cdot255=2550$, jonka tallennus vaatii 16 bittisen muuttujan.

Tehdyssä toteutuksessa pikseliarvojen summalle varataan 16 ja neliöiden summalle 32 bittiä.
Tällöin puskurien enimmäismäärä on $2^{16} / 2^8 = 256$.
Bittileveydet eri kohdissa laskentaa on esitetty kuvassa \ref{fig_devbits}.
Puskurien enimmäismäärää voitaisiin tarvittaessa nostaa käyttämällä suurempaa bittileveyttä summamuuttujissa.

\begin{figure}[htb]
\centering
\begin{tikzpicture}
[sq/.style={draw,rectangle,minimum size=7mm,font=\small,inner sep=0mm},
 sumreg/.style={draw,rectangle,minimum size=7mm,font=\small,minimum width=2cm},
 sum/.style={draw,circle,minimum size=7mm,inner sep=0mm},
 div/.style={draw,regular polygon, regular polygon sides=3,minimum size=11mm,font=\small,inner sep=-2mm},
 node distance=1cm]

\node (input) {Pikseliarvot};
\node [sq, below=of input] (inputsq) {$x^2$};
\node [div, shape border rotate=-90, right=of inputsq] (inputmul) {$n$};
\node [sumreg,right=of inputmul] (usum) {$\sum x^2$ {\scriptsize (32 b)}};
\node [sumreg,node distance=2.7cm,above=of usum] (lsum) {$\sum x$ {\scriptsize (16 b)}};
\node [sq,right=of lsum] (outputsq) {$x^2$};
\node [sum, below=of outputsq] (outputsub) {$-$};
\node [div, shape border rotate=-90, right=of outputsub] (outputdiv) {$\frac{s}{n}$};
\node [right=of outputdiv] (result) {Varianssi};

\draw [thick,->] (input)--(inputsq) node [midway,right] {\scriptsize 8 b};
\draw [thick,->] (input)|-(lsum) node [near start,right] {\scriptsize 8 b};
\draw [thick,->] (inputsq)--(inputmul) node [midway,auto] {\scriptsize 16 b};
\draw [thick,->] (inputmul)--(usum) node [midway,auto] {\scriptsize 32 b};
\draw [thick,->] (lsum)--(outputsq) node [midway,auto] {\scriptsize 16 b};
\draw [thick,->] (outputsq)--(outputsub) node [near end,left] {\scriptsize 32 b};
\draw [thick,->] (usum)-|(outputsub) node [near end,left,yshift=4mm] {\scriptsize 32 b};
\draw [thick,->] (outputsub)--(outputdiv) node [midway,auto] {\scriptsize 32 b};
\draw [thick,->] (outputdiv)--(result) node [midway,auto] {\scriptsize 16 b};

\end{tikzpicture}
\caption{Lukujen bittileveydet eri kohdissa varianssilaskentaa.
Kertominen ja jakaminen $n$:llä on kuvassa esitetty kohdassa \ref{dev_div} kuvatulla tavalla.}
\label{fig_devbits}
\end{figure}

Varianssin tulos voi sisältää murtolukuosan, vaikka itse pikseliarvot ja välitulokset ovat kokonaislukuja.
Kokonaislukulaskennassa tämä esitetään skaalauskertoimen avulla.
Skaalauskertoimen valintaa varten tarvitaan varianssin suurin mahdollinen arvo,
joka saadaan Popoviciuksen epäyhtälöstä \cite{bhatia}.
Koska pikseliarvojen vaihteluväli on $[0, 255]$, on varianssi enimmillään $128^2$.
Valitsemalla skaalauskertoimeksi $s = 4$, tulos mahtuu 16-bittiseen muuttujaan.

\subsection{Summien kerääminen}
Summien päivitys suoritetaan konvoluutiolaskennan yhteydessä.
Ennen konvoluution laskemista summataulukoista vähennetään vanhin puskurissa oleva kuva, eli kuva jonka päälle konvoluutio kirjoittaa uuden kuvan.
Konvoluution jälkeen suodatettu kuva summataan vastaavasti taulukkoihin.

Kumpikin operaatio suoritetaan rivi kerrallaan samalla funktiolla.
Päivitysfunktio ottaa parametrin, joka määrää vähennetäänkö vai lisätäänkö annettu kuva summaan.

Laskenta suoritetaan SSE2-käskykannalla 16 tavun lohkoissa.
Ladatut pikseliarvot laajennetaan 16- tai 32-bittisiksi, kerrotaan neliösumman tapauksessa itsensä kanssa ja lisätään vastaaviin kohtiin summataulukossa.

Neliösumman tapauksessa jokainen lohko joudutaan käsittelemään neljässä vaiheessa, sillä 32-bittisiä lukuja mahtuu rekisteriin vähemmän kuin 8-bittisiä.
Lohko kannattaa kuitenkin ladata kerralla ja jakaa sitten useaan käsiteltävään osaan, sillä näin tarvitaan vähemmän muistioperaatioita.

\subsection{Jakaminen puskurien määrällä}
\label{dev_div}
Kaavassa \ref{dev_sliding} on kaksi jakolaskua puskurien määrällä.
Yksinkertaisimmin kokonaislukutarkkuuteen päästäisiin, kun kumpikin jakolasku suoritettaisiin erikseen kertomalla puskurien määrän käänteisluvulla.
Parempi tarkkuus saavutetaan kuitenkin suorittamalla jakolaskut vasta laskennan lopuksi.
Ero on siis siinä, tuleeko esimerkiksi joukon $\{0,0,1\}$ varianssiksi $0$ vai $0.33$.

Toteutuksessa kaavan \ref{dev_sliding} laskentajärjestystä on muokattu seuraavasti:
\begin{equation}
\sigma(x, y)^2 = \frac{s}{n^2}\; \bigg[ 
\Big(\underbrace{\sum_{i} n \cdot g_i(x, y)^2}_{\longbracetext{32-bittinen summa}}\Big) -
\Big(\underbrace{\sum_{i} g_i(x, y)}_{\longbracetext{16-bittinen summa}}\Big)^2 \bigg]
\label{dev_impl}
\end{equation}
Erona on summattavien arvojen kertominen $n$:llä ensimmäisessä summassa, jolloin lopuksi voidaan jakaa koko tulos $n^2$:lla.
Kertominen suoritetaan ennen summausta, sillä tällöin luvut ovat vielä 16-bittisiä ja siten nopeampi kertoa.

Tämän muutoksen vaikutus nopeuteen on pieni, mutta todennäköisesti negatiivinen.
Ennen summausta tehtävä kertolasku suoritetaan liukuvan ikkunan vuoksi kahdesti jokaiselle pikselille, kun taas summan ulkopuolella se tarvitsisi laskea kerran.
Tässä toteutuksessa summan ulkopuolella tarvittaisiin kuitenkin 32-bittinen kertolasku, joka olisi hitaampi kuin kaksi 16-bittistä.
Jatko-optimoinnissa voitaisiin arvioida, riittääkö varianssille kokonaislukuarvo.
Tällöin jälkimmäisen summan voisi jakaa suoraan $n$:llä 16-bittisen kertolaskun avulla.

\subsection{Varianssin laskenta summista}
Varianssia laskettaessa summat käsitellään neljän luvun lohkoissa, koska 128-bittiseen SSE2-rekisteriin mahtuu neljä 32-bittistä lukua.
Konvoluution yhteydessä kerätyt summat ladataan muistista, ja 16-bittinen summa kerrotaan itsellään neliöönkorotusta varten.
Nämä 32-bittiset termit vähennetään toisistaan.

Tulos täytyy vielä jakaa puskurien määrän neliöllä.
Tämä tehdään kertomalla käänteisluvulla $2^{32} \cdot s/n^2$, jonka arvo on laskettu silmukan ulkopuolella.
Kerrottavat luvut ovat 32-bittisiä ja kertolaskun tulos siten 64-bittinen, mutta tuloksesta jätetään 32 alinta bittiä pois.
SSE2-käskykannassa ei ole suoraan sopivaa 32-bittistä kertolaskua, joten tässä käytetään 16-bittisiä kertolaskuoperaatioita kuvan \ref{fig_mult32} mukaisesti.

\begin{figure}[htb]
\centering
\begin{tikzpicture}
[x = 2cm, y = 0.7cm,
 value16/.style={draw, minimum width=1.9cm, minimum height=0.6cm},
 value32/.style={draw, minimum width=3.9cm, minimum height=0.6cm}
]
\path [yshift=1mm]
node [text width=10em] at (2.5,-0.5) {32-bittiset operandit\\ kertolaskulle}
node [value16] at (-1,0) {A {\scriptsize (16 b)}}
node [value16] at (0,0) {B {\scriptsize (16 b)}}
node [value16] at (-1,-1) {C {\scriptsize (16 b)}}
node [value16] at (0,-1) {D {\scriptsize (16 b)}}
node at (-3.75,-1) {$\times$};

\draw [thick, yshift=0.5mm] (-4,-1.5) -- (0.5,-1.5);

\path [fill=black!5] (-2.5,-1.5) rectangle (-1.5,-6.7);

\node [value32] at (-0.5,-2) {B$\times$C {\scriptsize (32 b)}};
\node [value32] at (-1.5,-3) {A$\times$D {\scriptsize (32 b)}};
\node [value32] at (-1.5,-4) {C$\times$B {\scriptsize (32 b)}};
\node [value32] at (-2.5,-5) {A$\times$C {\scriptsize (32 b)}};

\node at (-3.75,-5) {$+$};

\draw [thick, yshift=-0.5mm] (-4,-5.5) -- (0.5,-5.5);

\path [yshift=-1mm]
node [text width=10em] at (2.5, -5.75) {Kertolaskun\\64-bittinen tulos}
node [value16] at (-3,-6) {0}
node [value16] at (-2,-6) {Tulos {\scriptsize (16 b)}}
node [value32] at (-0.5,-6) {Merkityksetön};

\end{tikzpicture}
\caption{Koska SSE2-käskykannassa ei ole viimeisessä jakolaskussa tarvittavaa 32-bittistä kertolaskua, täytyy se suorittaa 16-bittisten kertolaskujen avulla.
Tarkoitus on jakaa kertolaskun tulos $2^{32}$:lla, jolloin tuloksesta jätetään pois alimmat 32 bittiä.}
\label{fig_mult32}
\end{figure}

\subsection{Tulosten käsittely}
Yleisimmässä käyttötapauksessa ohjelma ei tarvitse koko keskihajontataulukkoa, vaan tietyn prosenttipisteen arvoista tai valitun kynnysarvon ylittävien pikselien määrän.
Nämä tulokset kannattaa laskea suoraan keskihajonnan laskevassa silmukassa, jotta tuloksia ei tarvitse välissä tallentaa erilliseen taulukkoon.

Toteutuksessa keskihajonnan laskevalle funktiolle annetaan osoittimet tulosmuuttujiin, joita on kolme: prosenttipisteen arvo, kynnysarvon ylittävä lukumäärä ja koko taulukko keskihajonnoista.
Nollaosoittimen \code{NULL} antamalla voidaan tarpeettomat tiedot jättää laskematta.

\subsection{Rinnakkaistus}
Rinnakkaistus tapahtuu pääosin vastaavasti kuin konvoluution yhteydessä, eli jakamalla kuva vaakasuoriin osiin.
Ainoa ero on siinä, että kynnysarvolaskurista ja prosenttipisteen laskentaan käytetystä taulukosta täytyy olla jokaiselle säikeelle erillinen kopio.
Muussa tapauksessa säikeet saattaisivat päivittää laskuria yhtäaikaisesti, aiheuttaen väärän tuloksen.
Erilliset muuttujat summataan yhteen laskennan lopuksi.

\subsection{Rajoitukset}
Keskihajonta lasketaan yhdelle värikanavalle kerrallaan.
Tällöin kuva joudutaan käymään kolmesti läpi kaikkien kanavien laskemiseksi, mikä hidastaa laskentaa hieman.
Aiempi prototyyppi on tukenut pelkkiä harmaasävykuvia, joten kanavat haluttiin pitää erillään kunnes tulosten käsittelytapa on selvinnyt.
Mahdollisesti jatkokehityksessä kanavien käsittely yhdistetään, jolloin keskihajonnan prosenttipiste ja muut tulokset lasketaan kaikkien kanavien yhdistetyistä tuloksista.

