OpenCV ile Dikdörtgen Algılama ( Python )

OpenCV, C++ ile yazılmış açık kaynaklı ( BSD Lisanslı ) bir görüntü işleme kütüphanesidir. İlk olarak Intel tarafından geliştirilmiştir. Aynı zamanda Cross-platform dur. Windows, Linux, OS X, Android, iOS üzerinde çalışabilmektedir.

OpenCV sayısız özellikler barındıran bir kütüphane, bu özellikler ile neler yapılacağı tam olarak hayal gücünüze bağlı. İlk aşamada yapmak istediğim dikdörtgen algılaması yapmak. Örneğin masanın üzerinde duran bir kağıdı, kitabı vs. benzer geometrik şekilde olan nesneleri algılamak. Peki algıladıktan sonra ne olacak ?
Benim hedefim optik form okuyusu yapmak. Bunun için sıralı bir şekilde gerekli aşamaları takip etmek gerekli.

İlk olarak dikdörtgen algılama işlevini başarıyla geçmek lazım. Bunun için yapılan işlemler mantıksal olarak basit fakat ufak ayarlamalar çok fazla değişikliklere yol açıyor.

* Öncelikle renkli bir resmi gray yani gri ve tonlarına dönüştürüyoruz.

* Sonrasında bilateralFilter uyguluyoruz. Bu filtre ne işe yarıyor derseniz; Aslında yaptığımız her işlem gri renkli resim üzerinden objeleri daha algılanabilir yapmak. Bu amaçla ufak ayrıntıları ortadan kaldırmak vs. vs.
Bu filtre ile ne gibi bir değişim olacağını aşağıdaki resimden daha kolay bir şekilde anlayabilirsiniz
Bilateral Filter


* Ufak ayrıntıları biraz yok ettikten sonra Canny fonksiyonundan yararlanarak köşeleri veya geçiş noktalarını elde edeceğiz. Bu filtre ile tamamen siyah-beyaz bir sonuç elde edeceğiz. ( Canny fonksyionu hakkında ayrıntılı açıklama )

Canny filter


* Sonraki aşamalarda ise aradığımız geometrinin bir dikdörtgen olduğunu ve buna benzeyen şekilleri contour elde edip köşe sayılarına bakmak olacak. 4 adet köşeye sahip ise bizim için uygun bir geometri olacaktır. Elde ettiğimiz koordinatlara göre dörtgeni perspektif -> normal görünüme dönüştüreceğiz. Böylece dörtgeni normal bir görünümde elde etmiş olacağız.

Python kodları aşağıdaki gibidir. Webcam / Resim kullanabilirsiniz.

#-*- coding: utf-8 -*-

import cv2
import time
import numpy as np

"""
sudo apt-get install python-opencv
sudo apt-get install python-matplotlib
"""

##################
DELAY = 0.02
USE_CAM = 1
IS_FOUND = 0

MORPH = 7
CANNY = 250
##################
# 420x600 oranı 105mmx150mm gerçek boyuttaki kağıt için
_width  = 600.0
_height = 420.0
_margin = 0.0
##################

if USE_CAM: video_capture = cv2.VideoCapture(0)

corners = np.array(
	[
		[[  		_margin, _margin 			]],
		[[ 			_margin, _height + _margin  ]],
		[[ _width + _margin, _height + _margin  ]],
		[[ _width + _margin, _margin 			]],
	]
)

pts_dst = np.array( corners, np.float32 )

