﻿<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/examples.screen.xsl" media="screen"?>
<examples chapter="Korelační/konvoluční filtry" directory="grafika.3">

  <use-math/>

  <style>
    center > table { position: relative; display: inline-block; border-style: hidden; vertical-align: middle; }
    center > table td { width: 15px; height: 15px; text-align: center; }
    table.slim { border-collapse: collapse; border-spacing: 0; }
        table.slim td { /*border: 1px solid black;*/ }
    table.filtr { background-color: lightgreen; border-color: black; color: darkgreen; }
    img { display: block; margin: auto; }
  </style>

  <note>
      Jsou-li masky/matice/filtry symetrické, není mezi konvolučními a korelačními rozdíl.
  </note>

  <theory>
      <p>
          Při aplikaci filtru jde o to, že pro daný pixel se vypočítá nová hodnota na základě hodnot některých okolních pixelů, případně i jeho vlastní hodnoty. Například pro klasický gradientní Sobelův filtr zvýrazňování vertikálních hran $(\table -1, 0, 1; -2, 0, 2; -1, 0, 1)$ bude nová hodnota pixelu <em>e</em> určena následujícím způsobem:
      </p>
      <center>
          <table border="1" class="slim filtr" style="">
              <tr><td>-1</td><td>0</td><td>1</td></tr>
              <tr><td>-2</td><td>0</td><td>2</td></tr>
              <tr><td>-1</td><td>0</td><td>1</td></tr>
          </table>
          aplikován na
          <table border="1" class="slim" style="">
              <tr><td></td><td></td><td></td><td></td><td></td></tr>
              <tr><td></td><td>a</td><td>b</td><td>c</td><td></td></tr>
              <tr><td></td><td>d</td><td>e</td><td>f</td><td></td></tr>
              <tr><td></td><td>g</td><td>h</td><td>i</td><td></td></tr>
              <tr><td></td><td></td><td></td><td></td><td></td></tr>
          </table>
          se počítá jako
          <table border="1" class="slim" style="">
              <tr><td></td><td></td><td></td><td></td><td></td></tr>
              <tr><td></td><td>a</td><td>b</td><td>c</td><td></td></tr>
              <tr><td></td><td>d</td><td>e</td><td>f</td><td></td></tr>
              <tr><td></td><td>g</td><td>h</td><td>i</td><td></td></tr>
              <tr><td></td><td></td><td></td><td></td><td></td></tr>
          </table>
          <table border="1" class="slim filtr" style="left: -78px; opacity: 0.8;">
              <tr><td>-1</td><td>0</td><td>1</td></tr>
              <tr><td>-2</td><td>0</td><td>2</td></tr>
              <tr><td>-1</td><td>0</td><td>1</td></tr>
          </table>
          <br/>
          čili
          $-1*a + 0*b + 1*c - 2*d + 0*e + 2*f - 1*g + 0*h + 1*i$
      </center>
      <p>
          Je zjevné, že nemá-li filtr zrovna rozměr 1×1, znamená to, že krajní řádky a sloupce obrázku nemusí býti pokryté a musí se tudíž řešit zvlášť. Nejčastěji se používají čtvercové filtry s lichými rozměry (symetričnost a nejsnažsí aplikace) a „problematická“ místa se řeší několika různými způsoby:
      </p>
      <ul>
          <li>
              krajní sloupečky a řádky se jednoduše oříznou a výstupní obrázek je tak o fous menší (což použijeme v zájmu zjednodušení my);
          </li>
          <li>
              cyklické navázání okrajů – snadno se počítá (modulo na rozměr), ale zanáší do pixelu většinou naprosto nesouvisející informaci z protější strany;
          </li>
          <li>
              doplnění nulami – podle typu filtru může zanést chybu malou, ale také větší.
          </li>
      </ul>
      <p>
          Co se hledání (a případně následně zvýrazňování) hran týká, je myšlenka velmi jednoduchá: V černobílém obrázku je svislá hrana zjevně tam, kde jsou nalevo a napravo od daného pixelu přesně opačné barvy (a podobně pro hrany vodorovné či šikmé). Matematicky to v nejjednodušším případě vlastně znamená, že aplikujeme-li na daný pixel například filtr $(\table -1, 0, 1)$, dá stejná barva na obou stranách pixelu nulu (záporná a kladná složka se navzájem odečtou; samozřejmě za předpokladu, že suma koeficientů filtru je sama nulová!) zatímco nestejná nikoli. Sofistikovanější filtry pak pouze započítávají vliv bližšího a vzdálenějšího okolí s různými vahami.
      </p>
      <p>
          Je zřejmé, že hrany se počítají buď pro každou barvovou složku zvlášť, nebo pro obrázek převedený do odstínů šedi. Pro detekci hran použijeme druhou variantu, pro zostření obrazu první. Pracovat budeme nad naším starým známým testovacím obrázkem <img src="_files/kvetina/kvetina.jpg" width="500" height="375" alt="květina" style="vertical-align: top;"/>.
      </p>
      <p>
          PS: Jsou-li k práci potřeba nějaké další rovnice, jsou uvedeny v nápovědě.
      </p>
  </theory>


  <example>
    <text>
        Za pomoci výše uvedené teorie vyznačte ve vstupním obrázku ibišku svislé hrany <img src="grafika.3/gradient.svisle.jpg" width="498" height="373" alt="květina - svislé hrany" style="vertical-align: top;"/> Sobelovým filtrem $(\table -1, 0, 1; -2, 0, 2; -1, 0, 1)$.
    </text>
    <hint>
        Pro převod vstupního obrázku do ostínů šedi použijte opět zjednodušený <em>luma</em>-převod, tedy $Y^'_{601} = 0.299 * R^' + 0.587 * G^' + 0.114 * B^'$.
    </hint>
  </example>

  <example>
    <text>
        Zopakujte si předchozí zadání, použijte však Sobelův filtr pro hrany vodorovné $(\table -1, -2, -1; 0, 0, 0; 1, 2, 1)$: <img src="grafika.3/gradient.vodorovne.jpg" width="498" height="373" alt="květina - vodorovné hrany" style="vertical-align: top;"/>
    </text>
  </example>

  <example>
    <text>
        Ke zjišťování hran ve všech směrech najednou (nebo spíše ke zvýraznění každého bodu) slouží filtry založené na druhé derivaci, konkrétně třebas Laplaceův operátor $(\table 0, -1, 0; -1, 4, -1; 0, -1, 0)$ s výsledkem <img src="grafika.3/Laplace.1.jpg" width="498" height="373" alt="květina - aplikace Laplaceova operátoru" style="vertical-align: top;"/>
        <br/>
        případně $(\table -1, -1, -1; -1, 8, -1; -1, -1, -1)$ s výsledkem <img src="grafika.3/Laplace.2.jpg" width="498" height="373" alt="květina - aplikace Laplaceova operátoru" style="vertical-align: top;"/>
    </text>
  </example>

  <example>
    <text>
        Zostření obrazu se pak provádí tak, že výsledek aplikace vhodně zvoleného filtru pro zvýraznění hran se přičte k obrazu původnímu (samozřejmě s ohledem na barevné složky a případné přetečení ve světlých místech).
        <br/>
        Jelikož kopii původního obrazu vlastně můžeme získat aplikací filtru $(\table 0, 0, 0; 0, 1, 0; 0, 0, 0)$, dá se sečtení nahradit aplikací filtru jediného. Například pro druhou variantu Laplaceova operátoru z předchozího příkladu jde o rovnici s výsledným filtrem
        $$(\table -1, -1, -1; -1, 8, -1; -1, -1, -1) + (\table 0, 0, 0; 0, 1, 0; 0, 0, 0) = (\table -1, -1, -1; -1, 9, -1; -1, -1, -1)$$
        výsledek jehož aplikace jest: <img src="grafika.3/ostreni.1.jpg" width="498" height="373" alt="květina zostřená" style="vertical-align: top;"/>
    </text>
    <hint>
        Sečtené hodnoty mohou pro výsledný pixel přetéct povolený rozsah použitého obrazového formátu, ale zrovna v tomto případě jde o jednotky v bílých částech obrazu, takže stačí výsledek prostě na rozsah 0-255 oříznout pomocí funkce <em>numpy.clip()</em>.
    </hint>
  </example>

  <example>
    <text>
        Zajímavého efektu lze dosáhnout aplikací „negativního“ (nebo vlastně <a href="https://medium.com/@rajilini/laplacian-of-gaussian-filter-log-for-image-processing-c2d1659d5d2" class="external">„pozitivního“</a>) Laplaceova filtru
        $(\table 1, 1, 1; 1, -8, 1; 1, 1, 1) + (\table 0, 0, 0; 0, 1, 0; 0, 0, 0) = (\table 1, 1, 1; 1, -7, 1; 1, 1, 1)$
        <img src="grafika.3/ostreni.2.jpg" width="500" height="375" alt="květina X" style="vertical-align: top;"/>
    </text>
  </example>

  <example>
    <text>
        Zostření obrazu již začíná být poměrně náročnou operací – uvedená filtrační maska se aplikuje (prakticky) na všechny pixely obrazu a to ještě pro každou barvovou složku zvlášť. Podle velikosti vstupního obrázku a rychlosti použitého počítače již čistý Python nemusí být vůbec vhodnou volbou pro výpočet. Nicméně za pomoci alternativních interpretrů jako jsou například <em>Numba</em> nebo <em>Cython</em>, lze s menšími či většími úpravami možno u Python'u zůstat.
        <br/>
        Zkuste program pro zostření přepsat za pomoci některého alternativního interpretru a porovnejte jeho rychlost s programem v čistém Python'u.
    </text>
    <hint>
        Uvedená problematika je rozpracována v přednášce <a href="/materialy/python/speed/example-1.xml">Zrychlování programů – filtrace obrázků</a>.
    </hint>
  </example>


</examples>
