10 Haziran 2016 Cuma

JavaFX ile bir OpenCV uygulaması (Eclipse&SceneBuilder&Java)

Bu yayında yabancı bir kaynağı direkt olarak Türkçeye çevirerek sunmayı uygun buldum.
AnaSayfa'da da yazdığım gibi, sizlere henüz özgün bir kaynak sunamıyorum çünkü ben de sizin gibi öğrenmeye çalışıyorum ve öğrendikçe de Türkçe kaynak oluşturarak İngilizcesi olmayanların da bilgiden yararlanmasını sağlamayı hedefliyorum. Gerçi daha önce JavaFX kullanmadıysanız bu yayında sıkıntı yaşayabilirsiniz ama yine de en azından JavaFX'e bir başlangıç yapmış olursunuz. :)
Daha detaylı bilgi için buyurun: Kaynak Site


JavaFX ile bir OpenCV uygulaması

Bu eğitimde Eclipse'de OpenCV kütüphanesi kullanılarak basit bir JavaJX GUI uygulaması oluşturulabilmesi için size rehberlik edilecektir.
Bu yayınımızda yapacaklarımız:
  • e(fx)clipse plugin'i ve Scene Builder kurulumu.
  • Scene Builder ile çalışmak.
  • Uygulamamızı yazmak ve çalıştımak.

İlk JavaFX uygulamanız

Bu yayını takip ederek yazacağımız uygulama ile bir web kamerasının video akışını (kare kare) yakalayarak bir kullanıcı arayüzü (GUI) üzerinde göstereceğiz. Scene Builder ile şunlara sahip bir GUI oluşturacağız:
~ Görüntü akışını başlatmak-durdurmak için bir buton
~ Görüntüleri göstereceğimiz bir görüntü kapsayıcı.

e(fx)clipse plugin ve Scene Builder kurulumu

Eclipse'ye, e(fx)clipse plugin'i kurmak için şu kılavuzu takip edin:
http://www.eclipse.org/efxclipse/install.html#fortheambitious

JavaFX Scene Builder2.0 adlı programı şu linkten indirin ve kurun:http://www.oracle.com/technetwork/java/javafxscenebuilder-1x-archive-2199384.html
Şimdi bir JavaFX projesi oluşturabilrsiniz. File > New > Project... ve JavaFX project... seçin.
_images/03-00.png

