5. sada domácich zadaní

Najneskorší termín odovzdania: 1.4.2012 (nedeľa) o 21:00
Odovzdávané súbory: AnalyzatorSkenu.java

Doplňujúce požiadavky:

  • riešenia, ktoré nebude možné skompilovať (t.j. riešenia so syntaktickými chybami) nebudú hodnotené,
  • zdrojový kód správne naformátujte (CTRL+SHIFT+F),
  • komentovaný zdrojový kód

Bunka sem, bunka tam (8 bodov)

Zuzana je študentkou medziodborového štúdia biológia-informatika na UPJŠ. A ako to už býva pri štúdiu biológie, práci v labákoch sa žiaden študent nevyhne. Zuzana nie je žiadnou výnimkou. Pri jednej takej práci v laboratóriu im ich vyučujúci ukázal experiment, na ktorom práve robia. V rámci experimentu študujú pohyb a delenie akýchsi buniek (Zuzana je ešte len prváčka, takže to, o aké bunky ide, veľmi "nerieši"). Samozrejme na UPJŠ nie je núdza ani o modernú laboratórnu techniku. A tak biológovia na tie bunky nepozerajú bežným mikroskopom ale rovno digitálnym mikroskopom. Taký digitálny mikroskop si môžete predstaviť ako digitálny fotoapárat s veľmi kvalitným "zoomom" - umožňuje vyfotografovať či nakamerovať mikroskopické zábery. Aby bolo bunky lepšie vidieť, biológia si dopomáhajú rôznymi fluorescenčnými látkami alebo ožarovaním (preto sú bunky tak "do zelena"). Ale k veci. Vyučujúci poprosil Zuzanu a jej spolužiakov o pomoc pri vyhodnocovaní experimentu. Biológovia majú kopu záberov z mikroskopu, no pre samotné vyhodnotenie by potrebovali vedieť, koľko buniek je jednotlivých záberoch (keďže čas vzniku záberov poznajú, môžu tak určiť rýchlosť delenia buniek a jej závislosť od okolitých podmienok). Zuzana aj spolužiaci súhlasili (veď kto by sa nechcel podieľať na takomto experimente) - a začali bunky na záberoch rátať. S každým ďalším záberom a každou ďalšou bunkou ich nadšenie (exponenciálne?) klesalo... :-(

Ako také zábery vyzerajú, si môžete pozrieť na tomto obrázku:

Ďalšie mikroskopické zábery buniek nájdete v súbore: SkenyBuniek.zip (aj s počtom buniek, ktoré študenti na zábere napočítali)

A vtedy si Zuzana uvedomila: veď ja viem predsa programovať! PAZ1a má za sebou, PAZ1b je ešte otvorené, no už všelijaké algoritmy a údajové štruktúry pozná. A nebolo by skvelé, ak by kolegom biológom ukázala, že informatike sa dnes už nevyhne asi žiaden vedecký odbor? Čo tak naprogramovať program, ktorý by zrátal počet buniek na zábere z digitálneho mikroskopu?

Obrázkov na spracovanie je veľa, niektoré sú oveľa väčšie ako tie na obrázku. Zuzana je však znalá problematiky efektívnosti algoritmov. Rozhodla sa preto napísať program čo najefektívnejšie - v ideálnom prípade tak, aby pracoval v lineárnom čase (lepšie sa nedá, lebo na každý pixel obrázka sa musíme pozrieť aspoň raz).

Zadanie

Upravením triedy AnalyzatorSkenu vytvorte triedu na analýzu záberov buniek z digitálneho mikroskopu. Kód analyzujúci vstupný obrázok umiestnite do metódy analyzuj. Metódu pocetBuniek upravte tak, aby vrátila počet buniek na obrázku. Pri analýze použitie tieto skutočnosti:

  • metóda jePixelBunky vám povie, či pixel na zadaných súradniciach je pixelom bunky - využíva sa to, že obrázok je predspracovaný a pozadie má čiernu farbu,
  • rozmery načítaného obrázka sú v inštančných premenných sirka a vyska; platný rozsah indexov pre pixely v x-ovej súradnici je teda 0, ..., (sirka-1) a y-ovej súradnici je 0, ..., (vyska-1),
  • žiadne dve bunky sa nedotýkajú (ak niečo vyzerá ako 2 zlepené bunky, tak to je v skutočnosti jedna bunka, ktorá je práve v procese delenia)
  • pozor, bunka sa môže dotýkať aj niektorej zo strán obrázku a nemusí ju byť teda vidiet celú.

Trieda AnalyzatorSkenu:

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;

public class AnalyzatorSkenu {

        /**
         * Naskenovany obrazok
         */

        private BufferedImage obrazok;

        /**
         * Sirka nacitaneho obrazka
         */

        private int sirka;

        /**
         * Vyska nacitaneho obrazka
         */

        private int vyska;

        /**
         * Hustota buniek - podiel plochy pokrytej bunkami k ploche skenu
         */

        private double hustotaBuniek;

        /**
         * Vytvori novy analyzator skenu buniek a spusti zakladnu analyzu
         *
         * @param nazovSuboru
         *            nazov suboru so skenom buniek
         */

        public AnalyzatorSkenu(String nazovSuboru) {
                try {
                        obrazok = ImageIO.read(new File(nazovSuboru));
                        sirka = obrazok.getWidth();
                        vyska = obrazok.getHeight();
                } catch (IOException e) {
                        System.err.println("Subor " + nazovSuboru
                                        + " sa nepodarilo nacitat.");
                }

                analyzuj();
        }

        /**
         * Vrati, ci je pixel na suradniciach [x, y] pixelom bunky.
         */

        private boolean jePixelBunky(int x, int y) {
                Color pixel = new Color(obrazok.getRGB(x, y));
                return !pixel.equals(Color.black);
        }

        /**
         * Vypocita hustotu buniek
         */

        private void vypocitajHustotuBuniek() {
                int pocetBunkovychPixelov = 0;
                for (int y = 0; y < vyska; y++)
                        for (int x = 0; x < sirka; x++)
                                if (jePixelBunky(x, y))
                                        pocetBunkovychPixelov++;

                hustotaBuniek = pocetBunkovychPixelov / ((double) sirka * vyska);
        }

        /**
         * Realizuje zakladnu analyzu obrazka so skenom buniek
         */

        private void analyzuj() {
                vypocitajHustotuBuniek();
                // ???
                // ... dalsia analyza obrazku spustena po nacitani skenu
        }

        /**
         * Vrati pocet buniek na obrazku
         */

        public int getPocetBuniek() {
                // ???
                return 0;
        }

        /**
         * Vrati hustotu buniek (pomer bunkovych pixelov k vsetkym pixelom)
         */

        public double getHustotaBuniek() {
                return hustotaBuniek;
        }

        public static void main(String[] args) {
                AnalyzatorSkenu analyzator = new AnalyzatorSkenu("cellscan328371.png");
                System.out.println("Hustota buniek: "
                                + (analyzator.getHustotaBuniek() * 100) + " %");

                System.out.println("Pocet buniek: " + analyzator.getPocetBuniek());
        }
}

V triede AnalyzatorSkenu nájdete aj metódu vypocitajHustotuBuniek, ktorá demonštruje princíp práce s obrázkom ako aj použitie metódy jePixelBunky. Táto metóda do inštančnej premennej hustotaBuniek vypočíta pomer počtu pixelov, ktoré zachycujú nejakú bunku, k celkovému počtu pixelov obrázka. Inými slovami hustotaBuniek určuje, aké percento plochy obrázka je pokryté bunkami.