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:
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:
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.