web-dev-qa-db-ja.com

画像の横線を検出する

問題:次のような多くの画像を含むデータセットを使用しています:

img2img1

カラーパレットが画像の下部または右側になるように、これらすべての画像を水平または垂直に配置する必要があります。これは単に画像を回転させることで実行できますが、注意が必要なのは、回転する画像と回転しない画像を判別することです。

私が試したこと:

これを行う最善の方法は、カラーパレットと画像を分ける白い線を検出することだと思いました。パレットが下にあるすべての画像を回転させて、右側にあるようにしました。

# yes I am mixing between PIL and opencv (I like the PIL resizing more)
# resize image to be 128 by 128 pixels
img = img.resize((128, 128), PIL.Image.BILINEAR)
img = np.array(img)

# perform Edge detection, not sure if these are the best parameters for Canny
edges = cv2.Canny(img, 30, 50, 3, apertureSize=3)

has_line = 0

# take numpy slice of the area where the white line usually is 
# (not always exactly in the same spot which probably has to do with the way I resize my image) 
for line in edges[75:80]:

    # check if most of one of the lines contains white pixels
    counts = np.bincount(line)
    if np.argmax(counts) == 255:
        has_line = True

# rotate if we found such a line
if has_line == True:
    s = np.rot90(s)

正しく動作する例:

enter image description here

正しく動作しない例:

enter image description here

これはおそらく画像の98%で機能しますが、回転させるべきではない画像を回転させる場合や、回転させる必要がある画像を回転させない場合があります。たぶん、これを行う簡単な方法、またはより一貫性のあるより複雑な方法があるでしょうか?手動で行うこともできますが、多くの画像を扱っています。ヘルプやコメントをありがとう。

ここに、テスト目的でコードが失敗する画像があります:

enter image description hereenter image description here

5
Ahmad Moussa

250のような非常に高いしきい値を設定して画像をしきい値処理することから始め、線が白いという特性を利用できます。これにより、すべての背景が黒になります。次に、(1, 15)のような形状の特別な水平カーネルを作成し、それを使用して画像を侵食します。これにより、画像から縦線が削除され、横線のみが残ります。

import cv2
import numpy as np

img = cv2.imread('horizontal2.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)

kernel_hor = np.ones((1, 15), dtype=np.uint8)
erode = cv2.erode(thresh, kernel_hor)

thresheroded

質問で述べたように、色味は右側または下部にしかありません。したがって、適切な領域の輪郭がいくつあるかをテストして確認できます。このためには、画像を半分に分割し、適切な部分を選択します。輪郭を見つける前に、通常の(3, 3)カーネルでギャップを埋めるために結果を拡張します。 cv2.RETR_EXTERNALを使用して輪郭を見つけ、見つかった数を数えます。特定の数よりも大きい場合、画像は正しい面を上にしており、回転する必要はありません。

right = erode[:, erode.shape[1]//2:]

kernel = np.ones((3, 3), dtype=np.uint8)
right = cv2.dilate(right, kernel)

cnts, _ = cv2.findContours(right, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(cnts) > 3:
    print('No need to rotate')
else:
    print('rotate')
    #ADD YOUR ROTATE CODE HERE

追伸私はあなたが提供した4つの画像すべてをテストしましたが、うまくいきました。万が一画像に対して機能しない場合はお知らせください。

1
Vardan Agarwal