OpenGL을 사용하여 이진 이미지에서 모서리를 감지하는 방법은 무엇입니까?


13

다음과 같은 이진 160x120 이미지가 있습니다.

원본 이미지

하얀 얼룩의 모서리를 감지하고 싶습니다. 그것들은 이전에 수학적 형태로 닫혀 있으므로 내부 모서리가 없어야합니다. 이 특정 경우에는 다음과 같이 16 개의 모서리를 원합니다.

모서리 감지의 예

내 첫 번째 시도는 goodFeaturesToTrack 또는 FAST 와 같은 일부 OpenCV 기능을 사용 했지만 특히 느립니다 (플러스 FAST는 매우 불안정합니다). 내 아이디어는 내 소스 이미지에서 나온 것처럼 GPU에서 그러한 계산을 수행하는 것입니다. 웹에서 그러한 쉐이더를 작성하는 방법에 대한 아이디어를 찾았지만 (OpenGL ES 2.0을 사용하고 있습니다) 구체적인 내용은 없습니다. 어떻게 그런 알고리즘을 시작할 수 있을지 아십니까?


2
빠른가? :)
endolith

1
네, 재밌어요? 실제로 SURF 나 SIFT와 같은 선행 알고리즘보다 빠르지 만
정확도가 떨어지고

모든 프레임에서이를 정확하게 감지하는 것이 얼마나 중요합니까? 직사각형은 얼마나 빨리 이동합니까? 대부분의 프레임에서 모서리를 감지하고 알고리즘이 누락 된 프레임에서 보간하는 것이 괜찮습니까?
justis

@justis는, OpenCV의 cvFindContours () 및 cvApproxPoly () 함수를 사용하여) 지금 당장하는 방식은 시간이 지남에 따라 매우 안정적이지 않으므로 저역 통과 필터로 결과를 필터링하여 지연을 유발합니다. 보간으로 더 안정적인 결과를 얻을 수 있다고 생각하십니까?
Stéphane Péchard

답변:


3

어떤 크기의 이미지를 조작하고 있습니까? 어떤 프레임 속도로? 어떤 하드웨어에? FAST는 내 경험상 꽤 예쁘고 빠릅니다.

또한 FAST가 전체 이미지에서 gFTT의 페널티를 실행하지 않고 더 나은 안정성을 제공하기 위해 ROI에서 goodFeaturesToTrack을 실행하는 ROI 검출기로 사용되는 것을 보았습니다.

"해리스 '코너 검출기는 그것이 매우 간단한 작업으로 구성되어 잠재적으로 매우 빠른 또한 (예를 들어, 픽셀 당 SQRT ()!) -하지 gFTT 안정적으로하지만, 아마도 더 그렇게 FAST보다.

(GPU 구현 측면에서, 인터넷 검색 gpu corner은 많은 링크를 제공하는 것으로 보이지만, 그것이 얼마나 적합한 지 잘 모르겠습니다. FPGA로 구현하는 경향이 있습니다.)


내 이미지는 iPhone에서 160x120, 아마도 30fps이지만 물론 응용 프로그램에는 더 많은 작업이 필요합니다 .-) 그러한 장치에서 FAST를 매우 빠르게 구현하는 앱을 보았지만 데모 일뿐입니다. 저것은 ... 그래서 내가 gpu 기반 솔루션을 찾고 있습니다.
Stéphane Péchard

15

방금 Harris 코너 감지를 사용하여 OpenGL ES 2.0에서 이와 같은 것을 구현하고 있었고 완전히 완료되지는 않았지만 지금까지 쉐이더 기반 구현을 공유한다고 생각했습니다. iOS 기반 오픈 소스 프레임 워크일부로이 작업을 수행 했으므로 특정 단계가 어떻게 작동하는지 궁금한 경우 코드를 확인할 수 있습니다.

이를 위해 다음 단계를 사용합니다.

  • 벡터 (0.2125, 0.7154, 0.0721)를 사용하여 RGB 값의 내적을 사용하여 이미지를 광도 값으로 줄입니다.
  • 현재 픽셀의 좌우 상하의 픽셀에서 적색 채널 값을 빼서 X 및 Y 미분을 계산합니다. 그런 다음 x 도함수를 적색 채널에 제곱하고 Y 도함수를 녹색 채널에 제곱하고 X 및 Y 도함수의 곱을 파란색 채널에 저장합니다. 이를위한 프래그먼트 셰이더는 다음과 같습니다.

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

    여기서 변화는 각 방향의 오프셋 텍스처 좌표입니다. 버텍스 쉐이더에서 미리 계산하여 의존적 인 텍스처 읽기를 제거합니다. 이러한 모바일 GPU에서는 속도가 느립니다.

  • 이 미분 이미지에 가우시안 블러를 적용합니다. 분리 된 수평 및 수직 블러를 사용하고 하드웨어 패스 필터링을 활용하여 각 패스에서 5 회의 텍스처 판독으로 9 회 히트 블러를 수행합니다. 나는 이 스택 셰이더를 이 스택 오버 플로우 답변 에서 설명합니다 .

  • 흐릿한 입력 미분 값을 사용하여 실제 해리스 코너 감지 계산을 실행합니다. 이 경우, 저는 실제로 앨리슨 노블 (Alison Noble)이 박사 학위에 설명 된 계산을 사용하고 있습니다. 논문 "이미지 표면의 설명". 이를 처리하는 셰이더는 다음과 같습니다.

    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);
    }
    
  • 로컬 비 최대 억제를 수행하고 임계 값을 적용하여 통과하는 픽셀을 강조 표시하십시오. 다음 조각 셰이더를 사용하여 중앙 픽셀 근처에있는 8 개의 픽셀을 샘플링하고 해당 그룹에서 최대 값인지 여부를 식별합니다.

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

