BilgiTeknoloji.net    
b i l g i   t e k n o l o j i   y a z ı l ı m

Ana Sayfa

Marjinal XML Access Pratik Uygulamalar Projeler Ekonometri Dilimiz Çetrefil İletişim
 

Biraz Resim Çalışması


Bu ay resimlerle çalışırken kullanabileceğiniz basit ve faydalı teknikleri inceliyoruz.


Windows resimler üzerinde işlemler yapmak için çok çeşitli APİ işlevleri (Application Programming Interface) sunmaktadır. Windows arayüzünün programlanmasında karmaşık işleri kolaylaştıran kalabalık işlevler topluluğunun büyük bir bölümü de resimlerle çalışmak için geliştirilmiştir.

Windows’un açıktan sunduğu bu işlevler çoğunlukla işletim sisteminin kullanması için hazırlanmıştır. Bu yazıda birkaçını inceleyeceğimiz, resimlerle ilgili işlevlerin hemen hepsinin Windows tarafından sıkça kullanıldığını söyleyebiliriz.


RESİM GEÇİŞ UYGULAMASI

Bu yazıda hazırlayacağımız uygulamamız iki resmi belli bir oranda katıştırıp yeni bir resim oluşturuyor. Ek olarak resimler için iki farklı gri tonlama yöntemi uygulanabiliyor ve resimler yatay ve dikey çevrilebiliyor.

Ayrıca BMP ve JPG biçimindeki dosyalar okunabiliyor ve çıktı olarak bu türde dosyalar oluşturulabiliyor.


Dıştaki iki resmin belli bir oranda katıştırılmasıyla oluşan yeni görüntü ortada.


RESİMLERİ KATIŞTIRMAK

İki resmin iç içe geçirilmesi yani katıştırılması nasıl olur? Aslında bu durum yalnızca renk ve ışıkla ilgilidir.

Renkleri kırmızı, mavi ve yeşil ile (RGB) ifade edebildiğimiz bir ortamda mavi ışık şiddetini artırmak, görüntüdeki mavi değerlerinin artması, aynı zamanda diğer renk değerlerinin dolaylı olarak kısmen azalması anlamına gelir.

Ekteki Renk uygulamasını kullanarak renklerin nasıl sayısallaştırıldığını daha iyi anlayabilirsiniz. Göreceğiniz gibi gözle ayırdedebileceğimiz tüm renkler sayısal ortamda bu üç rengin karışımından elde ediliyor. Sonuçta her rengin, bu üç rengin belli oranlardaki karışımından oluştuğunu söylemek mümkündür.

Görüntülerin katıştırılması da aslında bununla ilgilidir. Aynı düzlemde karşılıklı her noktası kesiştirilen iki resmin karışımını oluşturmak için karşılık gelen renklerin değerlerinin yukarıdakine benzer şekilde, belli kuvvet oranlarında toplanması gerekir. Aynı kare içinde iki farklı resmi eşit oranda görüntülemek için herbirinin ışık şiddetini azaltıp çakışan renklerin değerlerini toplamak yeterlidir. Bu da aslında ortalamayı almaktan başka bir şey değildir.

Tam kesişen iki resmin bir A noktasına karşılık gelen yeni rengi hesaplamak için aşağıdaki yol kullanılır:

Renk_A = (Renk1+Renk2) / 2

yani

R = (R1 + R2) / 2
G = (G1 + G2) / 2
B = (B1 + B2) / 2
Yeni_Renk = (R G B)


Ekteki Resim Geçiş uygulamamız için renk karıştırma işlevleri aşağıdakine benzerdir.


//İki resmi belli oranda karıştırıp başka bir resmin üzerine yazar.
procedure ResimleriKatistir(resim1:TBitmap; resim2:TBitmap; var sonuc:TBitmap; oran:byte);
var
  w,h:integer;
begin
  for h:=0 to sonuc.Height do
    for w:=0 to sonuc.Width do
      sonuc.Canvas.Pixels[w,h]:=
         RenkKatistir( resim1.Canvas.Pixels[w,h],
                       resim2.Canvas.Pixels[w,h],
                       oran );
end;

function RenkKatistir(renk1,renk2:TColor; oran:byte):TColor; overload;
begin
  ...
end;

Overload’ın aynı adı taşıyan çok sayıda komut işlevleri oluşturmayı sağladığını söylememiz gerek. Yazının sonundaki kod dosyasını yükleyip kodları incelerseniz bu adı taşıyan iki farklı işlev olduğunu göreceksiniz. Bilenler aynı özelliğin C++’ta da olduğunu hatırlayacaklardır. Daha fazla bilgi için ilgili kaynaklarda overloading / aşırı yükleme konusunu araştırın.


RESİM İŞLEVLERİ

Resimleri hatta videoları bundan daha hızlı ve daha farklı şekillerde katıştırmak için çeşitli yollar ve araçlar vardır. DirectX araçlardan biridir. Ayrıca Windows’un içinde hazır bulunan ve yeni sürümlerle birlikte ortaya çıkan yeni APİ işlevleri de sözkonusudur.

