Processing math: 100%

RGB 컬러 이미지의 gray level의 cdf를 이용해서 histogram equalization을 수행한다. 컬러 이미지의 gray level은 

gray=r+g+b3

으로 정의한다.

gray level에 기반한 equalization 결과;
각 color 채널 equalization 결과:
RGB color를 HSV color로 변환한 후 V에 대해서 equalization을 했을 때 결과:

std::vector<BYTE> color_equalize_new(std::vector<BYTE> &rgb) {
    int hist[256] = {0};
    int chist[256], lut[256], partition[256 + 1];
    std::vector<BYTE> out = raster; // clone; 
    // pdf of gray level: g = (r + g + b) / 3
    for (int k = 0; k < rgb.size(); k += 3) {
        int a = rgb[k+0]; a += rgb[k+1]; a += rgb[k+2];
        ++hist[a/3];
    };
    // cdf;
    for (int i = 0, s = 0; i < 256; i++) {
        s += hist[i]; chist[i] = s;
    }
    
    /* assign equal number of pixels in each gray levels;*/
    int num_pixels = rgb.size()/3;
    double pixels_per_level = double(num_pixels) / 256;

    /* first and last in partition */
    partition[0]   = 0;
    partition[256] = 256;
    /* intermediate; */
    for (int j = 0, i = 1; i < 256; i++) {
        double desired = i * pixels_per_level;			
        while (chist[j + 1] <= desired) j++;
        /* chist[j] <= desired < chist[j+1];*/
        /* nearest interpolation */
        if ((desired - chist[j]) < (chist[j+1] - desired)) partition[i] = j;
        else partition[i] = j + 1;
    } 
    /* Find i s.t. partion[i] <= j < partition[i+1];*/
    for (int j = 0; j < 256; j++) {
        int i = 0;
        while (partition[i+1] <= j) i++;
        lut[j] = i;
    } 
    // needs hue preserving processes;
    for (k = rgb.size(); k-->0;)
        res[k] = lut[src[k]];
    return out;
}
std::vector<BYTE> color_equalize_HSV(std::vector<BYTE> &rgb) {
    int hist[256] = {0};
    int chist[256] = {0}, lut[256] = {0}, partition[256 + 1];
    std::vector<BYTE> out = raster; // cloning;
    std::vector<double> fH(rgb.size()/3);
    std::vector<double> fV(rgb.size()/3);
    std::vector<double> fS(rgb.size()/3);

    const int n = rgb.size()/3;
    double r, g, b;
    for (int k = 0; k < rgb.size(); k += 3) {
        b = rgb[k+0], g = rgb[k+1], r = rgb[k+2];
        RGBtoHSV(r / 255, g / 255, b / 255, fH[k], fS[k], fV[k]);
    };	
    // make histogram of V-color;
    for (int k = n; k-->0;)
        ++hist[int(fV[k] * 255)];

    // cdf;
    for (int i = 0, s = 0; i < 256; i++) {
        s += hist[i]; chist[i] = s;
    }
    /* assign equal number of pixels in each V-color;*/
    int num_pixels = rgb.size()/3;
    double pixels_per_level = double(num_pixels) / 256;

    /* first and last in partition */
    partition[0]   = 0;
    partition[256] = 256;
    /* intermediate; */
    for (int j = 0, i = 1; i < 256; i++) {
        double desired = i * pixels_per_level;			
        while (chist[j + 1] <= desired) j++;
        /* nearest interpolation */
        if ((desired - chist[j]) < (chist[j + 1] - desired)) partition[i] = j;
        else partition[i] = j + 1;
    } 
    /* fill equalization LUT */
    for (int j = 0; j < 256; j++) {
        int i = 0;
        while (partition[i + 1] <= j) i++;
        lut[j] = i;
    } 
    for (int k = n; k-->0;)
        fV[k]= lut[int(fV[k] * 255)] / 255.;

    for (int k = 0; k < rgb.size(); k += 3) {
        HSVtoRGB(fH[k], fS[k], fV[k], r, g, b);
        out[k+0] = int(b * 255); 
        out[k+1] = int(g * 255);
        out[k+2] = int(r * 255);
    }
    return out;
}
/* fR Red component, range: [0, 1]
** fG Green component, range: [0, 1]
** fB Blue component, range: [0, 1]
** fH Hue component, range: [0, 360]
** fS Hue component, range: [0, 1]
** fV Hue component, range: [0, 1] */
void RGBtoHSV(double fR, double fG, double fB, double& fH, double& fS, double& fV);
void HSVtoRGB(double fH, double fS, double fV, double& fR, double& fG, double& fB);
728x90