이 프로세스는 다음과 같은 객체로부터 코너 맵을 생성합니다.

코너 맵

다음 포인트는 최대 억제 및 임계 값을 기준으로 코너로 식별됩니다.

식별 된 코너

이 필터에 대해 적절한 임계 값을 설정하면이 이미지에서 16 개의 모서리를 모두 식별 할 수 있지만 모서리는 객체의 실제 가장자리 내부에 픽셀을 배치하는 경향이 있습니다.

iPhone 4에서이 코너 감지는 카메라에서 나오는 640x480 프레임의 비디오에서 20FPS로 실행할 수 있으며 iPhone 4S는 60+ FPS에서 해당 크기의 비디오를 쉽게 처리 할 수 ​​있습니다. 현재 포인트를 다시 읽는 프로세스는 CPU에 바운드되어 있고 속도는 약간 느리지 만 이와 같은 작업에서는 CPU 바운드 처리보다 훨씬 빠릅니다.

이것을 실제로보고 싶다면 내 프레임 워크의 코드를 잡고 함께 제공되는 FilterShowcase 예제를 실행할 수 있습니다. 해리스 코너 감지 예제는 장치 카메라의 라이브 비디오에서 실행되지만 코너 포인트의 읽기는 현재 CPU에서 발생하므로 실제로 속도가 느려집니다. 이를 위해 GPU 기반 프로세스로 전환하고 있습니다.


1
아주 좋아요! 나는 github에서 당신의 프레임 워크를 따르고, 정말 흥미로운 것 같습니다. 축하합니다!
Stéphane Péchard

코너 좌표를 실제로 CPU로 되 돌리는 방법에 대한 예가 있습니까? 스마트 GPU 방식이 있습니까? 아니면 판독 된 후 표시된 픽셀을 찾기 위해 반환 된 비트 맵을 통해 CPU에서 루핑해야합니까?
Quasimondo

@Quasimondo-포인트 추출을 위해 히스토그램 피라미드를 사용하여 노력하고 있습니다 : tevs.eu/files/vmv06.pdf 코너 감지를 위해 픽셀에 대한 CPU 바인딩 반복을 피하기 위해. 요즘 조금 산만 해 졌기 때문에 이것을 끝내지 못했지만 곧하고 싶습니다.
Brad Larson

안녕 @ BradLarson, 나는 이것이 매우 오래된 스레드라는 것을 알고 귀하의 답변에 감사드립니다. GPUImage 프레임 워크에서 KGPUImageHarrisCornerDetection.m을 확인했습니다. 이미지에서 코너 위치를 추출하기 위해 glReadPixels를 사용하여 이미지를 버퍼로 읽은 다음 버퍼에서 루프하여 colotByte> 0으로 포인트를 배열에 저장했습니다. 버퍼와 루프에서 이미지를 읽을 필요가없는 GPU에서이 모든 것을 수행 할 수있는 방법이 있습니까?
Sahil Bajaj

1
@SahilBajaj-내가 보았지만 아직 구현할 시간이 없었던 한 가지 기술은 히스토그램 피라미드 를 사용 하여 스파 스 이미지에서 포인트를 빠르게 추출하는 것입니다. 이것은 속도를 크게 향상시킬 것입니다.
Brad Larson

3

Shi-Tomasi 및 Moravec와 같은 "견고한"코너 검출기는 매우 느립니다. 여기를 확인하십시오 -http : //en.wikipedia.org/wiki/Corner_detection FAST는 아마도 유일한 유일한 경량 코너 검출기입니다. 최대 억제를 수행하지 않으면 서 FAST를 향상시킬 수 있습니다. 최고 "코너 니스"점수를 가진 FAST 출력을 선택하십시오 (시토 마시 및 Moravec를 코너 니스 점수로 포함하여 여러 가지 직관적 인 방법으로 계산할 수 있습니다) 또한 여러 FAST 검출기 중에서 선택할 수 있습니다. FAST-5에서 FAST-12 및 FAST_ER (마지막은 모바일에 비해 너무 클 수 있음) 또 다른 방법은 FAST를 생성하는 것입니다. 작성자 사이트에서 FAST 코드 생성기를 가져 와서 가능한 이미지 세트에서 학습하십시오. http://www.edwardrosten.com/work/fast.html


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.