Bitmap İşlemleri & Scanline

Yazdığınız makaleleri ve üyelerimizin işine yarayacağını düşündüğünüz kodlarınızı gönderebilirsiniz. Bu foruma soru sormayın!
Cevapla
Kullanıcı avatarı
Lost Soul
Üye
Mesajlar: 1064
Kayıt: 01 Nis 2007 02:55
Konum: mekan ANKARA toprak ELAZIĞ
İletişim:

Bitmap İşlemleri & Scanline

Mesaj gönderen Lost Soul »

Bu aralar da resim işleme ile ilgili projelerle çalışmam icab etti.
Bu çalışma boyunca bulduğum ve/veya yazdığım kodları burada paylaşacağım.
Şimdilik klasik bir kaç kullanım örneği yazacağım.

Genellikle resim üzerinde işlemler yapılırken pixel pixel tarama kullanılıryor (ki ben de onlardan birisiyim)
ancak pixel tarama ile ilgili yaptığınız işleri Scanline ile tekrar elden geçirin ve aradaki muazzam süre farkını görün.

1. Örneğimiz resmi siyah beyaz yapma
Aşağıdaki örnekle ilgili iki şey belirtmek istiyorum.
birincisi her ne kadar resimdeki renkleri siyah ve beyaz yapıyor ise de oluşturduğu resim 24 bitlik bir resimdir. Yani diğer renkler kullanılabilir.
ikincisi TColor olarak verdiğimiz Referans, bazı resimlerdeki açık renkleri atlamayı sağlama içindir.
Şöyleki krem rengi veya bazı nesnelere verilen hafif arkaplan rennkleri de beyaza yakın bir renktir ama resimde görülmez veya dikkat çekmezken siyah beyazda çirkin görüntülere sebep olabilir burada ReferenceColor deyimi üst limit olarak belirlenebilir.

Kod: Tümünü seç

Procedure ToMono(Bm: TBitmap; ReferenceColor : TColor = clWhite);
CONST
  PixelCountMax = 32768;
TYPE
  pRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = ARRAY [0 .. PixelCountMax - 1] OF TRGBTriple;
VAR
  Bmp: TBitmap;
  i: Integer;
  j: Integer;
  RowIn: pRGBTripleArray; // pByteArray;
  RowOut: pRGBTripleArray; // pByteArray;
  Cl: TColor;
  gr: Byte;

begin
  Bmp := TBitmap.Create;
  Bm.PixelFormat := pf24bit;
  try
    Bmp.Width := Bm.Width;
    Bmp.Height := Bm.Height;
    Bmp.PixelFormat := Bm.PixelFormat;
    FOR j := 0 TO Bmp.Height - 1 DO
    Begin
      RowOut := Bmp.Scanline[j];
      RowIn := Bm.Scanline[j];
      i := 0;
      while i <= Bmp.Width - (1) do
      Begin
        Cl := RGB(RowIn[i].rgbtRed, RowIn[i].rgbtGreen, RowIn[i].rgbtBlue);
        if cl < ReferenceColor then gr := 0 else gr := 255;
        RowOut[i].rgbtRed := gr;
        RowOut[i].rgbtGreen := gr;
        RowOut[i].rgbtBlue := gr;
        inc(i, 1);
      End;
    end;

    Bm.Assign(Bmp);
  finally
    Bmp.Free;
  End;
End;
Örnek Kullanımları

Kod: Tümünü seç

 
  ToMono(xResim.Picture.Bitmap, RGB(255,255,255){clWhite});
  ToMono(xResim.Picture.Bitmap, clWhite);
  ToMono(xResim.Picture.Bitmap);
  ToMono(xResim.Picture.Bitmap, RGB(200,200,200)); // daha temiz bir resim çıkar
2. Örneğimiz gri tonlama yapma

gri dediğimiz olay aslında RGB biçimindeki renklerin Red Green Blue tonlarının birbirine eşit veya çok yakın ollması olayıdır.
öyle ise

Kod: Tümünü seç

Function ColorToGrayTone(Color: TColor): Byte;
var
  l: LongInt;
  r, g, b: Byte;
