2017-02-15 80 views
1

画像を傾き補正したいと思います。私は(確かに助けたくさんの)プログラムを書いたことを行うには:OpenCV/Python:cv2.minAreaRectは、回転した矩形を返しません。

    1. は(などTHRESH、拡張を、)を計算しやすいように画像を変換すると、すべてのオブジェクトの周りの輪郭
    2. を計算を描きますテキストの輪郭の周りの4つの極端なポイントが

    アイデアはcv2.minAreaRectがどの私ができるだけでなく角度を返すということでしたcv2.minAreaRectを使用して、その領域の周囲に四角形を描画します(マージンを使って何を無視して)イメージの傾き補正に使用します。しかし、私の場合は-90°です。

    サンプル入力画像hereが表示されます。 私はhereという結果を見ることができます。

    私は「クリーン」の画像上のプログラムをテストした(MS Wordのスクリーンショットは、Gimpの中≈30°をrotaten)、それは、同一の結果を与えました。

    マイコード:

    import numpy as np 
    import cv2 
    import itertools 
    
    img = cv2.imread('zuo.png') 
    imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
    
    ret,thresh = cv2.threshold(imgray,64,255,0) 
    ############ 
    kernel = np.ones((2,2),np.uint8) 
    img_e = cv2.dilate(thresh,kernel,iterations = 1) 
    # cv2.imwrite("out_eroded.png", img_e) 
    # http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html 
    # img_e = thresh 
    ############ 
    imgbw, contours, hierarchy = cv2.findContours(img_e,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 
    # imgbw, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 
    
    margin_distance = 25 
    
    def flatten(arr, n = 1): 
        # print(arr) 
        ret = list(itertools.chain.from_iterable(arr)) 
        # print(ret) 
        if n != 1: 
         return flatten(ret, n - 1) 
        else: 
         return ret 
    
    # print(list(flatten([[1,2,3],[4,5,6], [7], [8,9]]))) 
    
    def get_min_max_values(cs, im_y, im_x): 
        # print(flatten(cs), 1) 
        # print(im_y, im_x) 
        min_y = im_y - margin_distance 
        min_x = im_x - margin_distance 
        max_y = margin_distance 
        max_x = margin_distance 
        for lvl1 in cs: 
         for lvl2 in lvl1: 
          x, y = lvl2[0] 
          # x = im_x - x 
          # y = im_y - y 
          max_y = max(y, max_y) if y + margin_distance < im_y else max_y 
          max_x = max(x, max_x) if x + margin_distance < im_x else max_x 
          min_y = min(y, min_y) if y > margin_distance else min_y 
          min_x = min(x, min_x) if x > margin_distance else min_x 
    
        return ((min_y, min_x), (min_y, max_x), (max_y, min_x), (max_y, max_x)) 
    
    new_rect = get_min_max_values(contours, len(img), len(img[0])) 
    new_rect = list(map(lambda x: list(x)[::-1], list(new_rect))) 
    print(new_rect) 
    rect = cv2.minAreaRect(np.int0(new_rect)) 
    # print(rect) 
    print(rect) 
    box = cv2.boxPoints(rect) 
    box = np.int0(box) 
    
    img_out = cv2.drawContours(img, [box], -1, (0,0,255), 5) # -1 = wszystkie kontury 
    img_out = cv2.drawContours(img, contours, -1, (0,255,0), 3) 
    
    cv2.imwrite("out.png", img_out) 
    

    矩形はテキストに一致するようにスキューされていないのはなぜ?私はそれを正当化する人工物は見ません。

    EDIT:追加されましたきれいな、生まれたデジタルファイル:inputoutput

    +1

    あなたは 'new_rect'に含まれる点(赤い丸または任意で)をプロットすることはできますか? – Miki

    +0

    GIMPイメージと結果も追加できますか? – Micka

    +1

    get_min_max_valuesでは、axis aligned rect(コーナーポイント)を返すので、minAreaRectが最適化します。 – Micka

    答えて

    2

    TLDR:使用凸包の代わりに、わずか4ポイント!

    パート1:現在の方法でのエラー。

    関数get_min_max_valuesは、axis-aligned bounding box of all contoursのコーナーポイントを計算します。しかし実際にここで計算したいのは、すべての輪郭の左端、最上部、右端、最下部の座標です。代わりに、最小限のYを「覚えて」の

    は、あなたはyが最小であったポイント(一番上のポイント)の両方の座標を保持する必要があります。他のすべての点についても同様です。

    以下のコードは、これらのポイントを正しく計算する方法を示しています。コードスニペットを短くして読みやすいようにすることにしました。そのため、ここでは左端と上端のポイントを計算する方法しか示していません。すべての4点は同じ方法で計算されます...

    お気づきのように、私はループで余白に点を(clamp)比較しません。代わりに、これを行うのは同じ結果を生成するので、ループの最後に一度だけ行いますが、コードは簡単です。今

    def get_min_max_values(cs, im_height, im_width): 
    
        min_y = im_height - margin_distance 
        min_x = im_width - margin_distance 
    
        left_point = (min_y, min_x) 
        top_point = (min_y, min_x) 
    
        for lvl1 in cs: 
        for lvl2 in lvl1: 
         x, y = lvl2[0] 
    
         left_point = left_point if x > left_point[1] else (y, x) 
         top_point = top_point if y > top_point[0] else (y, x) 
    
        left_point[0] = left_point[0] if left_point[0] > margin_distance else margin_distance + 1 
        left_point[1] = left_point[1] if left_point[1] > margin_distance else margin_distance + 1 
    
        top_point[0] = top_point[0] if top_point[0] > margin_distance else margin_distance + 1 
        top_point[1] = top_point[1] if top_point[1] > margin_distance else margin_distance + 1 
    
    
        return (top_point, left_point) 
    

    私たちは結果を見てみましょう:

    enter image description here

    あなたはすべての4つの「極値」のポイントが回転した矩形の内側に確かにあるが、他の点の多くは、のために外に残っていることがわかります

    「最小領域」制約。この作業を正しく行うために、最小限の回転した境界矩形を計算するときは、すべての「境界」点を考慮する必要があります。

    パート2:あなたは、同じ配列にすべてのそれらの輪郭点をコピーする必要があり、その後、最終的にはあなたがパスを持っているあなたはfindContoursで輪郭を計算した後に動作し、あなたのコード

    で最小限の変更が必要となるソリューションその配列はconvexHull functionになります。この関数はconvex hullポイントを計算します。その後、minAreaRect機能のための入力として、これらのポイントを使用し、これはあなたが得るものです:

    enter image description here

    さらに、ソリューション

    を改善する私は、あなたがしなければ、あなたのアルゴリズムははるかに高速に実行することができますかなり確信しています輪郭をまったく計算しない。その代わりに、凸包関数の入力として閾値化されたピクセル位置を使用してください。

    +0

    提案とサンプルコードをお寄せいただきありがとうございます。私はあなたが示唆したコードの残りを外挿して適応させようと試みましたが、それは本当に奇妙な座標を戻し続けます。私は最終的な提案をどのように適用するかについてのアイデアを持っているので、このコードの実行に伴う問題にもかかわらず、あなたの答えを受け入れるようにしてください。 – MrVocabulary

    +0

    私はあなたが私が答えた(リフレッシュしなかった)前に投稿を編集したことに気づいただけです。あなたの助けを借りてくれてありがとう - あなたが得たアウトプットは、私が探していたものです!しかし、私は結果を再現するのに問題があります。矩形を描画しないだけです。私の試行を見て、私が逃していることを教えてください。 http://pastebin.com/VEVHgtii – MrVocabulary

    +1

    こんにちは、見つかったすべての輪郭の*すべての点を1つの配列に入れて、それをconvexHullに引数として渡すだけでよいように見えます。 ところで、輪郭の使用を避け、代わりにしきい値点を使用するように私のアドバイスを忘れないでください。これは仕事をより簡単にし、結果は同じになります。 :) – Nejc

    関連する問題