Làm cách nào để phát hiện các góc trong ảnh nhị phân với OpenGL?


13

Tôi có hình ảnh nhị phân 160x120 như:

ảnh gốc

Tôi muốn phát hiện các góc của những đốm trắng. Chúng trước đây bị đóng bởi hình thái toán học nên không có bất kỳ góc bên trong nào. Trong trường hợp cụ thể này, tôi muốn có 16 góc, như:

ví dụ về phát hiện góc

Nỗ lực đầu tiên của tôi là sử dụng một số hàm OpenCV như goodFeaturesToTrack hoặc FAST nhưng chúng đặc biệt chậm (cộng với FAST rất không ổn định). Ý tưởng của tôi sẽ là thực hiện một tính toán như vậy trên GPU, vì hình ảnh nguồn của tôi đến từ nó. Tôi đã tìm kiếm ý tưởng trên web về cách viết các shader như vậy (Tôi đang sử dụng OpenGL ES 2.0), nhưng không tìm thấy gì cụ thể. Bất cứ ý tưởng làm thế nào tôi có thể bắt đầu một thuật toán như vậy?


2
NHANH CHÓNG có chậm không? :)
endolith

1
vâng, buồn cười phải không? thực tế, nó nhanh hơn các thuật toán tiền lệ như SURF hoặc SIFT, nhưng nó kém chính xác hơn, khá không ổn định từ hình ảnh này sang hình ảnh khác và vẫn không đủ nhanh để thực hiện trên CPU
Stéphane Péchard

Làm thế nào quan trọng là phát hiện chính xác những điều này trên mỗi khung hình? Làm thế nào nhanh chóng làm cho hình chữ nhật di chuyển? Có thể phát hiện các góc trên hầu hết các khung và nội suy chúng trên các khung mà thuật toán bỏ lỡ không?
justis

@justis, cách tôi làm ngay bây giờ (thông qua việc sử dụng các hàm cvFindContours () và cvApproxPoly () của OpenCV không ổn định theo thời gian, vì vậy tôi lọc kết quả bằng bộ lọc thông thấp, giới thiệu độ trễ. Bạn có nghĩ rằng tôi có thể có được một kết quả ổn định hơn với phép nội suy không?
Stéphane Péchard

Câu trả lời:


3

Những hình ảnh kích thước bạn đang hoạt động trên? Ở tốc độ khung hình nào? Trên phần cứng nào? FAST là đẹp, erm, nhanh chóng trong kinh nghiệm của tôi.

Tôi cũng đã thấy FAST được sử dụng như một trình phát hiện ROI với goodFeaturesToTrack chạy trên ROI được xác định để cung cấp sự ổn định tốt hơn mà không phải chạy hình phạt của gFTT trên toàn bộ hình ảnh.

Trình phát hiện góc "Harris" cũng có khả năng rất nhanh vì nó được tạo thành từ các thao tác rất đơn giản (chẳng hạn như sqrt () trên mỗi pixel!) - không ổn định như gFTT, nhưng có thể nhiều hơn NHANH CHÓNG.

(Về mặt triển khai GPU, Googling gpu cornerdường như trình bày khá nhiều liên kết, nhưng tôi không biết chúng có thể phù hợp đến mức nào - tôi có xu hướng triển khai trong FPGA.)


Hình ảnh của tôi có kích thước 160x120, được cho là 30 khung hình / giây, trên iPhone, nhưng tất nhiên, ứng dụng này còn nhiều việc phải làm :-) Tôi đã thấy một ứng dụng triển khai FAST khá nhanh trên một thiết bị như vậy, nhưng đó chỉ là bản demo làm điều đó ... Đó là lý do tại sao tôi đang hướng tới các giải pháp dựa trên gpu.
Stéphane Péchard

15

Tôi tình cờ triển khai một cái gì đó như thế này trên OpenGL ES 2.0 bằng cách sử dụng tính năng phát hiện góc Harris và trong khi tôi chưa hoàn thành, tôi nghĩ tôi đã chia sẻ cách triển khai dựa trên shader mà tôi có cho đến nay. Tôi đã thực hiện điều này như là một phần của khung công tác nguồn mở dựa trên iOS , vì vậy bạn có thể kiểm tra mã nếu bạn tò mò về cách một số bước cụ thể hoạt động.

Để làm điều này, tôi sử dụng các bước sau:

  • Giảm hình ảnh xuống các giá trị độ chói của nó bằng cách sử dụng một sản phẩm chấm của các giá trị RGB với vectơ (0.2125, 0.7154, 0.0721).
  • Tính toán các đạo hàm X và Y bằng cách trừ các giá trị kênh màu đỏ từ các pixel trái và phải và trên và dưới pixel hiện tại. Sau đó, tôi lưu trữ đạo hàm x bình phương trong kênh màu đỏ, đạo hàm Y bình phương trong kênh màu xanh lá cây và sản phẩm của các dẫn xuất X và Y trong kênh màu xanh. Các shader mảnh cho điều này trông giống như sau:

    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);
    }
    

    trong đó các biến thể chỉ là tọa độ kết cấu bù theo mỗi hướng. Tôi xác định trước những thứ này trong shader đỉnh để loại bỏ kết cấu đọc phụ thuộc, vốn nổi tiếng là chậm trên các GPU di động này.

  • Áp dụng hiệu ứng làm mờ Gaussian cho hình ảnh phái sinh này. Tôi đã sử dụng một hiệu ứng làm mờ ngang và dọc riêng biệt và tận dụng tính năng lọc kết cấu phần cứng để thực hiện làm mờ chín lần chỉ với năm lần đọc kết cấu trên mỗi lần vượt qua. Tôi mô tả shader này trong câu trả lời Stack Overflow này .

  • Chạy tính toán phát hiện góc Harris thực tế bằng cách sử dụng các giá trị đạo hàm đầu vào mờ. Trong trường hợp này, tôi thực sự sử dụng phép tính được mô tả bởi Alison Noble trong Ph.D. luận án "Mô tả các bề mặt hình ảnh". Trình tạo bóng xử lý việc này trông giống như sau:

    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);
    }
    
  • Thực hiện triệt tiêu không tối đa cục bộ và áp dụng ngưỡng để làm nổi bật các pixel đi qua. Tôi sử dụng trình đổ bóng phân đoạn sau để lấy mẫu tám pixel trong vùng lân cận của pixel trung tâm và xác định xem đó có phải là mức tối đa trong nhóm đó hay không:

    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);
    }
    