'Image Recognition > Fundamental' 카테고리의 다른 글

FFT를 이용한 영상의 미분  (0) 2022.02.12
SVD Fitting  (0) 2022.02.07
Least Squares Fitting of Ellipses  (1) 2022.01.27
Circle Fitting: Pratt  (0) 2022.01.20
Best-fit Ellipse 2  (0) 2022.01.18
,

일반적인 이차곡선은 다음의 이차식으로 표현이 된다:

F(x,y)=ax2+bxy+cy2+dx+ey+f=0

6개의 계수는 모두 독립적이지 않고 어떤 종류의 이차곡선인가에 따라 제약조건이 들어온다. 주어진 점 데이터 {(xi,yi)|i=1,2,...,n}를 피팅하는 이차곡선을 각각의 데이터 점에서 대수적 거리 (=|F(xi,yi)|)의 제곱을 최소로 하는 조건하에서 찾도록 하자:

ϵ=i|F(xi,yi)|2min

타원으로 피팅하는 경우 여러 가지 제약조건을 줄 수 있지만(e.g.: a+c=1, a2+b2+...+f2=1 등등) 여기서는 이차 곡선이 타원이기 위해서는 2차 항의 계수로 만드는 행렬 (ab/2b/2c)의 determinant (= acb2/4>0)가 양수이어야 한다는 조건에서 (타원을 회전+평행 이동시키면 표준형 타원으로 만들 수 있는데, 이때 두 주축의 계수 곱이 determinant다. 따라서 타원이기 위해서는 값이 양수여야 된다) 

4acb2=1

로 선택하도록 하자. 이 제약조건을 넣은 타원 피팅은 다음 식을 최소화하는 계수 벡터 a=[a,b,c,d,e,f]T을 찾는 문제가 된다:

ϵ=aT.S.aλ(aT.C.a1)

여기서 6×6 scattering matrix S

S=DT.D.

n×6 design matrix D는 

D=[x21x1y1y21x1y11x22x2y2y22x2y21x2nxnyny2nxnyn1].

그리고, 6×6 행렬 C

C=[002000010000200000000000000000000000].

ϵaT에 대해 미분하면 최소 제곱 문제는 Lagrange multiplier를 고윳값으로 하는 고유 방정식의 해를 구하는 문제로 환원이 된다.

S.a=λC.a

그리고 주어진 고유값 λ에 해당하는 normalized 고유 벡터가 a일 때 

ϵ=aT.S.a=λ

이므로 최소의 양의 고윳값에 해당하는 고유 벡터가 해가 된다. Silverster의 law of inertia를 이용하면 위의 고유 방정식에서 양의 고윳값은 딱 1개만 존재함을 보일 수 있다. 고유값 λ에 해당하는 고유 벡터가 a일 때 임의의 상수 μ 배를 한 μa도 같은 고유값을 갖는 고유벡터다. normalized 고유벡터는 제약조건 μ2aT.C.a=1을 만족하도록 크기를 조정하면 ˜a=a/aT.C.a로 주어진다. 

 

이 일반화된 고유방정식의 풀이는 먼저 positive definite인 S의 제곱근 행렬 Q=S1/2을 이용하면 쉽다. S의 고유벡터를 구하면 eigendecomposition에 의해 S=RΛRT로 쓸 수 있으므로 제곱근 행렬은 Q=RΛ1/2RT임을 쉽게 확인할 수 있다. 원래의 고유값 문제를 Q을 이용해서 표현하면 

Qa=λQ1Ca=λQ1CQ1Qa 이므로 더 다루기 쉬운 Q1CQ1의 고유값 문제로 환원이 됨을 알 수 있다.

 

추가로, 고유 방정식은 C3×3 크기의 block으로 나누어질 수 있으므로 [a,b,c]T 에 대한 고유 방정식으로 줄일 수 있어서 쉽게 해결할 수 있다. 물론 scattering matrix을 구성하는 moment의 개수가 많아서 matrix 연산 패키지를 사용하지 않는 경우 코드가 길어지게 된다. 

 

Note: 이 내용은 다음 논문을 정리한 것이다. Andrew Fitzgibbon, Maurizio Pilu, and Robert B. Fisher, Direct Least Square Fitting of Ellipses". IEEE TRANSACTIONS ON PATTERN ANALYSIS AND MACHINE INTELLIGENCE, VOL. 21, NO. 5, MAY 1999. 

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/ellipse-pami.pdf

 

구현은  https://kipl.tistory.com/566

 

Ellipse Fitting