while True :

	if USE_CAM :
		ret, rgb = video_capture.read()
	else :
		ret = 1
		rgb = cv2.imread( "opencv.jpg", 1 )

	if ( ret ):

		gray = cv2.cvtColor( rgb, cv2.COLOR_BGR2GRAY )

		gray = cv2.bilateralFilter( gray, 1, 10, 120 )

		edges  = cv2.Canny( gray, 10, CANNY )

		kernel = cv2.getStructuringElement( cv2.MORPH_RECT, ( MORPH, MORPH ) )

		closed = cv2.morphologyEx( edges, cv2.MORPH_CLOSE, kernel )

		contours, h = cv2.findContours( closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE )

		for cont in contours:

			# Küçük alanları pass geç
			if cv2.contourArea( cont ) > 5000 :

				arc_len = cv2.arcLength( cont, True )

				approx = cv2.approxPolyDP( cont, 0.1 * arc_len, True )

				if ( len( approx ) == 4 ):
					IS_FOUND = 1
					#M = cv2.moments( cont )
					#cX = int(M["m10"] / M["m00"])
					#cY = int(M["m01"] / M["m00"])
					#cv2.putText(rgb, "Center", (cX, cY), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 3)

					pts_src = np.array( approx, np.float32 )

					h, status = cv2.findHomography( pts_src, pts_dst )
					out = cv2.warpPerspective( rgb, h, ( int( _width + _margin * 2 ), int( _height + _margin * 2 ) ) )

					cv2.drawContours( rgb, [approx], -1, ( 255, 0, 0 ), 2 )

				else : pass

		#cv2.imshow( 'closed', closed )
		#cv2.imshow( 'gray', gray )
		cv2.namedWindow( 'edges', cv2.CV_WINDOW_AUTOSIZE )
		cv2.imshow( 'edges', edges )

		cv2.namedWindow( 'rgb', cv2.CV_WINDOW_AUTOSIZE )
		cv2.imshow( 'rgb', rgb )

		if IS_FOUND :
			cv2.namedWindow( 'out', cv2.CV_WINDOW_AUTOSIZE )
			cv2.imshow( 'out', out )

		if cv2.waitKey(27) & 0xFF == ord('q') :
			break

		if cv2.waitKey(99) & 0xFF == ord('c') :
			current = str( time.time() )
			cv2.imwrite( 'ocvi_' + current + '_edges.jpg', edges )
			cv2.imwrite( 'ocvi_' + current + '_gray.jpg', gray )
			cv2.imwrite( 'ocvi_' + current + '_org.jpg', rgb )
			print "Pictures saved"

		time.sleep( DELAY )

	else :
		print "Stopped"
		break

if USE_CAM : video_capture.release()
cv2.destroyAllWindows()

# end


Üstteki kodlardan daha gelişmiş bir örneğe aşağıdaki eklentiyi indirerek sahip olabilirsiniz:

Dosyayı kaydet: opencv-ex10.zip [2,4 Mb] (İndirilme: 424)

OpenCV Kaynakları
1. Resmi Site: OpenCV | OpenCV
2. Resmi Github Sayfası: OpenCV | Github
3. Stackoverflow: opencv tagı ile birçok kaynak koda ulaşabilirsiniz.
+10

