Bagaimana mendeteksi sudut dalam gambar biner dengan OpenGL?

13

Saya memiliki gambar biner 160x120 seperti:

gambar asli

Saya ingin mendeteksi sudut gumpalan putih itu. Mereka sebelumnya ditutup oleh morfologi matematika sehingga tidak boleh memiliki sudut dalam. Dalam kasus khusus ini, saya ingin 16 sudut, seperti:

contoh deteksi sudut

Upaya pertama saya menggunakan beberapa fungsi OpenCV seperti goodFeaturesToTrack atau FAST tetapi mereka sangat lambat (ditambah FAST sangat tidak stabil). Ide saya adalah melakukan perhitungan seperti itu pada GPU, karena sumber gambar saya berasal dari itu. Saya mencari ide di web tentang cara menulis shader seperti itu (saya menggunakan OpenGL ES 2.0), tetapi tidak menemukan apa pun yang konkret. Adakah yang tahu bagaimana saya bisa memulai algoritma seperti itu?

Stéphane Péchard
sumber
2
CEPAT lambat? :)
endolith
1
ya lucu kan? pada kenyataannya, ini lebih cepat daripada algoritma sebelumnya seperti SURF atau SIFT, tetapi kurang tepat, cukup tidak stabil dari satu gambar ke yang lain dan masih belum cukup cepat untuk dilakukan pada CPU
Stéphane Péchard
Seberapa pentingkah untuk secara akurat mendeteksi ini pada setiap frame? Seberapa cepat persegi panjang bergerak? Apakah boleh untuk mendeteksi sudut pada sebagian besar frame dan menginterpolasinya pada frame di mana algoritme tidak ada?
justis
@ cukup baik, cara saya melakukannya sekarang (melalui penggunaan cvFindContours () dan fungsi cvApproxPoly () OpenCV) tidak terlalu stabil dari waktu ke waktu, jadi saya memfilter hasilnya dengan filter low-pass, memperkenalkan lag. Apakah Anda pikir saya bisa mendapatkan hasil yang lebih stabil dengan interpolasi?
Stéphane Péchard

Jawaban:

3

Ukuran gambar apa yang Anda operasikan? Berapa frame rate? Pada perangkat keras apa? CEPAT cantik, eh, cepat dalam pengalaman saya.

Saya juga melihat FAST digunakan sebagai detektor ROI dengan goodFeaturesToTrack dijalankan pada ROI yang diidentifikasi untuk memberikan stabilitas yang lebih baik tanpa menjalankan penalti gFTT pada keseluruhan gambar.

The "Harris" sudut detektor juga berpotensi sangat cepat seperti yang terdiri dari operasi yang sangat sederhana (tidak ada sqrt () per pixel misalnya!) - tidak stabil seperti gFTT, tapi mungkin lebih daripada CEPAT.

(Dalam hal implementasi GPU, Googling gpu cornertampaknya menghadirkan cukup banyak tautan, tetapi saya tidak tahu seberapa cocok mereka - saya cenderung menerapkannya dalam FPGA.)

Martin Thompson
sumber
Gambar saya 160x120, seharusnya pada 30fps, pada iPhone, tetapi tentu saja, aplikasi ini memiliki banyak hal yang harus dilakukan :-) Saya telah melihat aplikasi yang mengimplementasikan FAST dengan cukup cepat pada perangkat seperti itu, tetapi itu hanya demo saja melakukan itu ... Itu sebabnya saya mencari solusi berbasis GPU.
Stéphane Péchard
15

Saya kebetulan mengimplementasikan sesuatu seperti ini pada OpenGL ES 2.0 menggunakan deteksi sudut Harris, dan sementara saya belum sepenuhnya selesai, saya pikir saya akan berbagi implementasi berbasis shader yang saya miliki sejauh ini. Saya telah melakukan ini sebagai bagian dari kerangka kerja open source berbasis iOS , sehingga Anda dapat memeriksa kode jika Anda ingin tahu bagaimana beberapa langkah tertentu bekerja.