Begin
  l := ColorToRGB(Color);
  r := Byte(l);
  g := Byte(l shr 8);
  b := Byte(l shr 16);
  Result := Round(r * 0.299 + g * 0.587 + b * 0.114)
End;
fonksiyonu ile bir rengin grideki tonunu hesaplayabilir ve

Kod: Tümünü seç

Function ColorToGrayScale(Color: TColor): TColor;
Var
  GT: Byte;
Begin
  GT := ColorToGrayTone(Color);
  Result := RGB(GT, GT, GT);
End;
ile de bu tonlamanyı RGB kanallarının üçüne de uygulayarak gri rengi elde edebiliriz.

son olarak Mono resim örneğimizi biraz değiştirerek gri bir resim elde edebiliriz.

Kod: Tümünü seç


Procedure ToGrayScale(Bm: TBitmap);
CONST
  PixelCountMax = 32768;
TYPE
  pRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = ARRAY [0 .. PixelCountMax - 1] OF TRGBTriple;
VAR
  Bmp: TBitmap;
  i: Integer;
  j: Integer;
  RowIn: pRGBTripleArray; // pByteArray;
  RowOut: pRGBTripleArray; // pByteArray;
  Cl: TColor;
  gr: Byte;

begin
  Bmp := TBitmap.Create;
  Bm.PixelFormat := pf24bit;
  try
    Bmp.Width := Bm.Width;
    Bmp.Height := Bm.Height;
    Bmp.PixelFormat := Bm.PixelFormat;
    FOR j := 0 TO Bmp.Height - 1 DO
    Begin
      RowOut := Bmp.Scanline[j];
      RowIn := Bm.Scanline[j];
      i := 0;
      while i <= Bmp.Width - (1) do
      Begin
        Cl := RGB(RowIn[i].rgbtRed, RowIn[i].rgbtGreen, RowIn[i].rgbtBlue);
        gr := ColorToGrayTone(Cl);
        RowOut[i].rgbtRed := gr;
        RowOut[i].rgbtGreen := gr;
        RowOut[i].rgbtBlue := gr;
        inc(i, 1);
      End;
    end;

    Bm.Assign(Bmp);
  finally
    Bmp.Free;
  End;
End;
3. Örneğimiz bir resmin yatay ve dikey çözünürlüğünü alma cm ve mm olarak çalışma

aşağıdaki örneğin orjinalinde real tipi kulllanılmaktaydı ancak daha hassas işlemler için extended olarak değiştirdim.
sebebine gelince

Kod: Tümünü seç

var
  a : extended;
  b : real;
begin
  a:= 1/6;
  b:= 1/6;
  Showmessage('Extended a :['+VarTostr(a) + '] = 1/6[' + VarToStr(1/6) + '] ? ' +  VarToStr(a=1/6) + #13#10 +
              'Real     b :['+VarTostr(b) + '] = 1/6[' + VarToStr(1/6) + '] ? ' +  VarToStr(b=1/6));
end;
sonucu a ve b ye 1 / 6 değerini atadığımızda a=1/6 sorusuna true çevabını alırken b=1/6 sorusunun cevabı falsedir.

Kod: Tümünü seç


Extended a :[0,166666666666667] = 1/6[0,166666666666667] ? True
Real     b :[0,166666666666667] = 1/6[0,166666666666667] ? False

Kod: Tümünü seç

Procedure GetDPI(Image: TBitmap; var DPIX, DPIY: Extended);
Var
  Temp, DPMX, DPMY: integer;
  Stream: TMemoryStream;

Begin
  Stream := TMemoryStream.Create;
  Image.SaveToStream(Stream);
  Stream.Seek($0E, soFromBeginning);
  Stream.Read(Temp, 4);
  If Temp = $28 then
  Begin
    Stream.Seek($26, soFromBeginning);
    Stream.Read(DPMX, 4);
    Stream.Read(DPMY, 4)
  End
else
Begin
  DPMX := 0;
  DPMY := 0
End;
Stream.Free;
DPIX := DPMX / 100 * 2.54;
DPIY := DPMY / 100 * 2.54
End;

