Evet güne hızlı başladım, şimdi de opencv ile bir resim nasıl açılır, nasıl görüntülenir, üzerinde bir iki basit işlem nasıl yapılır gibi şeyleri öğreneceğiz. (Linux için opencv paketini yükleyip, en altta verdiğim parametrelerle derleme yapmanız yeterli. Windows için biraz arama yapmanız gerekecek
)
Öncelikle her yazıda yaptığımız gibi, konu ile ilgili struct‘ın ne olduğunu görelim. Açtığımız resimler ile ilgili bilgiler (IplImage *) struct’ında tutulur. Şu an yapacağım kopyala yapıştırdan gözünüz korkmasın, hepsini bilmek zorunda değilsiniz. width, widthstep, height, nChannels, depth ve imageData‘yı bilmeniz yeterli, bunlar birazdan açıklanacak.
typedef struct _IplImage
{
int nSize; /* sizeof(IplImage) */
int ID; /* version (=0)*/
int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */
int alphaChannel; /* ignored by OpenCV */
int depth; /* pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,
IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported */
char colorModel[4]; /* ignored by OpenCV */
char channelSeq[4]; /* ditto */
int dataOrder; /* 0 – interleaved color channels, 1 – separate color channels.
cvCreateImage can only create interleaved images */
int origin; /* 0 – top-left origin,
1 – bottom-left origin (Windows bitmaps style) */
int align; /* Alignment of image rows (4 or 8).
OpenCV ignores it and uses widthStep instead */
int width; /* image width in pixels */
int height; /* image height in pixels */
struct _IplROI *roi;/* image ROI. when it is not NULL, this specifies image region to process */
struct _IplImage *maskROI; /* must be NULL in OpenCV */
void *imageId; /* ditto */
struct _IplTileInfo *tileInfo; /* ditto */
int imageSize; /* image data size in bytes
(=image->height*image->widthStep
in case of interleaved data)*/
char *imageData; /* pointer to aligned image data */
int widthStep; /* size of aligned image row in bytes */
int BorderMode[4]; /* border completion mode, ignored by OpenCV */
int BorderConst[4]; /* ditto */
char *imageDataOrigin; /* pointer to a very origin of image data
(not necessarily aligned) -
it is needed for correct image deallocation */
}
IplImage;
Tahmin edebileceğiniz gibi, width resmin genişliğini, height yüksekliğini, nChannel kaç tip renk kanalı olduğunu (kırmızı yeşil mavi gibi) widthStep ise bir satırın toplam genişliğini veriyor. Data, unsigned char tipinde, bize resme ait ve headerdan geriye kalan yani renk bilgilerini gösteren kısmı verir.
Şimdi örneğimize geçebiliriz. Örneğe geçmeden önce bir açıklamada bulunayım. Örnekte aslında iki şey yapıyoruz.
1. Bir resmin rengini normalizasyon yöntemi ile açmak (örneğin 255 ile 0 arasında bir renk değerini 50 açmak istiyoruz, bunun için direk 50 eklemek yerine rengi 50 ile 255 arasına oranlıyoruz)
2. Bir resmi grinin tonlarına dönüştürmek.
İlkini yapmak için, konsoldan parametre olarak girilmiş olan resmi yüklüyoruz, sonra normRenkAc fonksiyonu ile rengini açıyoruz, sonra da bu resmi bir pencerede gösteriyoruz. Bu resme ait IplImage değişkeninin adı img.
İkincisini yapmak için, konsoldan parametre olarak girilmiş olan ve yüklemiş olduğumuz resme ek olarak, cvCreateImage fonksiyonu ile gri tonlarında ve yüklenmiş olan resmin boyutlarında bir resim alanı yaratıp (IplImage * dimg), cvCvtColor ile img de bulunan renkli bilgiyi dimg ye gri tonlarında olacak şekilde aktarıyoruz.
Kodlara geçmemiz gerekirse;
// IplImage * img, bizim main fonksiyonunda belleğe yükleyeceğimiz (ki bu fonksiyon çalışırken yüklenmiş olacak) resme ait struct. miktar ise, bizim rengi ne kadar açmak istediğimiz.
void normRenkAc(IplImage * img,int miktar)
{
int i,j,k,yer;
// renk bilgilerinin tutulduğu data değişkeninden, unsigned char *’a typecast yaparak data işaretçimize bu alan değerini atıyoruz.
uchar * data = (uchar *)img->imageData;
// eveeet… Burada biraz karmaşık bir yapı var. Öncelikle bir hatırlatmada bulunalım. height resmin yüksekliği, width kalınlığı, nChannel her pixel e ait genişlik veya renk kanalı sayısı, widthStep ise bir satırda kaç bytelık yer olduğuydu. Şimdi, eğer (uchar * data) da renklerin bir matris şeklinde değil de arka arkaya gelen bytelar dizisi şeklinde tutulduğunu göz önünde tutarsak, i inci satırın j inci sütununa nasıl erişebiliriz?
// Şöyle düşünelim, elimizde bir 10*10 = 100 lük bir dizi olsa, ve bu diziyi 10*10′luk bir matris olarak kullanmak istersek, dizinin adı da A ise, 5 inci satırın 3′üncü sütununa nasıl erişiriz? A[5*i+3] değil mi? Burada da aslında buna benzer birşey yapıyoruz. (i*img->widthStep)+(j*img->nChannels)+k formülünde de aslında buna benzer birşey yapıyoruz. Burada +k ile k’ıncı kanala erişim sağlıyoruz.
for(i=0;i<img->height;i++) for(j=0;j<img->width;j++) for(k=0;k<img->nChannels;k++)
{
yer = i*img->widthStep+j*img->nChannels+k;
// Göreceğiniz gibi, her renk değerini 0 ile 255 arasında iken, miktar-255 arasına oranlıyoruz, bu şekilde renk açma hadisesini halletmiş durumdayız.
data[yer]=(data[yer]*(255-miktar))/255+miktar;
}
}
Gelelim main fonksiyonumuza, zurnanın zırt dememesi gereken yere
int main(int argc, char *argv[])
{
// açacağımız resim
IplImage* img = NULL;
// bellek açarak, yaratacağımız alan. Bu alana, dosyadan aldığımız resmin gri tonlu halini atacağız.
IplImage* dimg = NULL;
// Argümanlar konsoldan yanlış verildiyse o zaman uyarı vermemiz gerekiyor.
if(argc!=2){
printf(“kullanim: ./hede.o acilacak_dosya\n”);
exit(0);
}
// Resmi yüklüyoruz
// bakalım prototipinde ne deniliyormuş?
/*
// Loads an image from file
IplImage* cvLoadImage( const char* filename, int iscolor CV_DEFAULT(1));
iscolor
if >0, the loaded image will always have 3 channels;
if 0, the loaded image will always have 1 channel;
if <0, the loaded image will be loaded as is (with number of channels depends on the file)
Yani, resmimizin kanalları nasılsa, o şekilde yüklenmesini istiyoruz.
*/
img=cvLoadImage(argv[1],-1);
//resim yüklenemediyse uyarı veriyoruz.
if(!img){
printf(“Resim yuklenemedi: %s\n”,argv[1]);
exit(0);
}
// Resim açıldığında, resme ait bilgileri ekrana yazdırmakta fayda var. Eğer çalışmada sıkıntı çıkarsa o zaman debug açısından faydalı oluyor.
printf(“%dx%d resmi %d kanalda aciyoruz\n”,img->height,img->width,img->nChannels);
// Gri tonlarında yaratacağımız resim… IPL_DEPTH_8U nedir bilmiyorum yazdım, çalıştı ben de fazla kurcalamadım. yanındaki 1 ise tek kanala sahip olduğunu yani gri tonlu bir resim olduğunu ifade ediyor.
dimg = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
// burada resmimizi gri tonlarına çevirip, dimg ye atıyoruz. CV_BGR2GRAY, RGB resmi GRAYSCALE e yani gri tonlarına çevirmek istediğimizi anlatıyor. Yani neymiş, cvCvtColor fonksiyonunu resimlerimizi farklı şeylere çevirmek için de kullanabiliyor muşuz, tabi farklı parametreler vererek.
cvCvtColor(img,dimg,CV_BGR2GRAY);
// burada da img resmimizin rengini önce 50, sonra bir 50 daha açıyoruz. Tabi 100 açmayla iki 50 açmak farklı şeyler, işin felsefi boyutunu size bırakıp devam ediyorum.
normRenkAc(img,50);
normRenkAc(img,50);
// Burada iki işlem yaptık. renk açma, gri tonlara çevirme ve oluşturduğumuz bu iki farklı resmi bir şekilde kullanıcıya göstermemiz gerekiyor ki anlamı olsun. Biz de o zaman gri tonlara çevrilmiş resmi ekranda bir pencere ile gösterelim, rengi açılmış olanı da farklı bir isimle kaydedelim ![]()
if (!cvSaveImage(“acilmis.jpg”,img)) fprint(“Kaydedilemedi\n”);
else
printf(“Resim basariyla kaydedildi.\n”);
//bu fonksiyonun içeriği de gösterilecek.
penceredeGoster(dimg);
return 0;
}
Gelelim pencerede resim göstermek için kullandığımız fonksiyona
void penceredeGoster(IplImage * img)
{ // Pencere yarat, büyüklüğü içeriğine göre otomatik değişsin
cvNamedWindow(“mainWin”, CV_WINDOW_AUTOSIZE);
// pencereyi sol üste göre (100,100) konumuna getir ki köşeye yapışıp iğrenç bir görüntü sergilemesin
cvMoveWindow(“mainWin”, 100, 100);
// resmi goster
cvShowImage(“mainWin”, img );
// klavyeden bir tus icin bekle
cvWaitKey(0);
// free()
cvReleaseImage(&img );
}
Evet, böylece opencv ye küçük bir giriş yapmış olduk. Umarım faydası olmuştur. Projenin tamamı:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cv.h>
#include <highgui.h>
void normRenkAc(IplImage *,int);
void penceredeGoster(IplImage *);
int main(int argc, char *argv[])
{
IplImage* img = NULL;
IplImage* dimg = NULL;
if(argc!=2){
printf(“kullanim: ./hede.o acilacak_dosya\n”);
exit(0);
}
// Resmi yukle
img=cvLoadImage(argv[1],-1);
if(!img){
printf(“Resim yuklenemedi: %s\n”,argv[1]);
exit(0);
}
printf(“%dx%d resmi %d kanalda aciyoruz\n”,img->height,img->width,img->nChannels);
dimg = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
cvCvtColor(img,dimg,CV_BGR2GRAY);
normRenkAc(img,50);
normRenkAc(img,50);
if (!cvSaveImage(“acilmis.jpg”,dimg)) fprintf(stderr, “Kaydedilemedi\n”);
else
printf(“Resim basariyla kaydedildi.\n”);
penceredeGoster(dimg);
return 0;
}
void penceredeGoster(IplImage * img)
{ // Pencere yarat
cvNamedWindow(“mainWin”, CV_WINDOW_AUTOSIZE);
cvMoveWindow(“mainWin”, 100, 100);
// resmi goster
cvShowImage(“mainWin”, img );
// bir tus icin bekle
cvWaitKey(0);
// free()
cvReleaseImage(&img );
}
void normRenkAc(IplImage * img,int miktar)
{
int i,j,k,yer;
uchar * data = (uchar *)img->imageData;
for(i=0;i<img->height;i++) for(j=0;j<img->width;j++) for(k=0;k<img->nChannels;k++)
{
yer = i*img->widthStep+j*img->nChannels+k;
data[yer]=(data[yer]*(255-miktar))/255+miktar;
}
}
Makefile içeriği: (-I ile başlayan parametre değişebilir, whereis opencv diyerek bilgisayarınızda nerede bulunduğunu anlayabilirsiniz)
hede.o: hede.c
· gcc hede.c -o hede.o -I/usr/include/opencv -lcv -lcxcore -lcvaux -lhighgui -lml
Bu yazı toplamda 2631, bugün ise 1 kez görüntülenmiş