Untuk melakukan ini, saya menggunakan langkah-langkah berikut:

  • Kurangi gambar ke nilai luminansinya menggunakan produk titik dari nilai RGB dengan vektor (0,2125, 0,7154, 0,0721).
  • Hitung turunan X dan Y dengan mengurangi nilai saluran merah dari piksel kiri dan kanan dan di atas dan di bawah piksel saat ini. Saya kemudian menyimpan turunan x kuadrat di saluran merah, turunan Y kuadrat di saluran hijau, dan produk turunan X dan Y di saluran biru. Shader fragmen untuk ini terlihat seperti berikut:

    precision highp float;
    
    varying vec2 textureCoordinate;
    varying vec2 leftTextureCoordinate;
    varying vec2 rightTextureCoordinate;
    
    varying vec2 topTextureCoordinate; 
    varying vec2 bottomTextureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    void main()
    {
     float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
     float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
     float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
     float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
    
     float verticalDerivative = abs(-topIntensity + bottomIntensity);
     float horizontalDerivative = abs(-leftIntensity + rightIntensity);
    
     gl_FragColor = vec4(horizontalDerivative * horizontalDerivative, verticalDerivative * verticalDerivative, verticalDerivative * horizontalDerivative, 1.0);
    }
    

    di mana variasi hanyalah koordinat tekstur offset di setiap arah. Saya menghitung ulang ini dalam vertex shader untuk menghilangkan pembacaan tekstur dependen, yang terkenal lambat pada GPU seluler ini.

  • Terapkan blur Gaussian ke gambar turunan ini. Saya menggunakan blur horizontal dan vertikal yang terpisah, dan memanfaatkan penyaringan tekstur perangkat keras untuk melakukan blur sembilan-hit dengan hanya lima tekstur yang dibaca pada setiap lintasan. Saya menggambarkan shader ini dalam jawaban Stack Overflow ini .

  • Jalankan perhitungan deteksi sudut Harris yang sebenarnya menggunakan nilai turunan input kabur. Dalam hal ini, saya sebenarnya menggunakan perhitungan yang dijelaskan oleh Alison Noble di Ph.D. disertasi "Deskripsi Permukaan Gambar". Shader yang menangani ini terlihat seperti berikut:

    varying highp vec2 textureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    const mediump float harrisConstant = 0.04;
    
    void main()
    {
     mediump vec3 derivativeElements = texture2D(inputImageTexture, textureCoordinate).rgb;
    
     mediump float derivativeSum = derivativeElements.x + derivativeElements.y;
    
     // This is the Noble variant on the Harris detector, from 
     // Alison Noble, "Descriptions of Image Surfaces", PhD thesis, Department of Engineering Science, Oxford University 1989, p45.     
     mediump float harrisIntensity = (derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z)) / (derivativeSum);
    
     // Original Harris detector
     //     highp float harrisIntensity = derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z) - harrisConstant * derivativeSum * derivativeSum;
    
     gl_FragColor = vec4(vec3(harrisIntensity * 10.0), 1.0);
    }
    
  • Lakukan penekanan lokal non-maksimum dan terapkan ambang batas untuk menyorot piksel yang lewat. Saya menggunakan shader fragmen berikut untuk sampel delapan piksel di sekitar piksel pusat dan mengidentifikasi apakah itu maksimum dalam pengelompokan itu:

    uniform sampler2D inputImageTexture;
    
    varying highp vec2 textureCoordinate;
    varying highp vec2 leftTextureCoordinate;
    varying highp vec2 rightTextureCoordinate;
    
    varying highp vec2 topTextureCoordinate;
    varying highp vec2 topLeftTextureCoordinate;
    varying highp vec2 topRightTextureCoordinate;
    
    varying highp vec2 bottomTextureCoordinate;
    varying highp vec2 bottomLeftTextureCoordinate;
    varying highp vec2 bottomRightTextureCoordinate;
    
    void main()
    {
        lowp float bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).r;
        lowp float bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
        lowp float bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
        lowp vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
        lowp float leftColor = texture2D(inputImageTexture, leftTextureCoordinate).r;
        lowp float rightColor = texture2D(inputImageTexture, rightTextureCoordinate).r;
        lowp float topColor = texture2D(inputImageTexture, topTextureCoordinate).r;
        lowp float topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).r;
        lowp float topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
    
        // Use a tiebreaker for pixels to the left and immediately above this one
        lowp float multiplier = 1.0 - step(centerColor.r, topColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, topLeftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, leftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, bottomLeftColor);
    
        lowp float maxValue = max(centerColor.r, bottomColor);
        maxValue = max(maxValue, bottomRightColor);
        maxValue = max(maxValue, rightColor);
        maxValue = max(maxValue, topRightColor);
    
        gl_FragColor = vec4((centerColor.rgb * step(maxValue, centerColor.r) * multiplier), 1.0);
    }
    