Yorumlar 22

  1. Ömer Faruk MENDİ
    Ömer Faruk MENDİ Tarih 11 Ekim 2017 22:20
    Mehmet bey merhabalar, öncelikle böyle bir konu için Türkçe yazılmış bir kaynak olması sevindirici, elinize sağlık. Tespit ettiğimiz dikdörtgenin dışarıya scale etmeden birebir aynı boyutta nasıl alabiliriz ?
  2. m.hanoglu Tarih 18 Ekim 2017 15:01
    Teşekkürler. Aslında scale işlemi yok pts_src kısmında resim üzerinden algılanan dötgenin köşe noktalarının koordinatları mevcut. Perspektif dönüşüm için sabit bir oran belirledim (sonraki aşamalarda lazım olacağı için)
    ABC Optik Okuyucu uygulamasında kullandığım bir algoritmadır fakat java ile yeniden yazdım. ( Kaynak kod paylaşma yetkim yok )
  3. erdem
    erdem Tarih 11 Kasım 2017 19:27
    hocam merhaba, diyelim elimizdeki resimde birsürü kare var sudoku da var içlerinde sudokuları kırmızıyla normal kareleri maviyle göstermek istiyoruz, nasıl ayırt edicez sudoku olup olmadığını yardımcı olabilir misiniz?
    Teşekkürler.
  4. m.hanoglu Tarih 13 Kasım 2017 15:51
    Haar Cascade yardımı olabilir mi bilmiyorum ama. Direkt olarak sudoku çözecek örnekler mevcut. Diğer karelerin nasıl olduğunu bilmeden net olarak bir şey söyleyemem..
  5. murat
    murat Tarih 27 Kasım 2017 21:55
    Hocam iyi günler. Okul projem için python(opencv) ile optik form okuma yapmam gerekiyor fakat grayscale,threshold,canny derken bi ilerleme kaydedemiyorum. Python da ilk defa kullanıyorum ve açıkçası birşeyler parlamıyor kafamda. Benim anladığım kadarı ile elimde bir optik cevap anahtarı olacak ve diğer optikler ile eşleştirme yapacağım. Bana bu konu hakkında biraz yardımcı olabilir misin?
    1. m.hanoglu Tarih 4 Aralık 2017 22:11
      İyi günler, yapmak istediğiniz uygulama Java/Python hangi dille yazılacak?
      Burada daha önce birkaç kişiye gönderdiğim yönergeler mevcut. Mutlaka işinize yarayacaktır.
      Python ile ufak denemeler yaparak mantığı anlamaya çalıştım, sonrasında uygulama yapmak için java ile yazdım tekrardan. Fakat projeyi iş olarak yaptığım için kaynak kodlarını paylaşma imkanım yok.
      OpenCV dokümanlarında filtreler hakkında ayrıntılı bilgiler verilmiş, onlardan faydalanabilirsiniz.
      Yardımcı olacak linkleri mail ile gönderiyorum.
  6. cem
    cem Tarih 25 Aralık 2017 11:21
    pyhton da daire ve dikdörtgen sekillerini ve bunların sayısını bulan bir uygulama yapmak istiyorum
    yardımcı olursanız sevinirim
    teşekkürler..
    1. m.hanoglu Tarih 29 Aralık 2017 17:28
      Yapmış olduğunuz kadarını paylaşırsanız veya takıldığınız noktalardan bahsederseniz yardımcı olabilirim..
  7. Mustafa cenk
    Mustafa cenk Tarih 25 Aralık 2017 18:28
    iyi günler hocam opencv ile hazırlanmış bir uygulamayı kullanabilmek için telefona opencv yüklemek mi gerekir
    1. m.hanoglu Tarih 29 Aralık 2017 17:23
      Aslında hem evet hem hayır, eğer uygulama kütüphaneyi kendi içinde barındırmıyorsa ayrıca yüklemiş olmak gerekir.

      Uygulama kendi içinde barındırmıyorsa: [url=https://play.google.com/store/apps/details?id=org.opencv.engine&hl=tr]
      OpenCV Manager[/url] yüklenmesini isteyecektir.

      Barındırıyorsa gerek kalmaz: [url=https://mehmethanoglu.com.tr/blog/19-abc-optik-okuyucu-mobil-optik-form-ok
      uyucu.html]ABCOptik Okuyucu[/url]

      Ayrıca, eğer uygulama tüm mimarilere uygun kütüphaneleri barındırırsa boyutu 50MB+ olacaktır. Bunun yerine Her mimari için ayrı APK oluşturmak mantıklı olur.
  8. Faruk
    Faruk Tarih 14 Ocak 2018 14:21
    Merhaba,
    Mehmet bey bir projem var yardımcı olabilirseniz sevinirim. Bir dikdörtgen alan oluşturup o alan içine düşen kırmızı alanın yüksekliğinin kaç milimetre olduğunu hesaplamak istiyorum. Python da yapacağım. Buna uygun hazır kod var mı?
    Teşekkür ederim
    1. m.hanoglu Tarih 15 Ocak 2018 11:01
      Merhaba,
      Öncelikle isteğinizi tam olarak anladığımı söyleyemem. Eğer bir kağıt üzerinden dörtgenlerin boyutunu hesaplamak istiyorsanız benzer bir yapı kullanılmalı. Fakat uzaklık için bilinen bir değer girilmeli. Örneğin kamera ile yüzey arasında 1m varsa hesaplamak mümkün. Elimde hazır kod yok bununla ilgili. Burada bir örnek mevcut: tıklayın
  9. Mustafa kankan
    Mustafa kankan Tarih 21 Ocak 2018 22:44
    Merhaba Mehmet Bey. Python'un hangi sürümünü kullanıyorsunuz acaba? 3.6.4 sürümünde kodları çalıştırmaya çalışıyorum ama hata veriyor.
    1. m.hanoglu Tarih 21 Ocak 2018 22:50
      Merhaba, 2 serisini kullanıyorum.
      1. mkankan
        mkankan Tarih 22 Ocak 2018 00:18
        Hocam web cam ile Optik okuyucu sistemi üzerine bir hayli kafa yormama rağmen ilerleme kaydedemedim. Zamanınız olursa bir kaç şey sormak istiyorum. Şimdi ben javascript ile web tabanlı olarak bir sistem tasarlıyordum. optik okuyucu sistemdeöncelik sanırım kamera ile dikdörtgeni algılatmak. İşte bundan sonra tam olarak ne yapmak gerekiyor. Yani dikdörtgeni (optik formu kenarlarını) algılattığımız zaman görüntüyü otomatik kaydedip sonra üzerinde mi işlem yaptırmak gerekiyor. Şimdiden teşekkür ederim.
        1. m.hanoglu Tarih 22 Ocak 2018 00:30
          Burdaki mantık şu;
          * İlk olarak dörtgeni algılatmak.
          * Sonra o dörtgene perspektif dönüşüm uygulamak gerekiyor. Bunun için de en-boy oranı bilinmeli.
          * Düzlemsel hale getirdikten sonra, parça parça resimden okuma işlemi yapmak gerekiyor. Yine burada her kutucuğun yeri belli olmalı. O yerlere göre renk yoğunluklarından işaretli-işaretsiz ayrımı yapılabilir.

          Okuma mantığını şu videodan daha iyi anlayabilirsiniz.
          Optik Okuma - Parselizasyon
  10. Mustafa kankan
    Mustafa kankan Tarih 22 Ocak 2018 12:44
    Traceback (most recent call last):
    File "C:\Python27\Çalışmalar\opencv-ex10.py", line 81, in <module>
    contours, h = cv2.findContours( closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE )
    ValueError: too many values to unpack

    Sebebini çözemedim bir türlü
  11. m.hanoglu Tarih 22 Ocak 2018 12:46
    Aldığınız hatalar için stackoverflow'u kullanabilirsiniz.
    Çözümü burada verilmiş
    opencv python value error too many values to unpack
  12. Ahmet Turan
    Ahmet Turan Tarih 26 Ocak 2018 23:32
    Mehmet Bey kamerayı nasıl aktif edebiliyoruz acaba. Resim ile çalışıyor ama kamerayı aktif edemiyorum.
    1. m.hanoglu Tarih 2 Şubat 2018 11:25
      USE_CAM = 1 ise kamerayı kullanmalı, bu şekilde olduğu halde kamera açılmıyorsa başka bir şey eksik olabilir. Net olarak cevap veremeyeceğim maalesef.
  13. Mustafa cenk
    Mustafa cenk Tarih 28 Nisan 2018 11:27
    İyi çalışmalar.
    Optik okuma uygulamanızdaki
    Cevap anahtarı giriş ekranı tasarımı gibi bir tasarım yapmak istiyorum.
    Nasıl yapıldığına dair bir örnek paylaşabilir misiniz acaba(20 soruluk 5 şık ve listview)
    Android studio platformu
    1. m.hanoglu Tarih 28 Nisan 2018 11:42
      Normal bir listview aslında. Tabi custom için adapter vs. yazılması gerekiyor.
      Asıl işlem layout kısmında. Her bir satırda soldan-sağa doğru sırayla TextView(Soru sayısını yazmak için), RadioGroup(Her bir şık RadioButton) grup olunca satırda sadece bir tanesi seçilebiliyor. Bunu kısa yoldan halletmiş oluyoruz.
      Her bir RadioButton için stil düzenlemesi yaparak form görünümü verebilirsiniz. Radius ve Border eklemesi ile yuvarlak olarak gösterebilirsiniz.
      Mantık olarak böyle. Kaynak kodu paylaşmak isterdim fakat proje tamamen bana ait olmadığı için mümkün değil.
Yorum ekle

Yorum ekle

    • winksmile
      laughing
      angry
Okunamayan kodu yenilemek için resmin üstüne tıklayınız