Açıkçası bu çeşit işler için en doğru yollar burada anlatılanlar değildir. Bu yazı yalnızca okuyucularımızın bu işin nasıl yürüdüğünü kavramalarını sağlamak içindir. Asıl yapılması gerekenleri yeri geldikçe sonraki yazılarımızda inceleyeceğiz.

Resim Geçiş uygulamasında çok çeşitli işlevler var. Bunları oluştururken sıkça ihtiyaç duyabileceğiniz yöntemleri göz önünde bulundurup işinize yarayacak kodlar oluşturmaya çalıştık.

Ekteki Delphi kodlarında resimlerin yatay ve dikey döndürülmesi, renkli resimlerin gri tonlanması, JPEG ve BMP dosyalarının okunup kaydedilmesi gibi işlevler sağlayan çeşitli yordamlar yer alıyor.


 

RESİM DOSYALARIYLA ÇALIŞMAK

Resim Geçiş’te katıştırma işlemi yapmadan önce, üstteki Resim Seç düğmelerini kullanarak sağ ve sol bölmeye birer resim yüklemek gerekiyor.

Yüklenen resimleri TImage nesnelerinin haricinde aynı zamanda hafızada tutmayı tercih ettik. Bunun için görünür olmayan TBitmap nesnelerini kullanmak gerekliydi.

Aşağıdaki işlev verilen bir JPEG ya da BMP biteşlem resmini bellekteki bir TBitmap nesnesine yükler.

Uses jpeg;

...

//JPG ya da BMP resmi yükler.
function ResimDosyaYukle(dosya:string; bmp:TBitmap):boolean;
var
  uzanti:string;
  jpg:TJPEGImage;
begin
  result:=false;
  //Resim JPEG türünde mi?
  uzanti:=Lowercase(ExtractFileExt(dosya));
  if ( uzanti='.jpg' ) or (uzanti='.jpeg') then
  begin
    jpg:=TJPEGImage.Create;
    jpg.LoadFromFile(dosya);
    bmp.Assign(jpg);
    jpg.free;
    result:=true;
  end else
  //Yoksa BMP türünde mi?
  if ( uzanti='.bmp' ) then
  begin
    bmp.LoadFromFile(dosya);
    result:=true;
  end;
end;

Bunu bir TImage nesnesi için kullanmak istiyorsanız alttaki gibi deneyin.

If ResimDosyaYukle(‘c:\dosya.jpg’, image1.picture.bitmap) then
   ShowMessage(‘Yükleme başarılı’)
else
   ShowMessage(‘Yükleme başarısız’);

Jpeg dosyaları ile çalışmak için kod sayfasında Uses kısmına “Jpeg” adını eklemeyi unutmayın.

Bir resmi dosya olarak kaydetmek istediğinizde ise aşağıdakini kullanabilirsiniz:

procedure ResimDosyaKaydet(dosya:string; resim:TBitmap);
var
  uzanti:string;
  jpg:TJPEGImage;
begin
  Screen.Cursor:= crHourGlass;
  uzanti:=Lowercase(ExtractFileExt(dosya));
  if ( uzanti='.jpg' ) or (uzanti='.jpeg') then
  begin
    jpg:=TJPEGImage.Create;
    jpg.Assign(resim);
    jpg.SaveToFile(dosya);
    jpg.free;
  end else
  //JPG değilse BMP türünde kaydediyor.
  begin
    resim.SaveToFile(dosya);
  end;
  Screen.Cursor:= crDefault;
end;

JPEG ve TBitmap çevriminin Assign ile yapıldığına dikkat edin. Oluşturulan yeni TImage resmini kaydetmek için de şunu deneyin:

ResimDosyaKaydet(‘c:\dosya.jpg’, image1.picture.bitmap);


HAFIZADAKİ RESİMLER

Resim geçiş uygulamasında elde edilen sonuç resmi hafızaya alınabiliyor.


uses clipbrd;

...

procedure TAnaPencere.btnPanoyaKopyalaClick(Sender: TObject);
var
  pano:TClipboard;
begin
  pano:=TClipBoard.Create;
  pano.Assign(image1.Picture.Bitmap);
  pano.Free;
end;

Bu kod image1 resmini hafızaya alıyor. Aşağıdaki, uygulamamızda olmayan kod parçası ise hafızadaki bir resmi yüklemektedir.

procedure ...
var
  pano:TClipboard;
begin
  pano:=TClipBoard.Create;
  if pano.HasFormat(CF_BITMAP) then
     image1.Picture.Bitmap.Assign(pano);
  pano.Free;
end;

Yine Assign kullandık. Panodan okumakla dosyadan okumak arasında pek fark yok.


RESİMLERİ DÖNDÜRMEK

TImage nesneleri (Delphi’de Additional sayfasından seçilir) yüklenen resimlerin her noktasına ulaşmak için işlevler sağlar. Aslında bu işlevlerin çoğu Windows’taki APİ’leri kullanmaktadır. Resmin yüzeyindeki (Canvas) bir rengi almak için aşağıda gördüğünüz gibi gdi32.dll içindeki GetPixel işlevi kullanılıyor.

