USB Kamera + DirectShow + OpenCV ve COM programlama

Başlatan picusta, 09 Haziran 2008, 00:58:41

picusta

Ugrastigim bir konuyu : C programlayan bir kisinin USB kamera'dan gelen görüntüyü islemek ve kaydetmek için ne yapmasi gerektigini anlatacagim.
USB kamera'dan görüntü elde etmek için DirectShow kütüphanesini kullandim. Yakin zaman kadar DirectShow DirectX SDK'nin içinde bulunuyordu, artik Platform SDK'nin içinde. öncelikle Platform SDK'yi microsoft'un sitesinden indirmeniz gerekiyor. Bunun için Windows'un orjinal olup olmadigi kontrol ediliyor.
DirectShow'u kullanmak için COM programlama yöntemini az çok bilmek lazim. Sahsen bilmiyordum ama mantigi anladiktan sonra kolayca kullaniliyor.
Kisacasi söyle :
COM'u baslatmak için bir fonksyon çagriyoruz :
CoInitialize(NULL);
Bu fonksyonu COM kullanan her Thread çagirmali.
isimiz bittikten sonra CoUninitialize(); fonksyonu çagriliyor.
COM 'un içinde birbirleri ile iletisim kuran nesneler var.
Bir nesneyi yaratmak için CoCreateInstance(.. ) fonksyonu çagrilir.
Her nesne sinifinin bir numarasi vardir class ID denir ve CLSID_NESNEADI olarak tanimlanmis makrosu bulunur.
Bir nesne birçok arabirime sahip olabilir, bu arabirimler farkli islevler saglar.
Bir nesnenin arabirimini elde etmek için QueryInterface metodu kullanilir. Arabirimlerin nesneler gibi numaralari vardir , interface ID ve makrolar IID_ARABIRIMADI tarzinda tanimlanmistir.
Gelelim DirectShow ve bizi ilgilendiren kisma.
Mantik sudur:  bir veri akisi semasi olusturmak ve o semayi baslatip durdurmak.
öncelikle semayi yöneten bir nesne vardir (IGraphBuilder sinifindan).
Semayi olusuturan nesneler ise filtre gibidir (IBaseFilter sinifindan).
Filtrelerin 0,1 veya birçok giris / çikis olabilir, bu girislere Pin deniyor (IPin sinifi) ve her filtrede en az bir tane vardir.
çalisan bir sema en az bir üretici filtre birde tüketici filtre barindirir.
Semayi olusturmanin ve çalistirmanin 3 temel yöntemi vardir, birincisi ve en kolayi DirectShow ile gelen Graphedit adli bir grafik editörünü kullanmak. Daha sonra sema yönetecisi sema dosyasini okuyup grafigi olusturacaktir.
2. yöntem ise tamamen programlayarak : bütün filtreleri tek tek olusturmak ve birbirine baglamak. üçüncüsü ise IntelligentConnect sistemini kullanip ve bir kisim filtrelerin (encoder, decoder format degisimi vs..) kendiliginden semaya eklenmesi. Bu yöntem sayesinde kolayca bir MediaPlayer programlanabilir, sadece giris ve çikis filtreleri gerekli gerisi otomatik olarak semaya eklenecektir.

