Başlangıç olarak yine C: altına "calisma" adında bir klasör açalım ve üzerinde çalışacağımız bir jpg dosyasını bu klasörün altına koyalım. Adına da resim1.jpg diyelim.
#include "stdafx.h"
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
void fareOlaylari(int olay, int x, int y, int flag, void* kullaniciBilgisi) {
if (olay == EVENT_LBUTTONDOWN) cout << "Sol tiklandi - pozisyon (" << x << ", " << y << ")" << endl;
else if (olay == EVENT_RBUTTONDOWN) cout << "Sag tiklandi - pozisyon (" << x << ", " << y << ")" << endl;
else if (olay == EVENT_MBUTTONDOWN) cout << "Orta tiklandi - pozisyon (" << x << ", " << y << ")" << endl;
else if (olay == EVENT_MOUSEMOVE) cout << "Fare hareketi - pozisyon (" << x << ", " << y << ")" << endl;
}
int main() {
Mat resim = imread("C:/calisma/resim1.jpg");
if (resim.empty()) { cout << "Resim yuklenemedi" << endl; system("Pause"); return -1; }
namedWindow("Pencere");
setMouseCallback("Pencere", fareOlaylari, NULL);
imshow("Pencere", resim);
waitKey(0);
return 0;
}
Burada açıklamam gerekecek çok bir yer yok sanıyorum.
fareOlaylari() adında bir fonksiyon oluşturuyoruz. Pencere üzerinde bir fare olayı (hareket, tıklama) gerçekleştiğinde bu fonksiyon çalışıyor ve fonksiyonun içinde de olaya göre ekrana olayın ne olduğunu ve fare koordinatlarını yazdırıyoruz.
void fareOlaylari(int olay, int x, int y, int flag, void* kullaniciBilgisi) { ... }
Bu fonksiyonda bu parametrelerin hepsini almak zorunda mıydık? Evet. Bu parametreleri bu sırayla almazsanız, kodunuz hata verecektir. Çünkü bu fonksiyonu çağıran setMouseCallback() fonksiyonu, fonksiyonumuza bu parametreleri bu sırayla göndermek istiyor. Herhangi bir parametreyi sildiğinizdeyse setMouseCallback() fonksiyonu hata veriyor.
Fare olayları sadece bu tıklamalar ve koordinat bilgileri değil tabii... Bunları biraz sonra belirteceğim.
Mat resim = imread("C:/calisma/resim1.jpg");
if (resim.empty()) { cout << "Resim yuklenemedi" << endl; system("Pause"); return -1; }
Burada resmimizi resim adlı Mat nesnemize alıyoruz.
Eğer alamadıysak (resimde bir problem varsa veya resmi o adreste bulamadıysa mesela) bu if blokuna düşüyoruz ve ekrana "Resim yuklenemedi" yazdırıyoruz. Bu yazıyı görebilelim diye sisteme bir tuşa basılıncaya kadar beklemesi için "Pause" diyoruz. Sonra da programımız -1 döndürerek sonlanıyor.
setMouseCallback("Pencere", fareOlaylari, NULL);
Bu fonksiyon ile, "Pencere" adlı penceremiz üzerinde herhangi bir fare olayı gerçekleşirse, fareOlaylari() fonksiyonumuzu çağırıyor. Son parametreye NULL diyoruz.
Şimdi şu fareOlaylari() fonksiyonumuza tekrar göz atalım.
void fareOlaylari(int olay, int x, int y, int flag, void* kullaniciBilgisi) { ... }
Burada alınan parametrelerin mutlaka bulunması gerektiğini söylemiştik. Hangisi ne iş yapıyor ona bakalım.
1) int olay ~ Bu parametreye, farenin yaptığı olayın kodu gönderilmektedir. Bu olaylar şunlardır:
2) int x, int y ~ Farenin, olay gerçekleştiği sırada penceredeki konumunun x ve y koordinatları.
int flags ~ Bunu nasıl çevirsem dilimize bilemedim. int bayrak! mı diyeydim? :) flag olarak bıraktım. Bu değişken ile, faremizin olayları gerçekleştiği sırada klavyemizde gerçekleşen bazı olayları takip edebiliyoruz. Şöyle ki, mesela fonksiyonumuz içine şu if bloğunu ekleyebilirdik:
Eğer fare hareket etmişse ve klavyeden Alt tuşuna basıldıysa (İki olay aynı anda gerçekleştiyse)
if ( event == EVENT_MOUSEMOVE && flag == EVENT_FLAG_ALTKEY) { ... }
Eğer klavyeden CTRL tuşuna ve farenin sol tuşuna birlikte basıldıysa:
if ( flag == (EVENT_FLAG_CTRLKEY + EVENT_FLAG_LBUTTON) ) { ... }
Eğer klavyeden Shift tuşuna basıldıysa (Herhangi bir fare hareketi gerçekleşirken)
if ( flag == EVENT_FLAG_SHIFTKEY ) { ... }
Yani event && flag şeklinde de gösterebiliyoruz, flag + flag şeklinde de gösterebiliyoruz. Bu flag'lar sayesinde farenin olayı gerçekleştiği sırada klavyeden CTRL, ALT veya Shift tuşlarına basılıp basılmadığını kontrol edebiliyoruz. Buyurun burada da kullanabileceğimiz flag'larımız:
3) void* kullaniciBilgisi ~ Herhangi bir işaretçi, üçüncül parametre olarak fareOlayları() fonksiyona gönderilebilir. Bu işaretçi sayesinde fonksiyonda gerçekleştirilecek işlemler sonrası fonksiyon dışından dönüş alınabilir. Şu örneği inceleyelim:
#include "stdafx.h"
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
void fareOlaylari(int olay, int x, int y, int flag, void* kullaniciBilgisi) {
int* deger = (int*)kullaniciBilgisi;
*deger = *deger + 1;
cout << *deger << endl;
}
int main() {
Mat img = imread("C:/calisma/resim1.jpg");
namedWindow("Pencere");
int a = 5;
setMouseCallback("Pencere", fareOlaylari, &a);
imshow("Pencere", img);
waitKey(0);
destroyWindow("Pencere");
cout << "a'nin yeni degeri: " << a << endl;
system("Pause");
return 0;
}
main() fonksiyonunun içine bakarsak, sırasıyla şu işleri yaptık:
~ Mat img = imread("C:/calisma/resim1.jpg"); ile resmini img adlı Mat nesnemize aldık.
~ namedWindow("Pencere"); ile "Pencere" adında bir pencere oluşturduk.
~ int a = 0; ile a adında, int türünde, sıfır değerine sahip bir değişken oluşturduk. Amacımız, herhangi bir fare olayı gerçekleştiğinde bu değerin 1 artması.
~ setMouseCallback("Pencere", fareOlaylari, &a); ile, "Pencere" adlı pencere üzerinde herhangi bir fare olayı gerçekleştiğinde fareOlaylari() fonksiyonu çağırılsın ve kullaniciBilgisi adlı pointer parametreye a değişkeninin adresi gönderilsin. (C++'da pointer konusunu bilmiyorsanız, Google'de arama yaparak öğrenmelisiniz.)
~ imshow("Pencere", img); ile img adlı Mat nesnemizi "Pencere" adlı penceremizde gösterdik.
~ waitKey(0); ile klavyeden herhangi bir tuşa basılıncaya kadar Pencere açık kalacak.
~ destroyWindow("Pencere"); ile "Pencere" adlı penceremizi kapatıyoruz.
~ cout << "a'nin yeni degeri:" << a << endl; ile, a değişkeninin en son aldığı değeri yazdırıyoruz.
~ system("Pause"); ile sistem dursun ki a değişkeninin değerini okuyabilelim diyoruz. Herhangi bir tuşa basılınca da zaten return 0 diyerek main() fonksiyonunu tamamlıyoruz.
void fareOlaylari(int olay, int x, int y, int flag, void* kullaniciBilgisi) { ... } fonksiyonumuzda neler olduğunu da satır satır inceleyelim.Şimdi şu fareOlaylari() fonksiyonumuza tekrar göz atalım.
void fareOlaylari(int olay, int x, int y, int flag, void* kullaniciBilgisi) { ... }
Burada alınan parametrelerin mutlaka bulunması gerektiğini söylemiştik. Hangisi ne iş yapıyor ona bakalım.
1) int olay ~ Bu parametreye, farenin yaptığı olayın kodu gönderilmektedir. Bu olaylar şunlardır:
EVENT_MOUSEMOVE | Fare hareket ediyor |
EVENT_LBUTTONDOWN | Fareye sol tıklandı |
EVENT_RBUTTONDOWN | Fareye sağ tıklandı |
EVENT_MBUTTONDOWN | Farenin orta butonuna tıklandı |
EVENT_LBUTTONUP | Farenin sol butonu bırakıldı |
EVENT_RBUTTONUP | Farenin sağ butonu bırakıldı |
EVENT_MBUTTONUP | Farenin orta butonu bırakıldı |
EVENT_LBUTTONDBLCLK | Farenin sol butonuna çift tıklandı |
EVENT_RBUTTONDBLCLK | Farenin sağ butonuna çift tıklandı |
EVENT_MBUTTONDBLCLK | Farenin orta butonuna çift tıklandı |
2) int x, int y ~ Farenin, olay gerçekleştiği sırada penceredeki konumunun x ve y koordinatları.
int flags ~ Bunu nasıl çevirsem dilimize bilemedim. int bayrak! mı diyeydim? :) flag olarak bıraktım. Bu değişken ile, faremizin olayları gerçekleştiği sırada klavyemizde gerçekleşen bazı olayları takip edebiliyoruz. Şöyle ki, mesela fonksiyonumuz içine şu if bloğunu ekleyebilirdik:
Eğer fare hareket etmişse ve klavyeden Alt tuşuna basıldıysa (İki olay aynı anda gerçekleştiyse)
if ( event == EVENT_MOUSEMOVE && flag == EVENT_FLAG_ALTKEY) { ... }
Eğer klavyeden CTRL tuşuna ve farenin sol tuşuna birlikte basıldıysa:
if ( flag == (EVENT_FLAG_CTRLKEY + EVENT_FLAG_LBUTTON) ) { ... }
Eğer klavyeden Shift tuşuna basıldıysa (Herhangi bir fare hareketi gerçekleşirken)
if ( flag == EVENT_FLAG_SHIFTKEY ) { ... }
Yani event && flag şeklinde de gösterebiliyoruz, flag + flag şeklinde de gösterebiliyoruz. Bu flag'lar sayesinde farenin olayı gerçekleştiği sırada klavyeden CTRL, ALT veya Shift tuşlarına basılıp basılmadığını kontrol edebiliyoruz. Buyurun burada da kullanabileceğimiz flag'larımız:
EVENT_FLAG_LBUTTON | Sol fare tuşuna tıklandığında |
EVENT_FLAG_RBUTTON | Sağ fare tuşuna tıklandığında |
EVENT_FLAG_MBUTTON | Orta fare tuşuna tıklandığında |
EVENT_FLAG_CTRLKEY | Klavyeden CTRL tuşuna basıldığında |
EVENT_FLAG_SHIFTKEY | Klavyeden Shift tuşuna basıldığında |
EVENT_FLAG_ALTKEY | Klavyeden ALT tuşuna basıldığında |
3) void* kullaniciBilgisi ~ Herhangi bir işaretçi, üçüncül parametre olarak fareOlayları() fonksiyona gönderilebilir. Bu işaretçi sayesinde fonksiyonda gerçekleştirilecek işlemler sonrası fonksiyon dışından dönüş alınabilir. Şu örneği inceleyelim:
#include "stdafx.h"
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
void fareOlaylari(int olay, int x, int y, int flag, void* kullaniciBilgisi) {
int* deger = (int*)kullaniciBilgisi;
*deger = *deger + 1;
cout << *deger << endl;
}
int main() {
Mat img = imread("C:/calisma/resim1.jpg");
namedWindow("Pencere");
int a = 5;
setMouseCallback("Pencere", fareOlaylari, &a);
imshow("Pencere", img);
waitKey(0);
destroyWindow("Pencere");
cout << "a'nin yeni degeri: " << a << endl;
system("Pause");
return 0;
}
main() fonksiyonunun içine bakarsak, sırasıyla şu işleri yaptık:
~ Mat img = imread("C:/calisma/resim1.jpg"); ile resmini img adlı Mat nesnemize aldık.
~ namedWindow("Pencere"); ile "Pencere" adında bir pencere oluşturduk.
~ int a = 0; ile a adında, int türünde, sıfır değerine sahip bir değişken oluşturduk. Amacımız, herhangi bir fare olayı gerçekleştiğinde bu değerin 1 artması.
~ setMouseCallback("Pencere", fareOlaylari, &a); ile, "Pencere" adlı pencere üzerinde herhangi bir fare olayı gerçekleştiğinde fareOlaylari() fonksiyonu çağırılsın ve kullaniciBilgisi adlı pointer parametreye a değişkeninin adresi gönderilsin. (C++'da pointer konusunu bilmiyorsanız, Google'de arama yaparak öğrenmelisiniz.)
~ imshow("Pencere", img); ile img adlı Mat nesnemizi "Pencere" adlı penceremizde gösterdik.
~ waitKey(0); ile klavyeden herhangi bir tuşa basılıncaya kadar Pencere açık kalacak.
~ destroyWindow("Pencere"); ile "Pencere" adlı penceremizi kapatıyoruz.
~ cout << "a'nin yeni degeri:" << a << endl; ile, a değişkeninin en son aldığı değeri yazdırıyoruz.
~ system("Pause"); ile sistem dursun ki a değişkeninin değerini okuyabilelim diyoruz. Herhangi bir tuşa basılınca da zaten return 0 diyerek main() fonksiyonunu tamamlıyoruz.
~ Bildiğiniz gibi setMouseCallback() fonksiyonunun son parametresi ile, bu fonksiyonun void türündeki kullaniciBilgisi adlı parametresi için int türündeki a değişkeninin adresini göndermiştik. İlk satırımızda ise buradan gelen değerin türünü int olarak anlayacak bir değişken oluşturduk.
int* deger = (int*)kullaniciBilgisi; ile, int türünde veri tutacak deger adlı bir adres tutucu oluşturduk ve kullaniciBilgisi'ne gelen a değerinin adresini bu deger adlı adres tutucumuza gönderdik.
~ *deger = *deger + 1; ile, deger adresinin içindeki değere 1 ekliyoruz. a değişkenimizin adresi ile deger değişkenimizin adresi aynı olduğu için deger değişkenindeki her değişiklik aynı anda a değişkeninde de olmuş oluyor.
~ cout << *deger << endl; ile, deger adresi içindeki veriyi ekrana yazdırıyorum.
Böylelikle, fonksiyonumuz dışındaki bir değişkenin değerini fonksiyonumuz içinde okuduk ve değiştirdik. setMouseCallBack() fonksiyonundaki 3.parametre bildiğim kadarıyla bu işe yarıyor. Bir değerin adres bilgisini gönderiyorsunuz. Sonra 2.parametrede çağırdığınız fonksiyon içinde de bu adreste tutulan değerle oynayarak bir bakıma fonksiyon dışına bir dönüş yapmış oluyorsunuz.
Konu anlaşılmıştır umarım. Eğer pointer meselesini bilmiyorsanız bu son parametre meselesi kafanızı karışırmış olabilir. Ama C++ içinde pointer'leri Google'layarak kısa sürede bulup öğrenebilirsiniz. Eğer bir talep gelirse ben de anlatabilirim.
Umarım bu yayın da öğretici olmuştur. Bir sonraki yayında görüşmek üzere...
Hiç yorum yok:
Yorum Gönder