Quá trình này tạo ra một bản đồ góc từ các đối tượng của bạn trông như thế này:

Bản đồ góc

Các điểm sau đây được xác định là các góc dựa trên sự triệt tiêu và ngưỡng không tối đa:

Các góc được xác định

Với các ngưỡng thích hợp được đặt cho bộ lọc này, nó có thể xác định tất cả 16 góc trong ảnh này, mặc dù nó có xu hướng đặt các góc một pixel hoặc hơn bên trong các cạnh thực tế của đối tượng.

Trên iPhone 4, tính năng phát hiện góc này có thể chạy ở 20 FPS trên các khung hình video 640x480 đến từ camera và iPhone 4S có thể dễ dàng xử lý video có kích thước đó ở mức 60+ FPS. Điều này sẽ nhanh hơn nhiều so với xử lý ràng buộc CPU cho một tác vụ như thế này, mặc dù hiện tại quá trình đọc lại các điểm bị ràng buộc bởi CPU và chậm hơn một chút so với yêu cầu.

Nếu bạn muốn thấy điều này trong thực tế, bạn có thể lấy mã cho khung công tác của tôi và chạy ví dụ FilterShowcase đi kèm với nó. Ví dụ phát hiện góc Harris có chạy video trực tiếp từ camera của thiết bị, mặc dù như tôi đã đề cập đến việc đọc lại các điểm góc hiện đang xảy ra trên CPU, điều này thực sự làm chậm điều này. Tôi cũng đang chuyển sang một quy trình dựa trên GPU cho việc này.


1
Rất đẹp! Tôi theo khuôn khổ của bạn trên github, có vẻ như thực sự thú vị, xin chúc mừng!
Stéphane Péchard

Bạn có một ví dụ ở đâu đó làm thế nào để lấy tọa độ góc thực sự trở lại CPU? Có một số cách GPU thông minh hay nó yêu cầu đọc lại và sau đó lặp trên CPU thông qua bitmap được trả về để tìm các pixel được đánh dấu?
Quasimondo

@Quasimondo - Tôi đã nghiên cứu sử dụng các kim tự tháp biểu đồ để trích xuất điểm: tevs.eu/files/vmv06.pdf để tránh lặp đi lặp lại giới hạn CPU trên các pixel để phát hiện góc. Gần đây đã bị phân tâm một chút, vì vậy chưa hoàn thành việc này, nhưng tôi muốn sớm thôi.
Brad Larson

Xin chào @BradLarson, tôi biết đây là một chủ đề rất cũ và cảm ơn bạn đã trả lời. Tôi vừa kiểm tra KGPUImageHarrisCornerDetection.m trong khung GPUImage. Để trích xuất vị trí góc từ hình ảnh, bạn đã sử dụng glReadPixels để đọc hình ảnh vào bộ đệm và sau đó lặp trên bộ đệm để lưu trữ các điểm có colotByte> 0 trong một mảng. Có cách nào để làm tất cả điều này trong GPU mà chúng ta không phải đọc hình ảnh trong bộ đệm và vòng lặp không?
Sahil Bajaj

1
@SahilBajaj - Một kỹ thuật tôi đã thấy (và chưa có thời gian để thực hiện) là sử dụng kim tự tháp biểu đồ để trích xuất nhanh các điểm từ các hình ảnh thưa thớt như thế này. Điều đó sẽ tăng tốc đáng kể này.
Brad Larson

3

Các máy dò góc "mạnh mẽ" như Shi-Tomasi và Moravec nổi tiếng là chậm. kiểm tra chúng ở đây - http://en.wikipedia.org/wiki/Corner_detection FAST có lẽ là máy dò góc nhẹ đủ tốt duy nhất. Bạn có thể cải thiện FAST bằng cách thực hiện triệt tiêu không tối đa - chọn đầu ra FAST với điểm "góc" tốt nhất (có một số cách trực quan để tính toán, bao gồm Shi-Tomasi và Moravec làm điểm số góc) Bạn cũng có thể lựa chọn từ một số máy dò FAST - từ FAST-5 đến FAST-12 và FAST_ER (cái cuối cùng có lẽ là quá lớn đối với thiết bị di động) Một cách khác là tạo FAST - lấy trình tạo mã FAST từ trang tác giả và huấn luyện nó trên tập hợp các hình ảnh có khả năng. http://www.edwardrosten.com/work/fast.html


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.