peki DPI öğrendim de boyum mu uzadı?
Cevap evet. Şöyle ki DPI :Dot Per Inch olduğundan resimdeki 1 inchlik alana sığan dikey ve yatay nokt sayısından yola çıkarak.

Kod: Tümünü seç

CONST
    INCH_CM = 2.54;
    INCH_MM = 25.4;
    CM_INCH = 1/2.54;
    MM_INCH = 1 /25.4;

Kod: Tümünü seç

Function toDPMM(DPI : Extended) : Extended;
Begin
  Result := DPI / INCH_MM;
End;

Function toDPCM(DPI:Extended): Extended;
Begin
  Result := DPI / INCH_CM;
End;


Function toMMRect(aRect: TRect;VerticalDPI,HorizontalDPI : Extended): TRect;
Begin
  With Result do
  Begin
    Top    := Round(toDPMM(VerticalDPI)   *aRect.Top    );
    Bottom := Round(toDPMM(VerticalDPI)   *aRect.Bottom );
    Left   := Round(toDPMM(HorizontalDPI) *aRect.Left   );
    Right  := Round(toDPMM(HorizontalDPI) *aRect.Right  );
  End;
End;

Function toCMRect(aRect: TRect;VerticalDPI,HorizontalDPI : Extended): TRect;
Begin
  With Result do
  Begin
    Top    := Round(toDPCM(VerticalDPI)   *aRect.Top    );
    Bottom := Round(toDPCM(VerticalDPI)   *aRect.Bottom );
    Left   := Round(toDPCM(HorizontalDPI) *aRect.Left   );
    Right  := Round(toDPCM(HorizontalDPI) *aRect.Right  );
  End;
End;



ile ekrana 1 cm x 1 cm lik bir rectangle çizdireceğim zaman

Kod: Tümünü seç

  
var
  x,y: Extended;


GetDPI(xResim.Picture.Bitmap,x,y);
 xResim.Picture.Bitmap.Canvas.Rectangle(toMMRect( Rect(10,10,20,20),x,y));

ile yapabilirim ki bunu taranan bir dökümandaki çizimlerin mm cinsinden boyutunu hesaplama olarak da düşünebilirsiniz.

şimdilik bu kadar sevgi ve saygıılarımla.
neu84
Üye
Mesajlar: 307
Kayıt: 06 Oca 2011 11:27

Re: Bitmap İşlemleri & Scanline

Mesaj gönderen neu84 »

Kod: Tümünü seç

    Procedure ToMono(Bm: TBitmap; ReferenceColor : TColor = clWhite);
    CONST
      PixelCountMax = 32768;
    TYPE
      pRGBTripleArray = ^TRGBTripleArray;
      TRGBTripleArray = ARRAY [0 .. PixelCountMax - 1] OF TRGBTriple;
    VAR
      Bmp: TBitmap;
      i: Integer;
      j: Integer;
      RowIn: pRGBTripleArray; // pByteArray;
      RowOut: pRGBTripleArray; // pByteArray;
      Cl: TColor;
      gr: Byte;

    begin
      Bmp := TBitmap.Create;
      Bm.PixelFormat := pf24bit;
      try
        Bmp.Width := Bm.Width;
        Bmp.Height := Bm.Height;
        Bmp.PixelFormat := Bm.PixelFormat;
        FOR j := 0 TO Bmp.Height - 1 DO
        Begin
          RowOut := Bmp.Scanline[j];
          RowIn := Bm.Scanline[j];
          i := 0;
          while i <= Bmp.Width - (1) do
          Begin
            Cl := RGB(RowIn[i].rgbtRed, RowIn[i].rgbtGreen, RowIn[i].rgbtBlue);
            if cl < ReferenceColor then gr := 0 else gr := 255;
            RowOut[i].rgbtRed := gr;
            RowOut[i].rgbtGreen := gr;
            RowOut[i].rgbtBlue := gr;
            inc(i, 1);
          End;
        end;

        Bm.Assign(Bmp);
      finally
        Bmp.Free;
      End;
    End;
