Bu yayını, yabancı bir siteden kendimce Türkçeleştirerek ve kısaltarak aldım. Umarım faydalı olur...
C:/ klasörümüzün altına calisma adında bir klasör açıp içine de resim1.jpg adında bir resim koyarak başlayalım...
Aşağıdaki kodu inceleyelim.
#include "stdafx.h"
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat kaynak = imread("C:/calisma/resim1.jpg");
Mat hedef;
if (!kaynak.data) { cout << "Resim yuklenemedi." << endl; return -1; }
namedWindow("Pencere");
int deger1 = 50;
int aydinlik;
createTrackbar("Aydinlik", "Pencere", °er1, 100);
int deger2 = 50;
double kontrast;
createTrackbar("Kontrast", "Pencere", °er2, 100);
while (true) {
aydinlik = deger1 - 50;
kontrast = deger2 / 50.0;
kaynak.convertTo(hedef, -1, kontrast, aydinlik);
imshow("Pencere", hedef);
if(waitKey(50) == 27) break;
}
return 0;
}
main() fonksiyonumuz içine bakalım...
Mat kaynak = imread("C:/calisma/resim1.jpg"); Mat hedef;
resim1.jpg adlı resmimizi kaynak değişkenimize aldık. Ayrıca bu resmin işlenmiş (aydınlık ve kontrast katılmış) halini göstermek için de hedef adında boş bir Mat türü değişken oluşturduk.
if (!kaynak.data) { cout << "Resim yuklenemedi." << endl; return -1; }
Bunu önceki yayınlarda if( kaynak.empty() ) { ... } şeklinde görmüştük. Bu da başka bir yol. Resmimiz yüklenemediyse ekrana "Resim yuklenemedi." yazdırıp programı kapatıyoruz.
createTrackbar("Aydinlik", "Pencere", °er1, 100);
createTrackbar() fonksiyonu, trackbar oluşturur. Parametreleri inceleyelim:
~ "Aydinlik" trackbar'ımızın adı ne olacak?
~ "Pencere" trackbar'ımız hangi pencerede bulunacak?
~ °er1 trackbar'ımızın başlangıçtaki değeri, bellekteki hangi adreste tutuluyor?
~ 100 trackbar'ımız sıfır ile kaç aralığında değerler alacak?
while ile açtığımız sonsuz döngünün içine bakalım...
aydinlik = deger1 - 50;
aydinlik, bizim kaynak resmimizin üzerine uygulayacağımız değeri belirleyecek. deger1, 0-100 aralığında bir değer almak üzere trackbar için oluşturulmuştu. Ama biz hedef resmimize 0-100 aralığında değil, -50 ile + 50 aralığında bir değer uygulayacağız. Bu satır sayesinde deger1'in değeri değiştiğinde, aydinlik'in değeri de onun 50 eksiği olacak. Yani başlangıçta deger1=50 iken aydinlik=0 (yani hiçbir işlem yapılmamış) olacak. deger1=100 iken aydinlik=50 olacak. Bu durumun benzeri kontrast için de geçerli. Peki bu işlem yapmaktan kastım nedir? Ona da bakalım.
kaynak.convertTo(hedef, -1, kontrast, aydinlik);
kaynak adındaki matrisimiz üzerinde bazı güzel işlemler yapan convertTo fonksiyonunun parametrelerini inceleyelim.
~ hedef İşlemin sonucu olacak matris
~ -1 Piksel ve kanal sayısının değiştirilme parametresidir. -1 olunca bu alanda bir değişiklik olmaz.
~ kontrast Bu alfa adlı parametredir. kaynak'ın tüm piksel değerleri bu değerle çarpılır.
~ aydinlik Bu da beta adlı parametredir. kaynak'ın tüm piksel değerleri bu değerle toplanır.
if(waitKey(50) == 27) break;
Eğer klavyeden 27 kodlu tuşa (ESC'ye) basılırsa while döngüsünü kırıyoruz. Sonra da zaten program sonlanıyor.
Visual Studio 2015 ile C# ve C++, Eclipse ile Java kullaranarak OpenCV öğrenmek için Türkçe kaynak.
görüntü işleme etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
görüntü işleme etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
6 Haziran 2016 Pazartesi
5 Haziran 2016 Pazar
Fare Hareketlerini ve Tıklamaları Yakalamak (OpenCV & C++)
Bu yayınımızda da fare hareketlerini nasıl yakalayacağımıza bakalım. Bu yayında, pencerede bir resim göstereceğiz ve o resim üzerinde faremizin hareket ve tıklama koordinatlarını yakalamayı göreceğiz.
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) { ... }
Mat resim = imread("C:/calisma/resim1.jpg");
if (resim.empty()) { cout << "Resim yuklenemedi" << endl; system("Pause"); return -1; }
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...
Etiketler:
dinamik değer,
fare hareketleri,
fare olayları,
görüntü işleme,
imleç,
koordinatlarını bulmak,
opencv,
opencv 3.1.0,
opencv3,
örnek proje,
pointer,
resim gösterme,
tıklama,
visual studio 2015
Kaydol:
Kayıtlar (Atom)