reklama

Staňte se programátorem: napište si malý Photoshop II

Bitmapový editor Paint.NET je napsaný v jazyku C#. Dnes vám ukážeme, jak snadno lze v tomto jazyku napsat základní fotografické efekty. Napište si s námi takový malý Photoshop.

Ani tento týden nesmí na Živě.cz chybět tak trochu jiný článek o programování v jazyku C#. Nebudeme vás učit programovat základy, zároveň ale chceme nabídnout něco i těm, pro které je vývoj aplikací naprosto cizím pojmem. V každém článku vám tedy velmi stručně představíme nejdůležitější části nějakého skutečně funkčního a použitelného programu. Ten pak najdete v zápatí společně s projektem pro vývojové prostředí Microsoft Visual C# 2008 Express Edition. I když tedy celý text přeskočíte, určitě vám doporučujeme si EXE program stáhnout a spustit, možná vás totiž příště bude zajímat, z čeho se asi skládá a jak funguje jeho zdrojový kód.

Před týdnem jste si mohli vyzkoušet základní operace s obrázky a unsafe kódem v drobném editoru, dnes si náš malý grafický program obohatíme o několik pokročilejších funkcí pro práci s obrázky a podíváme se nejen na některé grafické filtry, ale také na různé způsoby manipulace s obrázky.

Klepněte pro větší obrázek  Klepněte pro větší obrázek
Nabídku grafických filtrů dnes výrazně rozšíříme

Dnes tedy přijde řada na:

  • Rozmazání obrázku
  • Barevný šum
  • Úprava rozměrů jednoho či více obrázků
  • Nastavení jasu
  • Pootočení obrázků

Jako vždy představíme pouze kód konkrétního algoritmu, kompletní kód najdete v přiloženém ZIP balíku. Ten si můžete otevřít v novějších verzích vývojového prostředí Visual Studio a libovolně upravit podle svého gusta. Určitě si také všímejte komentářů přímo uvnitř kódu uvozených dvojicí lomítek. Napoví vám, co se zrovna v tomto místě děje.

Rozmazání obrázku

Pro vytvoření efektu rozmazání, musíme postupně projít jednotlivé pixely bitmapy a vložit je na novou náhodně vygenerovanou pozici v bitmapě. Maximální vzdálenost této pozice od původní pozice je představována argumentem této metody a představuje tedy míru rozmazání.

Klepněte pro větší obrázek  Klepněte pro větší obrázek  Klepněte pro větší obrázek
Efekt rozmazání (šumu) můžete skládat

unsafe void Rozmazani(int silaSumu)
{
 
// Uzavře bezpečně data bitmapy v paměti
  BitmapData bd = b.LockBits(new Rectangle(0,0,b.Width,b.Height),
                                                                    ImageLockMode.ReadWrite,
                                                                    PixelFormat.Format32bppArgb);
  // Generátor náhodných čísel
 
Random rand = new Random();
     for (int y =0; y<b.Height; y++)
    
{
            // Vybere pixel
            int* ukzt = (int*)((int)bd.Scan0 + y * bd.Stride);
            for (int x = 0; x< b.Width; x++)
            {
                   // Vygeneruje náhodnou x-ovou souřadnici, kam bude vybraný
                   // pixel umístěn
                   int x2 = Math.Max(Math.Min(b.Width-1,rand.Next(-silaSumu,silaSumu )+x),0);
                   // To samé, ale pro souřadnici y
                   int y2 =Math.Max(Math.Min(b.Height-1,rand.Next(-silaSumu,silaSumu )+y),0);
                   // Vytvoří ukazatel na pixel ležící na náhodně vygenerovaných
                   // souřadnicích x,y
                   int* ukzt2 = (int*)((int)bd.Scan0 + y2 * bd.Stride +x2*4);
                    // A uloží jej do bitmapy
                    Color c = Color.FromArgb(*ukzt);
                    *ukzt2 = c.ToArgb();
                    // Posune se na další pixel v řadě
                    ukzt++;
            
}
     }
     // Uvolní bity bitmapy z paměti
     b.UnlockBits(bd);
     pictureBox1.Image = (Image)b;
}

Barevný šum

Snad všudypřítomným filtrem v každém editoru je i vložení barevnho šumu. Pokud jej zkombinujete s filtrm pro odstíny šedit nebo sépia efektem, získáte efekt staré fotografie s vysokým ISO šumem.