Bu asamada webcam'dan gelen görüntüyü pencerede gösterebiliriz, ve kayit edebiliriz.
Gelelim Directshow ile OpenCV'yi tanistirmaya.
OpenCV açik kaynak görüntü isleme kütüphanesidir. içinde barindirdigi Highgui kütüphanesini kullanarak ve DirectShow kullanmadan Webcam'dan gelen görüntüyü isleyerek ayri bir pencerede gösterebilirsiniz fakat DirectShow'un sundugu kontrolü elde edemezsiniz, ufak uygulamalarda kullanilabilir.
OpenCV COM nesnesi olan ProxyTrans isimli filtreyi sunar. Biz bu filtreyi DirectShow semamiza eklersek, kendi görüntü islememizi de semaya eklemis oluruz. Bu filtre, semamizin islerken bizim belirledigimiz bir callback fonksyonunun çagrilmasini saglar.
Kisacasi bu fonksyon webcam'dan gelen her görüntü için çagrilacak, ve fonksyon parametre olarak görüntünü OpenCV formatindaki (IplImage) pointer'ini alir.
Bu fonksyonda OpenCV'nin istediginiz fonksyonunu kullanabilir isleyebilir ve hatta degisitirirseniz (üzerine dikdörtgen çizmek, yazi yazmak vs...) filtrenin çikisinda'da görüntüyü degismis olarak elde edersiniz.
3 gün boyunca internette bu konuda arastirma yaptim ve program yazdim.
MFC ve C++ kullanmamak gibi kisitlamalar olunca biraz daha zor oluyor.
Ugrasacak arkadaslara simdiden kolay gelsin.
DirectShow, COM programlama:
http://www.flipcode.com/archives/DirectShow_For_Media_Playback_In_Windows-Part_I_Basics.shtml
OpenCV + DirectShow :
http://www.site.uottawa.ca/~laganier/tutorial/opencv+directshow/

atioky_216

usta sen ne yaptın oyle ben de C biliyorum zannediyordum... :lol:
Maşaallah yeni çalısmalarını bekleriz...

vsalma

Ellerinize sağlık. Hocam sdk sı olmayan bir ip cameradan opencv içine görüntü alabilirmiyiz. Highguinin desteklemediği bir cam. Sanırım DirectShow ile yapılıyor ancak ben aradan çekemedim.

picusta

Simdi PC'ye bagli bir kamerayi (USB , IP, TV karti, video ...) bir kaynak filtre olarak semaya koymak için "hardware enumeration" yöntemi kullaniliyor.
Onun için DirectShow'dan PC'ye bagli bütün kameralari saymasini istiyorsunuz ve bu kameralarin özelliklerini bir liste halinde elde ediyorsunuz, örnegin adi, pin isimleri ve diger özellikler.
Bu özelliklerden yola çikarak, örnegin kamera adi, seçtiginizi semaya koyuyorsunuz. Daha sonra kamera filtresinin pininden görüntünün formatini ve çözünürlügünü saymasini istiyorsunuz. Bu sayma isleminde IEnum, IMoniker arabirimlerini kullaniyorsunuz.
Platform SDK (Windows SDK veya eski DirectX SDK sürümü)'yi kurunca DirectShow SDK ile gelen bir sürü örnek proje var. Onlarda bu islemlerin nasil yapildigi gösterilmis (PlayCap ve PlayCapMoniker projelerinde).
Kolay gelsin, takilan olursa yardimci olurum.

fatihsatir

Merhaba arkadaşlar, özelde mesajlaştığımız konuyu buraya taşıyarak buradan devam edelim dedik, bu konuda benim gibi bilgi almak isteyenler okusun diye.

fatihsatir demiş ki:
Merhabalar, KTU Elektrik-Elektronik Mühendisliği 4.sınıfta okumaktayım. 2009 ta teslim edilmek üzere tez projesi aldım. Bazı noktalarda sıkıntılar yaşamaktayım. Özellikle 4 adet CCD kameradan programa görüntü almakta. Bu konudaki bazı sorularıma cevap verip yardımcı olabilirmisiniz?
Şimdiden Teşekkürler

picusta demişki:
Sana yardimci olabilmek için önce senin benim sorularima cevap vermen gerekiyor:
1. Kameralar nereye bagli? (Bilgisayar, mikorislemci, fpga)
2. Arabirim nedir ? (TV karti, USB vs..)
2. Elde edilen görüntü ile programda ne yapilmak isteniyor ? (saklama, isleme vs..)

fatihsatir demiş ki:
Bu soruların cevabını zaten söyleyecektim, sadece yardımcı olabilip olamayacağınızı sormak istemiştim. 4 adet kamera bilgisayara bağlı olacak fakat benim sorunum bundan sonra başlıyor. Daha önce hep webcam üzerinde çalışmıştım ve bu sebepten dolayı arabirime gerek kalmıyordu fakat sizce bu 4 adet kameradan görüntüleri neyse alabilirim. Yapacağım işlem ise kayıttan ziyade görüntü işleme fakat kayıt özelliğide ekliyebilirim.

