Po drátě 4: Řešení úlohy č. 12

Jak praví zadání, klasický způsob ukládání obrázku po řádcích je nuda. Existují zajímavější způsoby, například křivky pokrývající plochu. Taková křivka prochází bez přerušení každým bodem roviny. To je v počítačové grafice trošku jednodušší, protože máme konečný počet bodů, takže můžeme pokrýt plochu třeba křivkou tam-zpátky-tam-zpátky. Ale proč nepoužít něco obecnějšího.

Hilbertova křivka je jedna z nejjednodušších křivek pokrývajících plochu. Má nějaký základní tvar (písmeno U) a způsob, jak tenhle základní tvar rozdělit pomocí menších základních tvarů. V obrázku je nakreslena křivka po šesti krocích. K vyřešení je třeba křivku "dokončit" až na úroveň pixelů a ty pak podle té křivky uložit do nového obrázku tradičně zleva doprava, aby to nějaký prohlížeč dokázal zobrazit.

Výsledek vypadá takhle:
řešení hilberta

Vzorové řešení například takhle v pythonu (modul hilbert, který umí pracovat s Hilbertovou křivkou libovolné dimenze, ke stažení odsud):

from PIL import Image
import hilbert

i = Image.open('/tmp/hilbert.png')
o = Image.new('RGB', (512,512))

for x in xrange(512):
   for y in xrange(512):
       p = hilbert.int_to_Hilbert(x + 512 * y,2)
       px = i.getpixel((p[1], p[0]))
       o.putpixel((x,y), px)

o.save('/tmp/out.png')

Ostatně, i když nemáte po ruce ten správný modul, Hilbertovu křivku vygenerujete snadno:

int x=0, y=0, dx=0, dy=1;
#define R(r) do { int dx0=dx, dy0=dy; dx=-(r)*dy0; dy=(r)*dx0; } while(0)

void step(void)
{
  // Zpracuje pixel o souřadnicích (x,y)
  x += dx;
  y += dy;
}

void hilbert(int deg, int rot)
{
  if (!deg--)
    return;
  R(-rot);
  hilbert(deg, -rot);
  step();
  R(rot);
  hilbert(deg, rot);
  step();
  hilbert(deg, rot);
  R(rot);
  step();
  hilbert(deg, -rot);
  R(-rot);
}

Komentář

Původně úloha vypadala zcela jinak. Dostali byste pouze zcela nesmyslný obrázek jménem "hilbert.pcx" s komentářem "better compression through different scanline". Museli byste pak přijít na to, že scanline je Hilbertova křivka, a po převedení obrázku do "viditelné" podoby byste zjistili, že obrázek s Hilbertem je opravdu asi o 300 bajtů menší. (Například u GIFu už je komprese horší. Zřejmě jsou dobré důvody, proč se Hilbertova křivka jako scanline nepoužívá.)

Velmi záhy jsme si řekli, že to by bylo opravdu příliš drsné, a tak vzniklo tohle.

Úlohu vyrobil matějčík, ukázková řešení dodali Michal Čihař a Martin Mareš.

Re: Po drátě 4: Řešení úlohy č. 12 adino (18. 11. 2009 - 12:44) Sbalit(3)
No na tomto som pohorel. Ja som si myslel ze sa ako v ramci scanline pouzivali cele bloky 8x8. :) Pekne, naozaj velmi pekne. Nabuduce snad zacnem skor.
Re: Po drátě 4: Řešení úlohy č. 12 Tomas Cech (18. 11. 2009 - 13:45) Sbalit(2)
Jj, taky jsem to pochopil jinak. Nejdriv jsem si napsal v BASHi zelvu, ktera chodila po care a vytvorila tak permutaci ctverecku 8x8, pak jsem si jeste po sloupcich seradil gradient a skoncil jsem... Dobre maso.
Re: Po drátě 4: Řešení úlohy č. 12 vladimir.navrat (18. 11. 2009 - 22:26) Sbalit(1)
Ja jsem na to šel úplně stejně a pak jsem je ještě zkoušel otáčet podle směru křivky. Říkal jsem si: "přece nepůjdou na menší bloky." A to byla moje největší chyba.
Re: Po drátě 4: Řešení úlohy č. 12 s_tet_o (18. 11. 2009 - 13:54) Sbalit(1)
Tu som skončil tiež (žolík). Najprv som to rozkladal 8×8, potom som štvorčeky zmenšoval, ale asi som niekde vyrobil chybu, pretože rozklad po pixeloch k výsledku neviedol.
Re: Po drátě 4: Řešení úlohy č. 12 Pavel Machek (20. 11. 2009 - 10:39) Sbalit(1)
Bez wikipedie bych to nedal. Dlouho a marne jsem se snazil vyrobit cosi co by prochazelo hilberta...