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, Kladné číslo, Rand, Programátor, Barevný efekt, Next, Náhodné prostředí, Tmavý odstín, Random, Photos, Malé rozmazání, Kody

Určitě si přečtěte

Tesla chce změnit nákladní dopravu. Její elektrický náklaďák má ohromující parametry

Tesla chce změnit nákladní dopravu. Její elektrický náklaďák má ohromující parametry

** Tesla představila elektrický kamion ** Má obdivuhodný výkon i dojezd ** Prodávat by se měl už za dva roky

17.  11.  2017 | Vojtěch Malý | 162

30 počítačových brzd, které vám zpomalí Windows

30 počítačových brzd, které vám zpomalí Windows

Na webu najdete hromadu rad, jak zrychlit počítač a Windows. My jsme na to šli opačně a naopak jsme hledali činnosti, které ho nejvíce zpomalují. Toto je třicítka těch základních.

12.  11.  2017 | Jakub Čížek | 90

Elektronika, která nepotřebuje kabel ani baterii. Živí se rádiovým šumem

Elektronika, která nepotřebuje kabel ani baterii. Živí se rádiovým šumem

** Každá elektrická krabička má konektor pro napájení nebo baterii ** Jenže pozor, jednou by to tak nemuselo být ** Drobná elektronika se může živit rádiovými vlnami

14.  11.  2017 | Jakub Čížek | 15

Nejlepší notebooky do 10 tisíc, které si teď můžete koupit

Nejlepší notebooky do 10 tisíc, které si teď můžete koupit

** I pod hranicí desíti tisíc korun existují dobře použitelné notebooky ** Mohou plnit roli pracovního stroje i zařízení pro zábavu ** Nejlevnější použitelný notebook koupíte za pět a půl tisíce

16.  11.  2017 | Stanislav Janů | 52

Do 20 let nebude nikdo vlastnit auta, říká zkušený šéf několika automobilek

Do 20 let nebude nikdo vlastnit auta, říká zkušený šéf několika automobilek

** Bývalý šéf a expert z několika velkých automobilek se vyjádřil k budoucnosti tohoto průmyslu ** Do 20 let „nikdo“ nebude vlastnit auta ** Veškerá doprava bude řešená pomocí velkých logistických platforem

15.  11.  2017 | Karel Javůrek | 74


Aktuální číslo časopisu Computer

Otestovali jsme 5 HDR 4K televizorů

Jak natáčet video zrcadlovkou

Vytvořte si chytrou domácnost

Radíme s koupí počítačového zdroje