Projenize bir isim seçin ve 
Next'e tıklayın. (MyFirstJFXApp)_images/03-01.png
Library sekmesinden OpenCV kütüphanenizi projeye dahil edin ve Next deyin.
(İlk Java yayınımda bunun nasıl yapılacağını yazmıştım. Library sekmesinde, Add Library... butonuna tıklayıp, User Library seçip OpenCV-2.4.13'ü işaretleyip Finish dedikten sonra ortaya eklenen OpenCV-2.4.13'ün yanındaki oka tıklayıp Native Library Location bölümüne C:/OpenCV-2.4.13/build/java/x64 (veya siz OpenCV'nizi nereye koyduysanız o adresi belirtin) yazıyorsunuz)_images/03-02.png
FXML dosyası ve Controller Class'ın dahil olacağı Package Name girin (application). Language olarak FXML deyin. FXML dosyanız için bir isim verin (FirstJFX). Son olarak da bir Controller Name ile bu FXML dosyanızdaki bileşenlerle işlemler yapacağınız controller sınıfı için isim verin (FXController).
(Bence fxml dosyası ve controller aynı isimle olursa daha iyi olur ama bu örnekde farklı isim verilebileceği de gösterilmek istenmiş sanırım.)

Son olarak Finish diyerek projenizi oluşturun...
_images/03-03.png

Scene Builder ile çalışmak

Eğer Scene Builder'i yüklediyseniz şimdi Eclipse'de soldaki Package Explorer'da projenin altında src altında package adı altındaki FXML dosyanıza sağ tıklayıp  Open with SceneBuilder seçebilirsiniz.
Scene Builder grafik arayüz oluşturmanıza yardımcı olur. Anlık ön izleme ile çalışmanıza, bileşenlerin yerlerini değiştirmenize izin verir. Şimdi neden bahsettiğime bir göz atalım. 
İlk olarak FXML dosyanızda sadece AnchorPane olsun. (Not: SceneBuilder ilk açıldığında, ortada aslında bir BorderPane vardır ama boyutları ayarlanmadığı için görünmemektedir. Fareniz ile ortaları seçmeye çalışırsanız göreceksiniz.) AnchorPane, çocuk (child) bileşenlerinin (üzerine koyacağınız bileşenler onun çocuk bileşenleridir), kendisinin kenarlarından hiza alıp konumlanmasına olanak tanır. Eğer AnchorPane bir kenarlığa veya dolguya(padding) sahipse, hizalanmalar bunların iç taraflarından itibaren yapılır. AnchorPange, çocuk bileşenlerini belli bölümlere hizalamaya izin verdiği gibi, bileşenlerin düzensiz durmalarına da izin verir. 
Şimdi de AnchorPane'yi silin ve yerine bir BorderPane koyun. BorderPane'in çocukları üst, sol, sağ, alt ve merkez pozisyonlara hizalanabilirler.
_images/03-04.png
Soldaki Container menüsünden sürükleyip ortaya bırakarak sahneye bir BorderPane ekleyebilirsiniz. Bunu yaptığınızda, soldaki Hierarchy menüsünde onun bölümlerini (top, bottom, center...) göreceksiniz. Şimdi bu BorderPane'mize, görüntüyü başlatıp durdurma özelliği vereceğimiz butonumuzu ekleyelim. Bunun için sağdaki Controls menüsünden Button'u seçip sürükleyin ve Hierarchy bölümündeki BOTTOM alanına bırakın. Bunu yaptığınızda buton seçili iken sağ tarafta Properties, Layout ve Code bölümlerinin dolduğunu göreceksiniz. Buradan butonumuzun özelliklerini ayarlayabiliyoruz. Örnek olarak  Properties menüsünü açıp Text kutusuna Start Camera yazabilirsiniz. _images/03-05.png

Butonumuza kod tarafında erişebilmek için de bir kimlik (id) vermemiz gerekiyor. Bunun için de yine sağ taraftaki Code menüsündeki fx:id kutusuna buton için bir id verin (örn: start_btn)._images/03-06.png
Eğer butonun kenara çok yakın olduğunu düşünüyorsanız, sağdaki Layout menüsündeki margin özelliklerini artırarak butonun dışına doğru görünmez bir dolgu alanı oluşturabilirsiniz.
Butona tıklandığında bir eylem gerçekleşmesini istiyoruz. Bu yüzden, yine butona id verdiğimiz Code menüsündeki OnAction kutusuna, butona tıklandığında controller sınıfımızda çalışacak bir fonksiyon adı verin (örn: startCamera). (Bana sorarsanız butonun adını startButton, bu fonksiyonun adını da startButton_onAction vermek daha anlaşılır olurdu.)_images/03-07.png
Şimdi de Controls menüsünden bir ImageView bileşeni ekleyerek görüntülerimizi sunacağımız bileşenimizi de eklemiş olalım. ImageView'i alıp BorderPane'misin CENTER kısmına sürükleyip bırakın. Kod tarafında bu bileşenimize erişebilmemiz için de bir id verin (Yine sağdaki Code menüsündeki fx:id kutusundan) (örn: currentFrame), ve isterseniz margin de ekleyebilirsiniz..._images/03-08.png
Son olarak GUI'mize eklediğimiz bileşenlerin hepsine, hangi controller sınıfıyla birlikte çalışacağını da belirtmemiz gerekiyor. Sol alt taraflarda kalan Controller menüsündeki Controller class kutusuna, controller'ınızın adını  (paketAdı.controllerAdı şeklinde) yazmanız gerekiyor. Bizim örneğimizde buraya yazmamız gereken: application.FXController
Her bileşende bunun belirtilmesi gerekiyor. Eğer en başta BorderPane'mizde bunu belirttikten sonra diğer bileşenleri (button, imageview) ekleseydik, onlara bu otomatik olarak yazılırdı. Ama öğrenmiş olmak için şimdi tek tek yazmak daha faydalı olur...
Scene Builder programınızı kaydettiğinizde, Eclipse'deki fxml dosyasınızın, bizim yaptığımız değişikliklerin kodlarıyla otomatik olarak doldurulduğunu göreceksiniz.

Temel JavaFX Kavramları

Stage, uygulamanın nerede gösterileceğidir (örn. Windows'un bir penceresi).Scene, uygulamanın bir "sayfa"sını oluşturan Node'lerin (bileşenlerimizin) kapsayıcısıdır.Node, Scene'nin görsel bir görünüm ve interaktif bir davranış elemanıdır.Node'ler hiyerarşik olarak iç içe olabilirler. (Butonun, BorderPane'nin içinde olması gibi)
main sınıfında, start fonksiyonu ile birincil Stage'mizi belirtmeliyiz.
public void start(Stage primaryStage)
ve fonksiyonumuz içinde fxml dosyamızı stage'mize dolduruyoruz:
// Grafik arayüzümüzü oluşturduğumuz dosyayı ana dosyamız olarak yüklüyoruz:
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("FirstJFX.fxml"));

// Sahnemizi oluşturuyoruz, ana sahnemizi ve sahne boyutlarını belirtiyoruz:
Scene scene = new Scene(root,640,480);

// Sahnenin stillerini ayarlayan dosyayı da get'iriyoruz :)
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());

// Yukarıda çağırılanlarla sahnenin kurulmasını istiyoruz:
primaryStage.setScene(scene);

// Bu otomatik oluşturulmuş komutlar arasında yok. Pencereye başlık ekledim:
primaryStage.setTitle("Merhaba");

// Sahneyi göster:
primaryStage.show();

GUI bileşenlerininin Controller sınıfı ile yönetimi

Bizim uygulamamız için iki basit şeye ihtiyacımız var: butona basılmasının kontrolü ve buna bağlı olarak ImageView durumundaki değişiklik. Öncelikle controller sınıfımızı açıp, sınıfımız içine şunları yazarak nesnelerimize id'leri ile ulaşabiliriz:
@FXML
private Button start_btn;
@FXML
private ImageView currentFrame;
@FXML tagı, sıradaki komutun fxml dosyasıyla bağlantılı olduğunu belirtiyor.
@FXML tagı ile, fxml dosyamızdaki elemanların aksiyonlarına da ulaşabiliriz. Hatırlarsanız butona tıklandığında startCamera diye bir fonksiyon çalışsın istemiştik. fxml dosyamızda bu, onAction parametresinde aşağıdaki gibi belirtilmiştir:
<Button fx:id="button" mnemonicParsing="false" onAction="#startCamera" text="Start Camera" BorderPane.alignment="CENTER">
ve biz de controller sayfamıza gelerek aşağıdaki şekilde bu aksiyona ulaşabiliyoruz:
@FXML
protected void startCamera(ActionEvent event) { ... }

Video Yakalamak

Öncelikle geçen yayında söylediğim, OpenCV komutlarının çalışması için gerekli bir kütüphaneyi çağırma işini yapmamız gerekiyor. Bunun için main.java dosyanızı açın ve main fonksiyonunun içine System.loadLibrary(Core.NATIVE_LIBRARY_NAME); yazın. Böylece OpenCV komutlarımız hata vermeden çalışabilecek...
Video yakalamak için gerekli nesne türümüz VideoCapture'dir.
VideoCapture capture = new VideoCapture();
Bu, FFmpeg açık kaynak kütüphanesi oluşturur. Bir video, arka arkaya kareler şeklinde oluşur. İki kare arasında beklenen çok kısa bir süre vardır. Video kameraların saniyede kaç kare gösterebileceğinin sınırı vardır. Biz bunu saniyede 30 kare olarak ayarlayacağız. Bunu yapmak için, arkaplanda her 33 milisaniyede bir çalışacak bir zamanlayıcı (yani bir `ScheduledExecutorService`) ayarlayacağız.
Runnable frameGrabber = new Runnable() { ... }
this.timer = Executors.newSingleThreadScheduledExecutor();
this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
Eğer videonun başarılı bir şekilde alınıp alınamadığını öğrenmek isiyorsanız, isOpened fonksiyonu:
if (this.capture.isOpened()) { ... }
Yıkıcı nesne çağırıldığında video otomatik olarak kapanır. Siz bundan önce kapatmak istiyorsanız, onun release (bırakmak/salıvermek) fonksiyonunu çağırmanız gerekir:
this.capture.release();
Video kareleri, basit görüntülerden (images) oluşur. Bu nedenle, onları işleyebilmek için Mat nesnesine almamız gerekir.
Mat frame = new Mat();
Video akışı sıralıdır. VideoCapture nesnesinin read() fonksiyonunu her kullandığınızda, sıradaki karenin görüntüsünü alırsınız ve fonksiyonun parametresi olan Mat nesnesine yazmış olursunuz:
this.capture.read(frame);
Şimdi görüntümüzü BGR (BlueGreenRed) formatından GrayScale (GriSkala) formatına çevirelim. OpenCV bunu aşağıdaki fonksiyon ile kolayca yapmaktadır:
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
cvtColor fonksiyonunun aldığı parametrelere bakalım:
  • kaynak görüntü (frame)
  • hedef görüntü (frame), dönüşümden sonra görüntünün aktarılacağı Mat nesnesi.
  • Dönüşümün hangi türler arasında yapılacağını belirten parametre. Biz bu örneğimizde COLOR_BGR2GRAY kullandık. (Çünkü imread , görüntüyü BGR türünde alır).
Şimdi elimizdeki bu Mat nesnesini ImageView'e koyabilmek için dönüştürmek gerekir. Öncelikle Mat nesnemizi saklamak için bir tampon (buffer) oluşturalım.
MatOfByte buffer = new MatOfByte();
Sonra imencode fonksiyonu ile bu tamponumuza Mat türü karemizi (frame) koyabiliriz.
Imgcodecs.imencode(".png", frame, buffer);
Bu fonksiyon, ara belleğe bir görüntü kodlamaktadır. Görüntü sıkıştırılır ve ara bellekte bu sıkıştırılmış haliyle saklanır.
imencodeCV_8UC1 tipinde tek satırlı, byte'ler dizisi halinde kodlanmış bir matris dizi döndürür.
Üç paremetre alır:
  • (”.png”) Çıkış biçimini tanımlayan dosya uzantısı.
  • (frame) Görüntüsü yazılacak olan Mat nesnesi.
  • (buffer) Yeniden boyutlandırılıp sıkıştırılmış olan resmin saklanacağı nesne..
Artık görüntüyü ByteArrayInputStream fonksiyonu ile tampondaki diziyi okuyarak ImageView'de gösterilebilecek formatta oluşturabiliriz.
new Image(new ByteArrayInputStream(buffer.toArray()));"
Şimdi yeni görüntümüzü ImageView'e koyabiliriz. Java 1.8 ile hem ana işlemler devam ederken hem de görüntüleri kare kare gösterme işini aynı anda yapamazsınız; bu yüzden, başka bir thead oluşturarak bizim ana thead'ımızdaki ImageView görüntülerini güncelleştirmesini sağlamalıyız.
Image imageToShow = grabFrame();
Platform.runLater(new Runnable() {
        @Override public void run() { currentFrame.setImage(imageToShow); }
});
_images/03-09.png
Bu eğitimde anlatılanlar için kaynak kodu şuradan indirebilirsiniz: GitHub.





<< AnaSayfa - << Önceki Yayın - Sonraki Yayın >>

Hiç yorum yok:

Yorum Gönder