18

登録が必要なフィルムからスキャンされた海底画像の歴史的な時系列があります。

from pylab import *
import cv2
import urllib

urllib.urlretrieve('http://geoport.whoi.edu/images/frame014.png','frame014.png');
urllib.urlretrieve('http://geoport.whoi.edu/images/frame015.png','frame015.png');

gray1=cv2.imread('frame014.png',0)
gray2=cv2.imread('frame015.png',0)
figure(figsize=(14,6))
subplot(121);imshow(gray1,cmap=cm.gray);
subplot(122);imshow(gray2,cmap=cm.gray);

ここに画像の説明を入力

各画像の左側にある黒い領域を使用して登録を行いたいのですが、その領域はカメラの内側にあり、時間内に修正する必要があるためです。したがって、黒い領域間のアフィン変換を計算する必要があります。

しきい値を設定して最大の輪郭を見つけることで、これらの領域を決定しました。

def find_biggest_contour(gray,threshold=40):
    # threshold a grayscale image 
    ret,thresh = cv2.threshold(gray,threshold,255,1)
    # find the contours
    contours,h = cv2.findContours(thresh,mode=cv2.RETR_LIST,method=cv2.CHAIN_APPROX_NONE)
    # measure the perimeter
    perim = [cv2.arcLength(cnt,True) for cnt in contours]
    # find contour with largest perimeter
    i=perim.index(max(perim))
    return contours[i]

c1=find_biggest_contour(gray1)
c2=find_biggest_contour(gray2)

x1=c1[:,0,0];y1=c1[:,0,1]
x2=c2[:,0,0];y2=c2[:,0,1]

figure(figsize=(8,8))
imshow(gray1,cmap=cm.gray, alpha=0.5);plot(x1,y1,'b-')
imshow(gray2,cmap=cm.gray, alpha=0.5);plot(x2,y2,'g-')
axis([0,1500,1000,0]);

ここに画像の説明を入力

青は第 1 フレームからの最長の等高線、緑は第 2 フレームからの最長の等高線です。

青と緑の等高線の間の回転とオフセットを決定する最良の方法は何ですか?

矢印の間の領域のような、ステップを囲む領域で輪郭の右側のみを使用したい。

もちろん、これらの画像を登録するためのより良い方法があれば、ぜひ聞きたいです。未加工の画像に対して標準的な機能マッチング アプローチを既に試しましたが、十分に機能しませんでした。

4

4 に答える 4

9

Shambool の提案したアプローチに従って、私が思いついたのは次のとおりです。私は、Ramer-Douglas-Peucker アルゴリズムを使用して関心領域の輪郭を単純化し、2 つのターニング ポイントを特定しました。2 つのターニング ポイントを使用して 3 つの未知数 (xoffset、yoffset、および回転角度) を取得するつもりでしたが、RDP がこの領域のより滑らかな曲線を簡略化したため、2 番目のターニング ポイントは少し右寄りすぎています。代わりに、最初の転換点に至る線分の角度を使用しました。この角度を image1 と image2 で差すると、回転角度がわかります。私はまだこのソリューションに完全に満足していません。この 2 つの画像では十分に機能しましたが、一連の画像全体でうまく機能するかどうかはわかりません。見てみましょう。

輪郭を黒い境界線の既知の形状に合わせる方が本当に良いでしょう.

# select region of interest from largest contour 
ind1=where((x1>190.) & (y1>200.) & (y1<900.))[0]
ind2=where((x2>190.) & (y2>200.) & (y2<900.))[0]
figure(figsize=(10,10))
imshow(gray1,cmap=cm.gray, alpha=0.5);plot(x1[ind1],y1[ind1],'b-')
imshow(gray2,cmap=cm.gray, alpha=0.5);plot(x2[ind2],y2[ind2],'g-')
axis([0,1500,1000,0])

ここに画像の説明を入力

def angle(x1,y1):
    #  Returns angle of each segment along an (x,y) track
    return array([math.atan2(y,x) for (y,x) in zip(diff(y1),diff(x1))])

def simplify(x,y, tolerance=40, min_angle = 60.*pi/180.): 
    """
    Use the Ramer-Douglas-Peucker algorithm to simplify the path
    http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
    Python implementation: https://github.com/sebleier/RDP/
    """
    from RDP import rdp   
    points=vstack((x,y)).T
    simplified = array(rdp(points.tolist(), tolerance))
    sx, sy = simplified.T

    theta=abs(diff(angle(sx,sy)))
    # Select the index of the points with the greatest theta
    # Large theta is associated with greatest change in direction.
    idx = where(theta>min_angle)[0]+1
    return sx,sy,idx

sx1,sy1,i1 = simplify(x1[ind1],y1[ind1])
sx2,sy2,i2 = simplify(x2[ind2],y2[ind2])
fig = plt.figure(figsize=(10,6))
ax =fig.add_subplot(111)

ax.plot(x1, y1, 'b-', x2, y2, 'g-',label='original path')
ax.plot(sx1, sy1, 'ko-', sx2, sy2, 'ko-',lw=2, label='simplified path')
ax.plot(sx1[i1], sy1[i1], 'ro', sx2[i2], sy2[i2], 'ro', 
    markersize = 10, label='turning points')
ax.invert_yaxis()
plt.legend(loc='best')

ここに画像の説明を入力

# determine x,y offset between 1st turning points, and 
# angle from difference in slopes of line segments approaching 1st turning point
xoff = sx2[i2[0]] - sx1[i1[0]]
yoff = sy2[i2[0]] - sy1[i1[0]]
iseg1 = [i1[0]-1, i1[0]]
iseg2 = [i2[0]-1, i2[0]]
ang1 = angle(sx1[iseg1], sy1[iseg1])
ang2 = angle(sx2[iseg2], sy2[iseg2])
ang = -(ang2[0] - ang1[0])
print xoff, yoff, ang*180.*pi