picusta demiş ki:
Bu kameralardan gelen sinyali ögrenmelisin (TV olabilir PAL/SECAM olayina da bak) buna göre PC'ye bir (veya birkaç) capture karti takmalisin.
Yazilim kismi için DirectShow kullanmalisin. Görüntü isleme için ne kullanacaksin? OpenCV'mi baska bir kütüphanen var mi?
Iyi denk geldin, bu konu ile ilgili bir baslik baslatmistim zaten, oradan devam edebiliriz.

fatihsatir demiş ki:
Kameralardan gelen sinyal PAL olucak ve piksel sayıları ise 681x582 olan CCD kamera. Bunlardan 4 adet kulanmam gerekiyor. DirectShow daha önce kullanmadım fakat öğrenebilirim. Sıkıntılı olduğun nokta capture card olarak ne kullanacağım. DVR kartı alsam acaba işimi görür mü ve bu kameralardaki görüntüyü programa alabilecekmiyim. Görüntüleri aldıktan sonrasında rahatlıkla işleyebilirim fakat capture olayını nasıl çözeceğimi bilmiyorum. Bu arada forumda açtığın konunun linkini gönderirmisiniz, oradan devam edelim.!
Dünya güzelliklerine dalıp ahiret hayatını unutma..!

ise

capture kart kullansanda iş bitmiyor.  kendi yazılımı driver ile beraber geliyor . kendi yazdığın yazılıma adapte etmen zor olabilir. driverin api kullanımını tercih etmek daha makul fakat driver manuel nerden bulunur bilemiyorum. çünkü kanal seçimide yazılımla yapılacak.

fatihsatir

Alıntı yapılan: "ise"capture kart kullansanda iş bitmiyor.  kendi yazılımı driver ile beraber geliyor . kendi yazdığın yazılıma adapte etmen zor olabilir. driverin api kullanımını tercih etmek daha makul fakat driver manuel nerden bulunur bilemiyorum. çünkü kanal seçimide yazılımla yapılacak.

Evet dediğin gibi DVR kartlarıyla yazılımlarıda geliyor. Programın API'lerini kullanarak görüntüyü almak zor ve zahmet verici olur herhalde. Peki bu işi nasıl çözebiliriz.?
Dünya güzelliklerine dalıp ahiret hayatını unutma..!

picusta

Karti iyice kurduktan sonra (driver'leri vs..) DirectShow'dan görüntüyü elde etmek için hiçbir engel yoktur.
Kart Vfw veya WDM aygit modelini destekliyorsa (kartlarin çogunlugu), istedigin kanali ayarlarsin, Autotune edersin, hatta Teletext ve diger bilgileri (XDS) alabilirsin.
IAMTvTuner arabirimini incelemelisin (IAMTuner arabiriminden gelme).
Bu arabirimin StoreAutoTune ve put_Channel metodlarini kullan.
Bu filtreden sonra birde "Analog Video Crossbar" filtresini koy.
API yerine DirectShow'u kullanmanin performansi etkileyecegini sanmiyorum, çünkü Kernel Streaming oldugu için veriler kullanici alanina gelmeden aktariliyor.

picusta

Iste yukaridakileri kullanarak yapilmis bir proje.
Siparis hazirlama'dan sonra kutu içerigini kaydediyor.
Her kutu bir barkod ile iliskili. Alinan görüntüden bu barkod okunuyor, ayrica  ürünlerin bir kisminda Data Matrix var onlarda okunuyor.
ürünlerin yerinde olup olmadigini kontrol etmek için ROI'nin histogramina bakiliyor ve referans histogrami ile korelasyonu hesaplaniyor.

OptimusPrime

eyvah bana yeni oyuncak çıktı.
şimdi bu işe merak sardım.
hatta dün eski bir web cam ı parçalasanmı yoksa işime yarar mı diye düşünyordum. Allah tan parçalamamışım.
konuyu şöyle bir araştırayın sonra başınızı ağrıtırım. ;)
https://donanimveyazilim.wordpress.com || Cihân-ârâ cihân içredir ârâyı bilmezler, O mâhîler ki deryâ içredir deryâyı bilmezler ||