Rica etsem bu kod blogunda scanline ın nasıl çalıştıgını anlatırmısınız. Scanline ı ögrenmem lazım ancak anlayabilecegim türde anlatılmış bir kaynak bulamıyorum. scanline ile satırını okudugum bir resmin her pixelinde hangi renk oldugunu nasıl ögrenebilirim..pByteArray olara tanıtılmış olan degişkenler aracılıgıyla mı ulaşıcam pixelde hangi renk olduguna.. Lütfenn yardımmmm..
Kullanıcı avatarı
Lost Soul
Üye
Mesajlar: 1064
Kayıt: 01 Nis 2007 02:55
Konum: mekan ANKARA toprak ELAZIĞ
İletişim:

Re: Bitmap İşlemleri & Scanline

Mesaj gönderen Lost Soul »

Bitmap resmini 2 boyutlu bir dizi olarak gözünüzde canlandıırın.
Bu dizinin eleman sayısı Bitmap.Width( veya x) x Bitmap.Height( veya y) olsun.
Bu şekilde düşündüğümüzde bitmapın herbir y elemanı satır ve bu y elemanındaki her bir x satıra ait bir sütun diyecek olursak
Scanline fonksiyonu bitmapdaki belirlenen satırı (yani y numaralı satırdaki bütün x leri alır)
Yani aşağıdaki matris 256 renkli bir bitmap dizisi olsun

Kod: Tümünü seç

[FF,FF,FF,FF,FF,FF]
[0F,0F,0F,0F,0F,0F]
[00,00,00,00,00,00]
bu dizi 6(x)x3(y) boyutlu bir bitmapı temsil edecek olursa bu durumda scanline(0) dediğimizde
geri dönecek olan değer matrisin o numaralı satırı olan [FF,FF,FF,FF,FF,FF] olacaktır.

peki TRGBTripleArray tipi ne işe yarar.
bizim okumaya çalıştığımı bitmap RGB (Red, Green, Blue ) tonlama bilgisi taşır.
dolayısı ile bitmap nesnemizdeki her bir bilgiyi RGB olarak yorumlamamız gerekir.
Her bit tonlama bir byte değeri ifade eder. Yani bitmap nesnemizdeki her bir pixel 3 byte değere denk gelecektir.
yukarıdaki matrisimiz 256 renkli bir matristi.
Eğer RGB bitmap (24 Bit) matrisine örnek verecek olursak

Kod: Tümünü seç

FFFFFF, FFFFFF,FFFFFF,FFFFFF ==> Her biri beyaz rengi temsil eder RGB(255,255,255)
000000,000000,000000,000000 ==> Her biri siyyah rengi temsil eder RGB(0,0,0)
FF0000,FF0000,FF0000,FF0000 ==> Her biri kırmızı rengi temsil eder RGB(255,0,0)
burada TRGBTriblearray siye tanımladığımız dizi her bir elemanı TRGBTriple record türünde olan veriler dizisidir.
ve TRGBTriple yapısı ise aşağıdaki gibidir.

Kod: Tümünü seç

  tagRGBTRIPLE = packed record
    rgbtBlue: Byte;
    rgbtGreen: Byte;
    rgbtRed: Byte;
  end;
  TRGBTriple = tagRGBTRIPLE;
dolayısı ile biz scanline ile taradığımız nesnenin renk bilgisin alırken aslında şu işlemi yapmş oluyoruz.
  1. Resmi scanline ile tara ve bir satır al.
  2. bu satırdan 3'er byte al.
  3. bu 3 byte'in ilkini kırmızı ikincisini yeşil üçüncüsüni ise mavi ton olarak yorumla.
demiş oluruz. Yine bu elemanın bilgisin değiştirecek olursak tek yapmamız gereken tonları değiştirip tekrar elmana yazmaktır.

Umarım Faydası olur. Kolay gelsin.
neu84
Üye
Mesajlar: 307
Kayıt: 06 Oca 2011 11:27

Re: Bitmap İşlemleri & Scanline

Mesaj gönderen neu84 »

İşte bekledigim tarzda açıklama, çok güzel anlatmışsınız, dilinize parmaklarınıza saglık.. Biraz daha üstünde düşünmem gerek sanırım tam olarak anlayabilmem için, daha soracagım sorular olucak galiba :) çok teşekkür ederim..
Cevapla