function TCanvas.GetPixel(X, Y: Integer): TColor;
begin
  RequiredState([csHandleValid]);
  GetPixel := Windows.GetPixel(FHandle, X, Y);
end;

[Windows.pas]
function GetPixel; external 'gdi32.dll' name 'GetPixel';
function GetPixel(DC: HDC; X, Y: Integer): COLORREF; stdcall;

Bu haliyle resimlerin her karesini istediğimiz gibi değiştirmemiz de (SetPixel) mümkün olmaktadır.

image1.canvas.pixels[100,150]:= clRed;
image1.canvas.pixels[101,151]:= RGB(255,0,0);

Bu iki satır resim nesnesinin (100,150) ve (101,151) noktalarını kırmızıya boyamaktadır.

Resmi döndürmek de benzer şekilde yapılıyor. Ters çevirmek için birbirine karşılık gelen noktaların renk değerlerini değiştirmek yeterli olur.

procedure YatayDondur;
var
  w,h:integer;
  gecici:TBitmap;
begin
  Screen.Cursor:=crHourGlass;

  gecici:=TBitmap.Create;
  gecici.width:=resim.width;
  gecici.Height:=resim.Height;

  image1.Canvas.Lock;

  for h:=0 to image1.Height do
    for w:=0 to image1.Width do
        gecici.Canvas.Pixels[w,h]:= image1.Canvas.Pixels[image1.Width-w,h];

  image1.Assign(gecici);
  image1.Canvas.Unlock;

  Screen.Cursor:=crDefault;

end;


GRİ TONLAMA

Üç baytlık renk sisteminde (RGB) yalnızca gri ve siyah tonları elde etmek için renkteki tüm değerleri aynı yapmak gerekir. Bu aslında tek bayt ile ifade edilebilecek bir renk değeridir.

Renk:= RGB(128,128,128);

şeklindeki bir renk griden başkası değildir. Zaten graphics.pas dosyasında yer alan aşağıdaki satır bunu doğruluyor.

clGray = TColor($808080);

Bu durumda rengimizin sayısal olarak 128 ile (yani $80) ile tek bayt olarak ifade edilmesi mümkündür.

Peki bir rengi griye dönüştürme işlemi nasıl olmalıdır? Bunun en basit yolu her noktanın rengindeki baytların ortalamasını almaktır. Yani

Ortalama =(R+G+B) / 3
Renk = RGB(Ortalama, Ortalama, Ortalama)

Gri tonlama için çeşitli yöntemler üretilebilir. Bir diğeri özellikle doğa görüntüleri için kullanılabilen, ortalamada yeşilin ağırlıklı olduğu gri tonlama yöntemidir. Yukarıdaki ortalama yönteminde her rengin katılımı 0.33 (1/3) iken bu yöntemde yeşil %60, kırmızı %10, mavi de %30 olarak ortalamaya dahil edilmektedir.

Ortalama =(R * 0.1 + G * 0.6 + B * 0.3) / 3
Renk = RGB(Ortalama, Ortalama, Ortalama)

( R=Red (Kırmızı); G=Green (Yeşil); B=Blue (Mavi) )

İkinci yöntem için Resim Geçiş’in kodlarında yer alan GriTonlama2 yordamını oluşturduk.

procedure GriTonlama2(kaynak,nereye:TBitmap);
//Yeşilin ağırlıklı olduğu gri tonlama yöntemi.
//R=%10, G=%60, B=%30 oranında ortalamaya dahil ediliyor.
var
  w,h:integer;
  toplam: integer;
  renk:TColor;
  ortalama:integer;
begin
  Screen.Cursor:=crHourGlass;
  nereye.Canvas.Lock;
  for h:=0 to kaynak.Height do
  begin
    for w:=0 to kaynak.Width do
    begin
      renk:= kaynak.Canvas.Pixels[w,h];
      ortalama:= ( ( 10 * Byte(renk) ) +
                   ( 60 * Byte(renk shr 8) ) +
                   ( 30 * Byte(renk shr 16) ) ) div 100;
      nereye.Canvas.Pixels[w,h]:= ( ortalama or (ortalama shl 8) or (ortalama shl 16) );
    end;
  end;
  nereye.Canvas.Unlock;
  Screen.Cursor:=crDefault;
end;


AYIN RESMİ

Aşağıdaki resim için özenle çalıştık. Zar zor da olsa Windowsumuzun böyle bir felaket karşısında ne yapacağını öğrenmiş bulunuyoruz. Sizce verilerin kurtarılması için kayıt düğmesine basıp sabit diske kaydetmeyi sağlayacak kadar boş bellek alanı kalmış mıdır?

DOSYALAR:
renk.exe
.rar, 196 kb.)
renk_kod.rar
resimgecis.exe
.rar, 280 kb.)
resimgecis_kod.rar


Serkan ŞAHİNOĞLU
(Chip Dergisi, Nisan 2003)


http://BilgiTeknoloji.net