picusta

Alıntı Yapselamlar,
sizin kameradan aldığınız görüntülerin üzerine şekiller çizebildiğinizi gördüm. bunu nasıl yapıyorsunuz? ben diskten oynatılan bir video üzerine bir kaç text yazmak istiyorum bunu nasıl yapabilirm. bana bir örnek kod verebilir misiniz? veya bir yol gösterebilir misiniz?
saygılar.
Bu uygulamada söyle bir yöntem izledim :
OpenCV kullandigim içindirect show graphlarina (capture ve preview) OpenCV'nin filtresini ekledim. Filtrede hem islem yapiyorum (capture kismi),  hem de openCV'nin komutlari ile preview kismina kare, yazi vs... çiziyorum.
Baska bir uygulamada ise baska bir yöntem izledim :  OpenCV filtresi kullanmadim, Dshow'un Grabber filtresini koydum ve callback tanimladim, bu callback'in içinde resime dogrudan müdahale ettim.

EDIT :
Dedigini yapan hazir kod örnegini Direct Show SDK kurduktan sonra örnek dosyasinin içinde bulabilirsin, "watermark" olarak ara.

OptimusPrime

direct show winsdk da içerisindeymiş. az önce indirdim ve bahsettiğiniz örneği buldum. peki bu kodları derleyen program hangisidir. visual studio olabilir mi?
https://donanimveyazilim.wordpress.com || Cihân-ârâ cihân içredir ârâyı bilmezler, O mâhîler ki deryâ içredir deryâyı bilmezler ||

picusta

Varsa Visual Studio ile açabilirsin.
Yoksa Visual C++ Express 2008 ücretsiz indirip kullanabilirsin.
Projeler Visual Studio 2003 formatinda olabilir, o yüzden projeyi ilk kez açarken convert islemi gerçeklesebilir. Proje dosyalari "*.dsw, *dsp, *.sln *vcproj" gibi uzantilar ile bitiyor, onlari açmalisin.

OptimusPrime

VS2008 bulduğum bazı örnekleri açamadı. VS2005 indireyim derkende rapidden eğitim dvd sini indirmişim. şimdi yeniden imndirmeye başladım.
bakalım hayırlısı. bu arada rica etsem bana herhangi bir player da çalan videonun tam ortasına bir iki lakırtı yazan bir program örneği verebilir misiniz. watermark dediğiniz örnek sanırım kendi playerinı oluşturup videonun köşesinde bir resim gösteriyor.
teşekkürler.
https://donanimveyazilim.wordpress.com || Cihân-ârâ cihân içredir ârâyı bilmezler, O mâhîler ki deryâ içredir deryâyı bilmezler ||

picusta

Bu isi en son DirectShowNet kullanarak yaptim.  C#kodu ile yaplmis tam senin istedigin gibi bir örnek proje var :
Alıntı Yap
DxLogo - Capture video and add a watermark
A sample application showing how to süperimpose a logo on a data stream. It uses
a capture device for the video source, and outputs the result to a file.

ISampleGrabber filtresinin callback'inde istedigini çizebiliyorsun :
       /// <summary> buffer callback, COULD BE FROM FOREIGN THREAD. </summary>
        int ISampleGrabberCB.BufferCB( double SampleTime, IntPtr pBuffer, int BufferLen )
        {
            // Avoid the possibility that someone is calling SetLogo() at this instant
            lock (this)
            {
                if (m_bmdLogo != null)
                {
                    IntPtr ipSource = m_bmdLogo.Scan0;
                    IntPtr ipDest = pBuffer;

                    for (int x=0; x < m_bmdLogo.Height; x++)
                    {
                        CopyMemory(ipDest, ipSource, (uint)m_bmdLogo.Stride);
                        ipDest = (IntPtr)(ipDest.ToInt32() + m_stride);
                        ipSource = (IntPtr)(ipSource.ToInt32() + m_bmdLogo.Stride);
                    }
                }
            }

            return 0;
        }