일반적인 conic section 피팅은 주어진 데이터 {(xi,yi)}를 가장 잘 기술하는 이차식 F(x,y)=ax2+bxy+cy2+dx+ey+f=0 의 계수 uT=(a,b,c,d,e,f)을 찾는 문제이다. 이 conic section이 타원이기

kipl.tistory.com

 
728x90

'Image Recognition > Fundamental' 카테고리의 다른 글

SVD Fitting  (0) 2022.02.07
Color Histogram Equalization  (4) 2022.02.07
Circle Fitting: Pratt  (0) 2022.01.20
Best-fit Ellipse 2  (0) 2022.01.18
Best-fit Ellipse  (0) 2022.01.16
,

주어진 점집합을 원으로 피팅하기 위해 이차식

A(x2+y2)+Bx+Cy+D=0

을 이용하자. 원의 경우는 A=0인 경우는 직선을 나타내고, A0인 경우가 원을 표현한다. 물론 A=1로 설정을 할 수 있으나 이 경우는 주어진 데이터가 원의 일부 부분만 나타내는 경우 문제가 생긴다. 원은 중심과 반지름을 알면 결정되므로 3개의 변수가 필요한데 위의 이차 다항식은 4개의 변수를 가진다. 원을 제대로 표현하기 위해서는 제한 조건이 들어오는데 

(x+B2A)2+(y+C2A)2=B2+C24AD4A2

으로 쓸 수 있으므로 좌변이 양수이므로 

B2+C24AD>0이어야 한다. 따라서 B2+C24AD=1인 제한 조건을 둘 수 있다.

주어진 각 점들이 정확히 추정하는 원 상에 있지 않으므로 이차식의 좌변의 값이 반드시 0이 되지는 않는다. 잘 피팅하기 위해서는 제한조건을 만족하면 각 점들에서 차이를 최소로 만드는 파라미터를 찾으면 된다:

ϵ(A,B,C,D)=|A(x2i+y2i)+Bxi+Cyi+D|2λ(B2+C24AD1)min

여기서 λ는 Lagrange multiplier이다. 식을 행렬로 표현하기 위해

zi=x2i+y2i, Mzz=1nz2i, Mxx=1nx2i, Myy=1ny2i,

Mzx=1nzixi, Mzy=1nziyi, Mxy=1nxiyi,

Mz=Mxx+Myy, Mx=1nxi, My=1nyi,

x=(ABCD), M=(MzzMzxMzyMzMzxMxxMxyMxMzyMxyMyyMyMzMxMy1), N=(0002010000102000)

로 놓으면 (M=scattering matrix) n1ϵ=xT.M.xλ(xT.N.x1)

로 표현된다. 질량중심 좌표계에서 계산을 하면 Mx=My=0이므로 행렬이 더 간단해진다.

ϵxT에 대해서 미분하면

M.x=λN.x

인 lagrange multiplier가 고윳값이 되는 일반화된 고윳값 방정식을 얻는다. 이 고윳값 식이 해를 가지려면

det(MλN)=0

이어야 한다. 이 특성 방정식은 4차 다항식이므로 closed form 형태의 해를 쓸 수 있으나 실질적으로 수치해석적으로 구하는 것이 더 편하다(Newton-Raphson). min(ϵ)은 제한조건 때문에

n1ϵ=λxT.N.x=λ>0

이므로 0보다 큰 최소 고윳값이 원하는 해이다. (λ=0det(M)=0, 즉, M이 singular 한 경우)

그런데 주어진 고윳값 λ 해당하는 고유 벡터가 x일 때, 임의의 0이 아닌 상수 μ에 대해 μx도 역시 고유벡터이다. 고유 벡터의 크기를 고정하기 위해서 제한조건을 적용하면 1=μ2xT.N.x=μ2xT.M.x/λ이므로 μ=λ/xT.M.x로 선택하면 된다. 그렇지만 해가 원을 나타내는 경우 원의 중심 (B/2A,C/2A), 반지름 (B/2A)2+(C/2A)2D/A은 계수의 비에만 의존하므로 굳이 normalized 된 고유벡터를 구할 필요가 없다. 따라서, 해 중에 직선인 경우는 무시하고(A=0) 원만을 선택할 때는 A=1로 놓고 계산을 해도 영향을 받지 않는다. 또한 이 경우 고유벡터 성분을 구체적으로 적을 수 있어 수치해석에 의존할 필요도 없어진다.

최소의 고윳값에 대해서 고유 방정식을 풀면 고유벡터의 성분은

CxyMxxMyyM2xy,  Δλ2Mzλ+Cxy

A=1

B=Mzx(Myyλ)MzyMxyΔ

C=Mzy(Mxxλ)MzxMxyΔ

D=Mz2λ로 주어진다. 따라서 원의 중심은

