HyperLink
Bejelentkezés
E-mail: 
Jelszó: 





Skip Navigation Links
 

Kép pixelenkénti módosítása


Példaprogram letöltése

111728 bájt

Képfeldolgozásnál, látványos effektek készítésénél az esetek többségében a képet pixelenként kell elérnünk és azok értékeit módosítanunk. Ha ehhez a Bitmap osztály GetPixel, SetPixel függvényeit használjuk, akkor még egy kisméretű képnél is igen időigényes művelet lesz az összes pixel elérése és módosítása.
Mellékelt példában egy olyan osztályt készítünk, melyet felhasználva egy tetszőleges kép esetén igen gyorsan elérhetjük és módosíthatjuk a kép pixeleit tetszés szerint.

A megoldáshoz a Bitmap közvetlen memóriabeli elérését fogjuk alkalmazni. Mivel a kép a memóriában úgy van tárolva, hogy a pixeleket leíró RGB értékek egymás után kapnak helyet, így ha kapunk egy mutatót, mely a kép első pixelére mutat, akkor egy ciklussal igen gyorsan végigmehetünk az összesen és módosíthatjuk azokat.
Mivel ez a fajta memória művelet nem biztonságos a .NET keretrendszer számára, így az ilyen műveleteket végző függvényeinket, osztályainkat az unsafe jelzővel kell ellátnunk.
  public unsafe class ManipulateBitmap
Ahhoz, hogy unsafe-el jelölt függvényeket futtathassunk, szükséges ezt engedélyeznünk is a projektünkben, különben az „Unsafe code may only appear if compiling with /unsafe” hibaüzenetet kapjuk. Ennek beállításához válasszuk a Solution Explorer-ben az adott projektünket, majd jobb gomb, Properties menüpont. A megjelenő ablakban a Configuration Properties - Build - Allow unsafe code blocks értékét állítsuk igazra.
Mivel egy pixelhez három színérték tartozik, melyek egy-egy bájtot foglalnak el a memóriában, így ezek egyszerűbb kezeléséhez hozzunk létre egy új struktúrát.
  public struct OnePixel
  {
    public byte blue;
    public byte green;
    public byte red;
  }
Osztályunk konstruktorában kell majd megadnunk a kezelni kívánt képet Bitmap formában. Ezt most csak tároljuk egy belső változóba a későbbi felhasználás végett.
  public ManipulateBitmap(Bitmap bmp)
  {
    bitmap = bmp;
  }
A Start függvény meghívásával kezdődhet el a képfeldolgozás. Itt egy kettős ciklus fog futni, mely végigmegy a kép összes során és minden sor összes pixelén. A két ciklus végére minden pixelen végighaladtunk. A ciklusmagban egy eseményt helyezünk el. Ezt az eseményt felhasználva módosíthatjuk majd a pixeleket. Ehhez azonban kell készítenünk egy új eseményt, illetve az ehhez szükséges delegate-et. Az eseménykezelőnek szeretnénk átadni az x, y koordinátát, melyből megtudható lesz, hogy melyik pixelnél is tart a feldolgozás, valamint ennek a pixelnek a színeit RGB értékre bontva. A színeket tartalmazó byte típusú paraméter előtt használjuk a ref módosítót, így a megváltoztatott érték rögtön vissza is kerül a memóriába.
public delegate void PixelDelegate(int x, int y, ref byte red, ref byte green, ref byte blue);
public event PixelDelegate Pixel;
Nézzük mi is történik a Start függvényben. Mivel a kép manipulálása Pixel eseményen keresztül történik, így ha ehhez nincs eseménykezelő rendelve, akkor nem is érdemes a függvényünknek tovább futnia. Ez esetben a függvény null értékkel tér vissza.
    public Bitmap Start()
    {
      if (Pixel!=null)
      {
Következő lépés az lesz, hogy zároljuk a memóriaterületet, hogy más folyamat ne férjen hozzá a képhez, amíg a változtatásokat végezzük rajta.
        BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
A kép bal felső sarkában lévő első pixelt a Scan0 property-n keresztül érhetjük el.
        OnePixel* pixel = (OnePixel*) bd.Scan0.ToPointer();
Most már adott egy mutató, mely a kép első pixelére mutat, így indíthatjuk a kettős ciklust, mely végigmegy a kép minden pixelén.
        for (int y = 0; y < bitmap.Height; y++)
        {
          for (int x = 0; x < bitmap.Width; x++)
          {
A ciklusmagban a Pixel nevű eseményt hívjuk meg, hogy az adott pixel esetleges változtatása elvégzésre kerüljön. Ezt követően a kép következő pixelére léptetjük a mutatónkat.
            Pixel(x, y, ref pixel->red, ref pixel->green, ref pixel->blue); 
            pixel++;
          }
        }
A ciklusok végén feloldjuk a kép memória területének a zárolását és visszaadjuk a már módosított Bitmap-et.
        bitmap.UnlockBits(bd);
        return bitmap;
      }
A ManipulateBitmap osztály felhasználása
A létrehozott osztályunk felhasználása a következő módon zajlik. Létrehozunk belőle egy új példányt, a konstruktorban megadunk egy Bitmap-et, majd hozzárendelünk egy eseménykezelőt a Pixel eseményhez és végül a Start függvénnyel elindítjuk a folyamatot.
      ManipulateBitmap mb = new ManipulateBitmap(new Bitmap(pictureBox1.Image as Bitmap));
      mb.Pixel+=new PixelDelegate(Pixel);
      pictureBox2.Image = mb.Start();      
A Pixel eseménynél tetszőleges módon megváltoztathatjuk az egyes pixelekhez tartozó RGB értékeket, ezzel befolyásolva a kép kinézetét.
Például egy színes képből úgy készíthetünk szürkeárnyalatos képet, hogy minden pixelénél kiszámítjuk a három színösszetevő átlagértékét, majd a kapott eredményt adjuk az színösszetevőknek.
    public void Pixel(int x, int y, ref byte red, ref byte green, ref byte blue)
    {
       byte v = (byte)((red + green + blue) / 3);
       red =  v;
       green = v;
       blue = v;
    }

Könyv
Ez a cikk megtalálható ebben a könyvben: C# Software Offline 2002 évkönyv 234. oldal

Felhasználási feltételek
A Software Online szoftverfejlesztői magazin mindegyik cikke, minden megjelent képe, és egyéb publikált anyaga szerzői jog védelme alatt áll! Bármilyen formában történő másodlagos terjesztésük, közzétételük vagy felhasználásuk kizárólag a kiadó előzetes írásbeli engedélyével történhet!

Copyright © 1999-2012 Animare Software Kft. Minden jog fenntartva!
| Készült: Animare Stúdió | Adatvédelem | Kapcsolat |