-28 14 5.07775871644

# 2x3 affine matrix M
M=array([cos(ang),sin(ang),xoff,-sin(ang),cos(ang),yoff]).reshape(2,3)
print M

[[  9.99959685e-01   8.97932821e-03  -2.80000000e+01]
 [ -8.97932821e-03   9.99959685e-01   1.40000000e+01]]

# warp 2nd image into coordinate frame of 1st
Minv = cv2.invertAffineTransform(M)
gray2b = cv2.warpAffine(gray2,Minv,shape(gray2.T))

figure(figsize=(10,10))
imshow(gray1,cmap=cm.gray, alpha=0.5);plot(x1[ind1],y1[ind1],'b-')
imshow(gray2b,cmap=cm.gray, alpha=0.5);
axis([0,1500,1000,0]);
title('image1 and transformed image2 overlain with 50% transparency');

ここに画像の説明を入力

于 2013-04-18T11:30:20.820 に答える
3

良い質問。

1 つのアプローチは、等高線を 2 次元の点群として表現し、登録を行うことです。アフィン変換を提供できるMatlab のよりシンプルで明確なコード。

さらに複雑な C++ コード (VXL lib を使用)とpython および matlab ラッパーが含まれています。または、ノイズに強く、アフィン変換を処理できる、修正された ICP (反復最近点) アルゴリズムを使用できます。

また、あなたの輪郭はあまり正確ではないように見えるので、問題になる可能性があります.

もう 1 つの方法は、ピクセル値を使用するある種のレジストレーションを使用することです。 Matlabコード(ある種の最小化+相互相関メトリックを使用していると思います)また、医療画像で使用されるある種のオプティカルフロー登録(または他の種類)があるかもしれません。

また、ポイント フィーチャを SIFT(SURF) として使用することもできます。

FIJI(ImageJ) でもこのリンクで簡単に試すことができます。

  1. 2枚の画像を開く
  2. プラグイン -> 特徴抽出 -> 選別 (またはその他)
  3. 予想変換をアフィンに設定
  4. ImageJ ログの推定変換モデル [3,3] ホモグラフィ マトリックスを見てください。うまく機能する場合は、OpenCV を使用して Python に実装するか、Jython と ImageJ を使用して実装できます。

そして、元の画像を投稿して、すべての条件を説明するとより良いでしょう(フレーム間で画像が変化しているようです)

于 2013-04-17T07:43:39.893 に答える
2

これらの等高線は、それぞれの楕円で表すことができます。これらの楕円は、等高線の重心を中心とし、密度の主軸の方向を向いています。重心と方向角を比較できます。

1) 輪郭を塗りつぶす => drawContours with thickness=CV_FILLED

2) 瞬間を見つける => cvMoments()

3) そして、それらを使用します

重心: { x, y } = {M10/M00, M01/M00 }

向き (シータ): ここに画像の説明を入力

編集: あなたのケースに合わせて、レガシー (enteringblobdetection.cpp) のサンプル コードをカスタマイズしました。

            /* Image moments */
            double      M00,X,Y,XX,YY,XY;
            CvMoments   m;
            CvRect      r = ((CvContour*)cnt)->rect;
            CvMat       mat;
            cvMoments( cvGetSubRect(pImgFG,&mat,r), &m, 0 );
            M00 = cvGetSpatialMoment( &m, 0, 0 );
            X = cvGetSpatialMoment( &m, 1, 0 )/M00;
            Y = cvGetSpatialMoment( &m, 0, 1 )/M00;
            XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X;
            YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y;  
            XY = (cvGetSpatialMoment( &m, 1, 1 )/M00) - X*Y; 

            /* Contour description */
            CvPoint myCentroid(r.x+(float)X,r.y+(float)Y);
            double myTheta =  atan( 2*XY/(XX-YY) );

また、これを OpenCV 2.0 の例で確認してください。

于 2013-04-17T07:17:25.293 に答える
1

2 つの画像間のホモグラフィを見つけたくないが、アフィン変換を見つけたい場合は、3 つの未知数、回転角 (R)、および x と y の変位 (X,Y) があります。したがって、未知数を見つけるには、少なくとも 2 つのポイント (それぞれに 2 つの既知の値を持つ) が必要です。2 つの画像または 2 つの線の間で 2 つの点を一致させる必要があります。それぞれの点には、切片と勾配の 2 つの既知の値があります。ポイントマッチングアプローチを使用する場合、ポイントが互いに離れているほど、検出されたノイズへの変換がより堅牢になります (エラー伝搬ルールを覚えていれば、これは非常に簡単です)。

2 点マッチング方式の場合:

  1. 最初の画像 I1 の 2 つの点 (A と B) と、2 番目の画像 I2 の対応する点 (A'、B') を見つける
  2. A と B の中間点: C、および A' と B' の中間点: C' を見つけます。
  3. C と C' の差 (CC') は、イメージ (X と Y) 間の変換を示します。
  4. CA と C'-A' のドット積を使用すると、回転角 (R) を見つけることができます

ロバストなポイントを検出するには、2 次導関数 (ヘッシアン) の絶対値が最も高いカウンターの側面に沿ってポイントを見つけ、それらを一致させようとします。これはビデオ映像であると述べたので、外れ値を拒否するために、2 つのフレーム間の変換が小さいと簡単に仮定できます。

于 2013-04-17T02:13:51.757 に答える