Proses ini menghasilkan peta cornerness dari objek Anda yang terlihat seperti ini:

Peta Cornerness

Poin-poin berikut diidentifikasi sebagai sudut berdasarkan penindasan dan ambang batas yang tidak maksimal:

Sudut diidentifikasi

Dengan ambang yang tepat ditetapkan untuk filter ini, ini dapat mengidentifikasi semua 16 sudut dalam gambar ini, meskipun cenderung menempatkan sudut-sudut piksel atau lebih di dalam tepi sebenarnya dari objek.

Pada iPhone 4, deteksi sudut ini dapat dijalankan pada 20 FPS pada 640x480 frame video yang berasal dari kamera, dan iPhone 4S dapat dengan mudah memproses video dengan ukuran itu pada 60+ FPS. Ini seharusnya jauh lebih baik daripada pemrosesan yang terikat CPU untuk tugas seperti ini, meskipun saat ini proses membaca kembali poin-poinnya adalah terikat-CPU dan sedikit lebih lambat dari yang seharusnya.

Jika Anda ingin melihat ini dalam tindakan, Anda dapat mengambil kode untuk kerangka kerja saya dan menjalankan contoh FilterShowcase yang menyertainya. Contoh deteksi sudut Harris ada berjalan pada video langsung dari kamera perangkat, meskipun seperti yang saya sebutkan pembacaan titik sudut saat ini terjadi pada CPU, yang benar-benar memperlambat ini. Saya pindah ke proses berbasis GPU untuk ini, juga.

Brad Larson
sumber
1
Sangat bagus! Saya mengikuti kerangka Anda di github, sepertinya sangat menarik, selamat!
Stéphane Péchard
Apakah Anda memiliki contoh di suatu tempat bagaimana cara mendapatkan koordinat sudut kembali ke CPU? Apakah ada beberapa cara GPU pintar atau apakah itu memerlukan pembacaan kembali dan kemudian pengulangan pada CPU melalui bitmap yang dikembalikan mencari piksel yang ditandai?
Quasimondo
@Quasimondo - Saya telah bekerja menggunakan piramida histogram untuk ekstraksi titik: tevs.eu/files/vmv06.pdf untuk menghindari iterasi yang terikat CPU lebih dari piksel untuk deteksi sudut. Sudah sedikit terganggu akhir-akhir ini, jadi belum menyelesaikan ini, tapi saya ingin segera.
Brad Larson
Hai @BradLarson, saya tahu ini adalah utas yang sangat lama dan terima kasih atas jawaban Anda. Saya baru saja memeriksa KGPUImageHarrisCornerDetection.m dalam kerangka GPUImage. Untuk mengekstrak lokasi sudut dari gambar, Anda telah menggunakan glReadPixels untuk membaca gambar ke dalam buffer dan kemudian memutar buffer untuk menyimpan poin dengan colotByte> 0 dalam sebuah Array. Apakah ada cara untuk melakukan ini semua dalam GPU di mana kita tidak perlu membaca gambar di buffer and loop?
Sahil Bajaj
1
@SahilBajaj - Salah satu teknik yang saya lihat (dan belum punya waktu untuk mengimplementasikan) adalah dengan menggunakan histogram piramida untuk melakukan ekstraksi cepat poin dari gambar jarang seperti ini. Itu akan mempercepat ini secara signifikan.
Brad Larson
3

Detektor sudut "Kuat" seperti Shi-Tomasi dan Moravec sangat lambat. periksa di sini - http://en.wikipedia.org/wiki/Corner_detection CEPAT mungkin adalah satu-satunya detektor sudut ringan yang cukup bagus. Anda dapat meningkatkan FAST dengan melakukan penindasan non-maksimal - pilih output FAST dengan skor "cornerness" terbaik (ada beberapa cara intuitif untuk menghitungnya, termasuk Shi-Tomasi dan Moravec sebagai skor cornerness) Anda juga memiliki pilihan dari beberapa detektor FAST - dari FAST-5 ke FAST-12 dan FAST_ER (yang terakhir mungkin terlalu besar untuk seluler) Cara lain adalah dengan mendapatkan FAST - dapatkan pembuat kode FAST dari situs pembuat dan latih di set gambar yang mungkin. http://www.edwardrosten.com/work/fast.html

mirror2image
sumber