블로그 이미지
Leeway is... the freedom that someone has to take the action they want to or to change their plans.
maetel

Notice

Recent Post

Recent Comment

Recent Trackback

Archive

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
  • total
  • today
  • yesterday

Category

2010. 4. 4. 17:33 Computer Vision
패턴 격자 무늬의 꼭지점 찾기

ref. swPark_2000rti.pdf

2010/04/04 - [Visual Information Processing Lab] - OpenCV: cvFilter2D() 연습 코드
2010/04/03 - [Visual Information Processing Lab] - Image Filtering



1) 1차 DoG filter 만들기: x방향과 y방향의 local maxima를 찾는다.

swPark_2000rti.pdf 440쪽:
"To find the edge of the grid, a first-order Derivative of Gaussian (DoG) filter with a kernel h = [-1, -7, -15, 0, 15, 7, 1] is used."




1차 DoG 필터 테스트:

입력 영상

흑백 영상

수평 방향 1차 DoG 필터링한 영상

수직 방향 1차 DoG 필터링한 영상





2) 다음 이미지에서 보다 확실히 나타나는, 2가지 문제를 해결해야 함

입력 영상

흑백 영상

x방향 DoG 필터링한 영상

y방향 DoG 필터링한 영상


(1) 패턴 격자에서 흑->백으로 넘어갈 때에만 edge 검출 (백->흑인 경우에는 무효)
(2) 검출된 edge 영역이 너무 두터움 (Non-Maxima Suppression을 해 주어야 한다고 함)


필터링 함수가 실제로 어떻게 이미지에 (색상/세기)값을 넣는지 볼 수 있을까 하여 cvFilter() 함수의 정의를 찾아 보니, 다음 부분이 나옴.

opencv/opencv/src/cv/cvfilter.cpp
CV_IMPL void
cvFilter2D( const CvArr* srcarr, CvArr* dstarr, const CvMat* _kernel, CvPoint anchor )
{
    cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr);
    cv::Mat kernel = cv::cvarrToMat(_kernel);

    CV_Assert( src.size() == dst.size() && src.channels() == dst.channels() );

    cv::filter2D( src, dst, dst.depth(), kernel, anchor, 0, cv::BORDER_REPLICATE );
}

본론인 "cv::filter2D"에 대하여 같은 파일 안에 다음의 정의가 있음.

template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFilter
{
    typedef typename CastOp::type1 KT;
    typedef typename CastOp::rtype DT;
   
    Filter2D( const Mat& _kernel, Point _anchor,
        double _delta, const CastOp& _castOp=CastOp(),
        const VecOp& _vecOp=VecOp() )
    {
        anchor = _anchor;
        ksize = _kernel.size();
        delta = saturate_cast<KT>(_delta);
        castOp0 = _castOp;
        vecOp = _vecOp;
        CV_Assert( _kernel.type() == DataType<KT>::type );
        preprocess2DKernel( _kernel, coords, coeffs );
        ptrs.resize( coords.size() );
    }

    void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn)
    {
        KT _delta = delta;
        const Point* pt = &coords[0];
        const KT* kf = (const KT*)&coeffs[0];
        const ST** kp = (const ST**)&ptrs[0];
        int i, k, nz = (int)coords.size();
        CastOp castOp = castOp0;

        width *= cn;
        for( ; count > 0; count--, dst += dststep, src++ )
        {
            DT* D = (DT*)dst;

            for( k = 0; k < nz; k++ )
                kp[k] = (const ST*)src[pt[k].y] + pt[k].x*cn;

            i = vecOp((const uchar**)kp, dst, width);

            for( ; i <= width - 4; i += 4 )
            {
                KT s0 = _delta, s1 = _delta, s2 = _delta, s3 = _delta;

                for( k = 0; k < nz; k++ )
                {
                    const ST* sptr = kp[k] + i;
                    KT f = kf[k];
                    s0 += f*sptr[0];
                    s1 += f*sptr[1];
                    s2 += f*sptr[2];
                    s3 += f*sptr[3];
                }

                D[i] = castOp(s0); D[i+1] = castOp(s1);
                D[i+2] = castOp(s2); D[i+3] = castOp(s3);
            }

            for( ; i < width; i++ )
            {
                KT s0 = _delta;
                for( k = 0; k < nz; k++ )
                    s0 += kf[k]*kp[k][i];
                D[i] = castOp(s0);
            }
        }
    }

    Vector<Point> coords;
    Vector<uchar> coeffs;
    Vector<uchar*> ptrs;
    KT delta;
    CastOp castOp0;
    VecOp vecOp;
};



Try #1.
-1) 필터링의 결과 이미지의 bit depth를 "8"이 아니라 "IPL_DEPTH_32F"로 바꾼 다음,  음수로 나온 gradient 값을 양수로 바꾸어 준다.
그런데, 입력 영상을 담을 메모리를 별도로 생성하지 않고, 다음과 같이 비디오 프레임 캡처 시 만들어 주므로 인위적으로 설정해 줄 수 없다.
iplInput = cvRetrieveFrame(capture);

그래서 cvConvert() 함수를 이용한다. 정의는 다음과 같다.

OpenCV.framework/Versions/A/Headers/cxcore.h
#define cvConvert( src, dst )  cvConvertScale( (src), (dst), 1, 0 )

void cvConvertScale(const CvArr* src, CvArr* dst, double scale=1, double shift=0)

Converts one array to another with optional linear transformation.

#define cvCvtScale cvConvertScale
#define cvScale cvConvertScale
#define cvConvert(src, dst ) cvConvertScale((src), (dst), 1, 0 )
Parameters:
  • src – Source array
  • dst – Destination array
  • scale – Scale factor
  • shift – Value added to the scaled source array elements


-2) Non Maximum Suppression  (NMS)
이웃 화소들의 세기값을 비교하여 해당 픽셀이 최대값이 아니면 "0"으로 하여 지워 준다



입력 영상

x방향 DoG filtering하고 NMS한 영상

y방향 DoG filtering하고 NMS한 영상





Szeliski, Computer Vision: Algorithms and Applications, 214쪽



Szeliski, Computer Vision: Algorithms and Applications, 215쪽

ref.
http://research.microsoft.com/en-us/um/people/szeliski/Book/
2010/04/06 - [Visual Information Processing Lab] - Non Maximum Suppression (NMS)


 
Try #2.
Sobel 마스크 이용



3)  gradient의 방향 판별
 
: 검출된 edge의 Gx, Gy의 절대값을 비교하여 vertical인지 horizontal인지 direction을 판별한다.
이로부터 그 점이 수평선 위의 점인지 수직선 위의 점인지를 구별하여 다음 단계인 line fitting에 적용한다.

ref.  2010/02/23 - [Visual Information Processing Lab] - virtual studio 구현: workflow







입력 영상

수직선 상의 edges만 검출한 영상

수평선 상의 edges만 검출한 영상




입력 영상

수직선 상의 edges만 검출한 영상

수평선 상의 edges만 검출한 영상


잘 됨 ^^

'Computer Vision' 카테고리의 다른 글

OpenCV: cvFitLine() 연습 코드  (0) 2010.04.06
virtual studio 구현: line fitting test  (0) 2010.04.06
OpenCV: cvFilter2D() 연습 코드  (0) 2010.04.04
Image Filtering  (0) 2010.04.03
OpenCV: cvSobel() 연습 코드  (3) 2010.04.03
posted by maetel