cx=B2A=Mzx(Myyλ)MzyMxy2Δ

cy=C2A=Mzy(Mxxλ)MzxMxy2Δ

원의 반지름은 

r=(B/2A)2+(C/2A)2D/A=c2x+c2y+Mz+2λ 

로 주어진다.

 

** 추가: Sylvester's law of inertia에 의하면 위에서 구하는 고유값 λ의 부호 개수는  N의 고유값 부호별 개수와 같아야 하는데, N의 고유값이 {2,1,1,2}이므로 양수인 고유값 λ는 3개가 존재한다.

 

Ref: V. Pratt, Direct least-squares fitting of algebraic surfaces, Computer Graphics 21, 1987, 145–152.

double circleFit(std::vector<CPoint>& data, double& centerx, double& centery, double& radius) {
    const int maxIter = 99;
    double mx = 0, my = 0;
    for (int i = data.size(); i-- > 0;)
        mx += data[i].x, my += data[i].y;

    // center of mass;
    mx /= data.size(); my /= data.size();

    // moment calculation;
    double Mxy, Mxx, Myy, Mzx, Mzy, Mzz;
    Mxx = Myy = Mxy = Mzx = Mzy = Mzz = 0.;
    for (int i = data.size(); i-- > 0;) {
        double xi = data[i].x - mx;   //  center of mass coordinate
        double yi = data[i].y - my;   //  center of mass coordinate
        double zi = xi * xi + yi * yi;
        Mxy += xi * yi; Mxx += xi * xi;
        Myy += yi * yi; Mzx += xi * zi;
        Mzy += yi * zi; Mzz += zi * zi;
    }
    Mxx /= data.size(); Myy /= data.size();
    Mxy /= data.size(); Mzx /= data.size();
    Mzy /= data.size(); Mzz /= data.size();
    double Mz = Mxx + Myy;
    double Cxy = Mxx * Myy - Mxy * Mxy;
    double Vz = Mzz - Mz * Mz;
    // coefficients of characteristic polynomial;
    double C2 = 4 * Cxy - 3 * Mz * Mz - Mzz; 
    double C1 = Vz * Mz + 4 * Cxy * Mz - Mzx * Mzx - Mzy * Mzy;
    double C0 = Mzx * (Mzx * Myy - Mzy * Mxy) + Mzy * (Mzy * Mxx - Mzx * Mxy) - Vz * Cxy;
    // Newton's method starting at lambda = 0
    double lambda = 0;
    double y = C0;  // det(lambda = 0)
    for (int iter = 0; iter < maxIter; iter++) {
        double Dy = C1 + lambda * (2. * C2 + 16.*lambda * lambda);
        double lambdaNew = lambda - y / Dy;
        if ((lambdaNew == lambda)) break;
        double ynew = C0 + lambdaNew * (C1 + lambdaNew * (C2 + 4 * lambdaNew * lambdaNew));
        if (fabs(ynew) >= fabs(y))  break;
        lambda = lambdaNew;
        y = ynew;
    }
    double DEL = lambda * lambda - lambda * Mz + Cxy;
    double cx = (Mzx * (Myy - lambda) - Mzy * Mxy) / DEL / 2;
    double cy = (Mzy * (Mxx - lambda) - Mzx * Mxy) / DEL / 2;
    centerx = cx + mx;   // recover origianl coordinate;
    centery = cy + my;
    radius = sqrt(cx * cx + cy * cy + Mz + 2 * lambda);
    return fitError(data, centerx, centery, radius);
}

https://kipl.tistory.com/32

 

RANSAC: Circle Fit

RANSAC 알고리즘을 써서 주어진 2차원 점집합에서 원을 추정한다. 원을 만들기 위해서는 최소한 3점이 필요하고, 또 일직선에 있지 않아야 한다. 이렇게 만들어진 원은 세 점을 꼭짓점으로 하는

kipl.tistory.com

https://kipl.tistory.com/207

 

Least Squares Fitting of Circles

점집합을 일반적인 2차 곡선으로 피팅하는 경우에 방정식은ax2+by2+cxy+dx+ey+f=0의 계수를 주어진 데이터를 이용하여서 구해야 한다. 실제 문제에서는 타원, 포물선 쌍곡 선등의 타입

kipl.tistory.com

 

728x90

'Image Recognition > Fundamental' 카테고리의 다른 글

Color Histogram Equalization  (4) 2022.02.07
Least Squares Fitting of Ellipses  (1) 2022.01.27
Best-fit Ellipse 2  (0) 2022.01.18
Best-fit Ellipse  (0) 2022.01.16
Image Moments  (0) 2021.12.04
,