Pro vytvoření barevného šumu musíme k jednotlivým odstínům R,G,B pixelů bitmapy připočítat náhodně vygenerované číslo ležící v intervalu -N až N, kde platí, že N< 255.

Klepněte pro větší obrázek
Barevný šum

unsafe void BarevnySum( int silaSumu)
{
       // Uzavře data bitmapy
       
BitmapData bd = b.LockBits(new Rectangle(0,0,b.Width,b.Height),
                                                                  ImageLockMode.ReadWrite,
                                                                  PixelFormat.Format32bppArgb);

       // Vytvoří generátor náhodných čísel
       Random rand = new Random();
       for (int y =0; y<b.Height; y++)
       
{
              int* ukzt = (int*)((int)bd.Scan0 + y * bd.Stride);
              for (int x = 0; x< b.Width; x++)
              {
                     Color c = Color.FromArgb(*ukzt);
                     // Náhodně pozmění barevné složky R,G,B vybraného pixelu
                     int r=Math.Max(Math.Min(255,(rand.Next(-silaSumu,silaSumu) + c.R)),0);
                     int g=Math.Max(Math.Min(255,(rand.Next(-silaSumu,silaSumu)+c.G)),0);
                     int bl=Math.Max(Math.Min(255,(rand.Next(-silaSumu,silaSumu) + c.B)),0);
                     Color c2 = Color.FromArgb(r,g,bl);
                     *ukzt = c2.ToArgb();
                     ukzt++;
              }
       }
       b.UnlockBits(bd);
       pictureBox1.Image = (Image)b;
}

Změna rozměrů bitmapy

Náš nový program umí si poradí i se změnou velikosti obrázku, zatím mu ale chybí zachování poměru stran. Specialitou je dávková možnost změny všech obrázků ve vybrané složce. A co je třeba udělat v praxi?

Chceme-li upravit rozměry obrázku, který je představován instancí třídy System.Drawing.Image, použijeme metodu GetThumbnailImage. Tato metoda jako první dva argumenty očekává čísla typu int představující nové rozměry obrázku. Třetím argumentem je delegát na metodu, která bude zavolána v případě, že změna rozměrů obrázku selže.

Klepněte pro větší obrázek
Dialog pro změnu velikosti obrázku

Image NastavRozmery(Image i)
{
       return i.GetThumbnailImage((int)numericUpDown1.Value,
                                                                      (int)numericUpDown2.Value,
                                                                      newImage.GetThumbnailImageAbort(PredcasneUkonceni),
                                                                      IntPtr.Zero);
}

// Pokud se vytváření miniatury nepovede, bude zavolána tato metoda:
bool PredcasneUkonceni()
{
       // Kód reagující na chybu.
       MessageBox.Show("Nepodarilo se zmenit rozmery obrázku","Chyba");
       return false;
}

Pro úpravu rozměrů obrázků umístěných v určité složce, použijeme tento kód:

// Metoda GetFiles třídy System.IO.Directory vrací pole typu string
// obsahující cesty k souborům ve složce, která je jejím argumentem:
foreach(string soubor in System.IO.Directory.GetFiles(textBox1.Text))
{
       // Změní rozměry jednotlivých obrázků ve složce
       Image img = NastavRozmery(Image.FromFile(soubor));
       // ..a uloží je do složky, jejichž cesta je vepsána
       // v ovládacím prvku textBox2:
       img.Save(textBox2.Text + @"\"+ Path.GetFileName(soubor));
}

Nastavení jasu obrázku

Pro nastavení jasu obrázku, musíme k jednotlivým položkám modelu R,G,B jednotlivých pixelů přičíst číslo v rozmezí -255 až 255. Přičteme-li záporné číslo, bude obrázek tmavší. Přiteme-li kladné číslo, obrázek bude světlejší.

Klepněte pro větší obrázek  Klepněte pro větší obrázek  Klepněte pro větší obrázek
Tmavý obrázek, původní obrázek a zesvětlený obrázek

unsafe public void NastavJas(int n)
{
       BitmapData bd = b.LockBits(new Rectangle(0,0,b.Width,b.Height),
                                                                      ImageLockMode.ReadWrite,
                                                                      PixelFormat.Format32bppArgb);

       for (int y =0; y<b.Height; y++)
       {
              int* ukzt = (int*)((int)bd.Scan0 + y * bd.Stride);
              for (int x = 0; x< b.Width; x++)
              {
                     Color c = Color.FromArgb(*ukzt);
                     
int r = Math.Min(Math.Max(0,c.R + n),255);
                     int g = Math.Min(Math.Max(0,c.G + n),255);
                     int bl =Math.Min(Math.Max(0,c.B + n),255);
                     Color c2 = Color.FromArgb(r,g,bl);
                     *ukzt = c2.ToArgb();
                     ukzt++;
              }
       }
       b.UnlockBits(bd);
       pictureBox1.Image = (Image)b;
}

Pootočení obrázku

K pootočení obrázku použijeme metodu RotateFlip třídy System.Drawing.Image, která jako argument očekává položku výčtového typu RotateFlipType udávající osu a úhel pootočení obrázku. Pokud máme obrázek uložený v instanci třídy Bitmap, explicitně jej překonvertujeme na instanci třídy Image.

Klepněte pro větší obrázek  Klepněte pro větší obrázek
Překlopený obrázek a otočená fotografie o devadesát stupňů

// Explicitně překonvertujeme instanci třídy Bitmap na Image:
Image i = (Image)b;
// Pootočí obrázek
i.RotateFlip(RotateFlipType.Rotate90FlipX);

Není vám něco jasné? Zeptejte se autora v diskuzi pod článkem

Manipulace s pixely je velmi intuitivní, na základě zde popsaných algoritmů si tak snadno můžete vytvořit svůj vlastní filtr. Zdrojový kód můžete upravit, vylepšit a rozšířit. Pak nám ho určitě pošlete na tento e-mail a my vaše úpravy zveřejníme v některém z příštích článků.

Pod každým článkem na Živě.cz najdete diskuzi čtenářů. Pakliže chcete položit technickou otázku autorovi, toto je ta nejlepší možnost, odpověď totiž uvidí i ostatní. Určitě ale napište i tehdy, pakliže máte nějaký zajímavý námět, který bychom měli zpracovat příště.

Nakonec nesmí chybět ani slíbený spustitelný program a projekt. K prvnímu budete potřebovat nainstalovaný novější Microsoft .NET Framework, pakliže si budete chtít projekt upravit a sestavit, budete potřebovat bezplatné vývojové prostředí Microsoft Visual C# 2008 Express Edition.

Témata článku: Software, Programování, Adobe Photoshop, Rand, Image, Next, Random

8 komentářů

Nejnovější komentáře

  • Jaroslav Šmíd 24. 12. 2008 14:11:51
    Ano, jste prase. Vite, proc je property Scan0 IntPtr a ne int? Ze by...
  • Michal Smrž 27. 8. 2008 21:28:35
    A tohle není zdarma? ;-)
  • jehoVista 27. 8. 2008 19:59:37
    Copak nejsou sofistikovanejsi reseni zdarma? S Imagemagickem provedete...
reklama
Určitě si přečtěte

UPC překopli páteřní kabel. V Brně i druhý den nejede internet ani kabelovka

UPC překopli páteřní kabel. V Brně i druhý den nejede internet ani kabelovka

** V Brně byl velký výpadek služeb UPC ** Důvodem je překopnutý páteřní kabel ** V některých lokalitách služby stále nefungují

5.  12.  2016 | Jakub Čížek | 104

17 expertek Microsoftu předpovědělo rok 2027. Splní se alespoň něco?

17 expertek Microsoftu předpovědělo rok 2027. Splní se alespoň něco?

** Zmizí klasické vyhledávače ** Budeme programovat buňky ** Kvantové počítače překonají šifry

6.  12.  2016 | Jakub Čížek | 36

11 tipů na dobrý stolní počítač: od základu po herní mašiny

11 tipů na dobrý stolní počítač: od základu po herní mašiny

** Postavte si stolní počítač! Máme pro vás 11 vzorových sestav s rozpisem komponent ** Většina tipů cílí na hráče, věnujeme se ale i základnímu PC a počítačům na střih videa ** Nadělte si nový počítač třeba pod stromeček

5.  12.  2016 | Adam Kahánek | 74

Nejlepší notebooky nad 20 tisíc: poradíme, které teď chcete

Nejlepší notebooky nad 20 tisíc: poradíme, které teď chcete

** V notebooku s cenou nad 20 tisíc nesmí chybět kvalitní displej a rychlé úložiště ** Za dalších deset tisíc můžete dostat navíc styl nebo výkonnější komponenty ** Vybírat můžete z různých velikostí i konstrukcí

8.  12.  2016 | Stanislav Janů | 85


reklama