よく質問はそれをすべて言います
輪郭があり、そこから最高の四角形を取得したい
角が丸いカードで問題が発生しましたapproxPolyDP()
。4ポイント/エッジしか返さなかったとしても、「角」がこれらの丸いエッジに近づくように頻繁に選択されるため、最高ではありませんでした。私の解決策は、コーナーをたどるために返されるポイントの数を増やすことでした。その後、私の問題により、見つかった4つの最も長いエッジが側面を表すと比較的安全に想定することができました。これらの4つの最も長いエッジを順番に取り、各ペアが交差する場所を見つけて、クワッドのポイントを取得します。
ボーナスとして、あなたはすでに長さを持っているので、あなたはそれが間違った方法で押しつぶされたり伸びたりしないように正しい順序であなたのパースペクティブ変換にポイントを供給することができます。
epsilon
ポイントの数は。の減少とともに増加するため、ネストされた区間の方法を使用して適切な値を見つける必要がありますepsion
。ただし、イプシロンの特定の値でポイントの数が3から5にジャンプする可能性があるため、4つのコーナーポイントの値に到達できない可能性があります。
このケースを除外したい場合は、Ramer–Douglas–Peuckerアルゴリズムを自分で実装して変更する必要があるかもしれません。そうすれば、指定された数のポイントが返されます。(ちなみに、あなたは「最良の」4つのコーナーを求めています。あなたは最良の意味を指定する必要があります。approxPolyDp()
最適な解決策を主張していません!)
それ以外に、approxPolyDP()
4ポイントを強制的に返す方法はありません。
値に対して二分探索を実行して、epsilon
最良の単純化を見つけることができると思います。
コード:
def simplify_contour(contour, n_corners=4):
'''
Binary searches best `epsilon` value to force contour
approximation contain exactly `n_corners` points.
:param contour: OpenCV2 contour.
:param n_corners: Number of corners (points) the contour must contain.
:returns: Simplified contour in successful case. Otherwise returns initial contour.
'''
n_iter, max_iter = 0, 100
lb, ub = 0., 1.
while True:
n_iter += 1
if n_iter > max_iter:
return contour
k = (lb + ub)/2.
eps = k*cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, eps, True)
if len(approx) > n_corners:
lb = (lb + ub)/2.
elif len(approx) < n_corners:
ub = (lb + ub)/2.
else:
return approx