web-dev-qa-db-ja.com

OpenCVのリマップ機能を使用するにはどうすればよいですか?

Remap()の可能な限り簡単なテストケースを次に示します。

import cv2
import numpy as np
inimg = np.arange(2*2).reshape(2,2).astype(np.float32)
inmap = np.array([[0,0],[0,1],[1,0],[1,1]]).astype(np.float32)
outmap = np.array([[10,10],[10,20],[20,10],[20,20]]).astype(np.float32)
outimg = cv2.remap(inimg,inmap,outmap,cv2.INTER_LINEAR)
print "inimg:",inimg
print "inmap:",inmap
print "outmap:",outmap
print "outimg:", outimg

出力は次のとおりです。

inimg: [[ 0.  1.]
 [ 2.  3.]]
inmap: [[ 0.  0.]
 [ 0.  1.]
 [ 1.  0.]
 [ 1.  1.]]
outmap: [[ 10.  10.]
 [ 10.  20.]
 [ 20.  10.]
 [ 20.  20.]]
outimg: [[ 0.  0.]
 [ 0.  0.]
 [ 0.  0.]
 [ 0.  0.]]

ご覧のとおり、outimgは0,0を生成しますが、正しい形状ではありません。範囲0〜3の値が補間された20x20または10x10の画像が必要です。

すべてのドキュメントを読みました。それとSOでは、開始点の配列(マップ)、終了点のマップを入力すると、remap()はimgのすべての値を新しい位置に配置し、私はそれをやっていますが、それはちょうど動作しませんなぜ?ほとんどの例はC++のためです。Pythonで壊れていますか?

11
john ktejik

これはドキュメントの単なる誤解であり、私はあなたを責めることはできません。それを理解するのに手間がかかりました。ドキュメントは明確ですが、この関数はおそらく期待どおりに機能しません。実際、最初に期待していたものとは反対のopposite方向で動作します。

remap()does n'tは、ソースイメージの座標を取得し、ポイントを変換してから補間します。 remap()doesが行うことは、destination画像内のすべてのピクセルについて、それがどこから来るかを検索ソース画像に挿入し、補間値を割り当てます。補間するためには、各ピクセルでソース画像の周囲の値を見る必要があるため、この方法で動作する必要があります。拡大させてください(少し繰り返すかもしれませんが、間違ったやり方をしないでください)。

remap() docs から:

map1(x,y)ポイント、またはCV_16SC2CV_32FC1、またはCV_32FC2タイプを持つx値の最初のマップ。速度のために浮動小数点表現を固定小数点に変換する方法の詳細については、convertMaps()を参照してください。

map2–タイプCV_16UC1CV_32FC1、またはnone(map1(x,y)ポイントの場合は空のマップ)を持つy値の2番目のマップ、それぞれ。

firstmap of ...」というmap1の言葉遣いは、やや誤解を招く恐れがあります。覚えておいてください、これらは厳密にあなたのイメージがマップされる場所の座標ですfrom...ポイントがマップされていますfromsrcmap_x(x, y), map_y(x, y)のvar] _を使用し、x, ydstに配置します。そして、それらはあなたがそれらをワープしたい画像の同じ形状でなければなりませんto。ドキュメントに示されている式に注意してください。

dst(x,y) =  src(map_x(x,y),map_y(x,y))

ここでmap_x(x, y)map_xで指定された行と列でx, yを検索しています。次に、これらのポイントで画像が評価されます。 srcx, yのマッピングされた座標を検索し、dstx, yにその値を割り当てます。これを十分長く見つめると、それは意味を成し始めます。新しい宛先イメージのピクセル(0, 0)で、ソースイメージ内の対応するピクセルの位置を示すmap_xおよびmap_yを確認し、次に、宛先イメージの(0, 0)で補間値を割り当てます。ソース。これは、remap()がこのように機能する基本的な理由の一種です。ピクセルがどこから来たのかを知る必要があるため、補間する隣接ピクセルを見ることができます。

小さく、不自然な例

img = np.uint8(np.random.Rand(8, 8)*255)
#array([[230,  45, 153, 233, 172, 153,  46,  29],
#       [172, 209, 186,  30, 197,  30, 251, 200],
#       [175, 253, 207,  71, 252,  60, 155, 124],
#       [114, 154, 121, 153, 159, 224, 146,  61],
#       [  6, 251, 253, 123, 200, 230,  36,  85],
#       [ 10, 215,  38,   5, 119,  87,   8, 249],
#       [  2,   2, 242, 119, 114,  98, 182, 219],
#       [168,  91, 224,  73, 159,  55, 254, 214]], dtype=uint8)

map_y = np.array([[0, 1], [2, 3]], dtype=np.float32)
map_x = np.array([[5, 6], [7, 10]], dtype=np.float32)
mapped_img = cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)
#array([[153, 251],
#       [124,   0]], dtype=uint8)

ここで何が起きているのでしょうか?これらはimgのインデックスであり、それらが位置する行と列にマッピングされることを忘れないでください。この場合、マトリックスを調べるのが最も簡単です。

map_y
=====
0  1
2  3

map_x
=====
5  6
7  10

そのため、(0、0)の宛先イメージはmap_y(0, 0), map_x(0, 0) = 0, 5のソースイメージと同じ値を持ち、行0と列5のソースイメージは153です。宛先イメージではmapped_img[0, 0] = 153であることに注意してください。私のマップ座標は正確な整数であるため、ここでは補間は行われません。また、範囲外のインデックス(map_x[1, 1] = 10、画像の幅よりも大きい)を含めましたが、範囲外になったときに値0が割り当てられるだけです。

完全な使用例

以下は、グラウンドトゥルースホモグラフィを使用し、ピクセル位置を手動でワーピングし、remap()を使用して変換されたポイントから画像をマッピングする本格的なコード例です。ここで、私のホモグラフィはtrue_dsttosrcを変換することに注意してください。したがって、必要なポイントをいくつでも作成し、それらのポイントがホモグラフィで変換することでソースイメージ内のどこにあるかを計算します。次に、remap()を使用して、ソースイメージ内のそれらのポイントを検索し、それらを宛先イメージにマップします。

import numpy as np
import cv2

# read images
true_dst = cv2.imread("img1.png")
src = cv2.imread("img2.png")

# ground truth homography from true_dst to src
H = np.array([
    [8.7976964e-01,   3.1245438e-01,  -3.9430589e+01],
    [-1.8389418e-01,   9.3847198e-01,   1.5315784e+02],
    [1.9641425e-04,  -1.6015275e-05,   1.0000000e+00]])

# create indices of the destination image and linearize them
h, w = true_dst.shape[:2]
indy, indx = np.indices((h, w), dtype=np.float32)
lin_homg_ind = np.array([indx.ravel(), indy.ravel(), np.ones_like(indx).ravel()])

# warp the coordinates of src to those of true_dst
map_ind = H.dot(lin_homg_ind)
map_x, map_y = map_ind[:-1]/map_ind[-1]  # ensure homogeneity
map_x = map_x.reshape(h, w).astype(np.float32)
map_y = map_y.reshape(h, w).astype(np.float32)

# remap!
dst = cv2.remap(src, map_x, map_y, cv2.INTER_LINEAR)
blended = cv2.addWeighted(true_dst, 0.5, dst, 0.5, 0)
cv2.imshow('blended.png', blended)
cv2.waitKey()

Remap for warping

オックスフォードのビジュアルジオメトリグループ の画像とグラウンドトゥルースのホモグラフィ。

37
alkasm