画像内の複数の長方形を検出する


13

この画像でパイプの数を検出しようとしています。これには、OpenCVとPythonベースの検出を使用しています。同様の質問に対する既存の回答に基づいて、次の手順を思い付くことができました

  1. 画像を開く
  2. フィルターする
  3. エッジ検出を適用する
  4. 等高線を使用
  5. カウントを確認する

ここに画像の説明を入力してください

パイプの総数は〜909です我々はそれを手動でカウント与えるか、または4を取るとき。

フィルター適用後

import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread('images/input-rectpipe-1.jpg')
blur_hor = cv2.filter2D(img[:, :, 0], cv2.CV_32F, kernel=np.ones((11,1,1), np.float32)/11.0, borderType=cv2.BORDER_CONSTANT)
blur_vert = cv2.filter2D(img[:, :, 0], cv2.CV_32F, kernel=np.ones((1,11,1), np.float32)/11.0, borderType=cv2.BORDER_CONSTANT)
mask = ((img[:,:,0]>blur_hor*1.2) | (img[:,:,0]>blur_vert*1.2)).astype(np.uint8)*255

このマスクされた画像が表示されます

ここに画像の説明を入力してください

これは、表示される長方形の数の点でかなり正確に見えます。ただし、カウントして画像の上にバウンディングボックスをプロットしようとすると、多くの不要な領域も選択されます。円の場合、HoughCirclesには最大半径と最小半径を定義する方法があります。精度を向上させることができる長方形に似たものはありますか?また、私はこの問題への代替アプローチの提案を受け入れています。

ret,thresh = cv2.threshold(mask,127,255,0)
contours,hierarchy = cv2.findContours(thresh, 1, 2)

count = 0

for i in range(len(contours)):

  count = count+1
  x,y,w,h = cv2.boundingRect(contours[i]) 
  rect = cv2.minAreaRect(contours[i])
  area = cv2.contourArea(contours[i])
  box = cv2.boxPoints(rect)
  ratio = w/h
  M = cv2.moments(contours[i])

  if M["m00"] == 0.0:
         cX = int(M["m10"] / 1 )
         cY = int(M["m01"] / 1 )

  if M["m00"] != 0.0:
    cX = int(M["m10"] / M["m00"])
    cY = int(M["m01"] / M["m00"])

  if (area > 50 and area < 220 and hierarchy[0][i][2] < 0 and (ratio > .5 and ratio < 2)):
    #cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
    cv2.circle(img, (cX, cY), 1, (255, 255, 255), -1)
    count = count + 1 



print(count)

cv2.imshow("m",mask)
cv2.imshow("f",img)
cv2.waitKey(0)

ここに画像の説明を入力してください

更新 2番目の回答に基づいて、c ++コードをpythonコードに変換し、より近い結果を得ましたが、いくつかの明らかな四角形がまだありません。

ここに画像の説明を入力してください


あなたの狂ったイメージで、拡張操作を実行してください。次に、内部輪郭のみを検出します(第1レベル)。
ミカ

マスク画像をpngとして提供できますか?
ミカ

1
質問をpngバージョンで更新しました
Donny

検出する必要があるパイプの数についてグラウンドトゥルースありますか?
TA

試行錯誤の1つは、しきい値のステップを調整して、欠落した検出を改善することです。大津のしきい値または適応しきい値を調べます。ただし、現在のソリューションはおそらく、従来の画像処理技術を使用して取得する最高のものです。それ以外の場合は、深層/機械学習を調べることができます
ナサンシー

回答:


6

もちろん、あなたはそれらをその地域でフィルタリングすることができます。私はあなたのバイナリ画像を取り、以下のように作業を続けました:

1- findContoursから見つけたすべての輪郭でループを実行します

2-ループで、各輪郭が内部輪郭であるかどうかを確認します

3-内部輪郭であるものから、それらの面積をチェックし、面積が許容範囲内にあるかどうか、各輪郭の幅/高さの比率をチェックし、最後にそれも良ければ、その輪郭をパイプとして数えます。

私はあなたのバイナリイメージに対して上記の方法を実行し、794パイプ見つけました

ここに画像の説明を入力してください

(ただし、一部のボックスが失われます。エッジ検出器のパラメーターを変更して、画像でより分離可能なボックスを取得する必要があります。)

そしてここにコードがあります(それはc ++ですが簡単にPythonに変換可能です):

Mat img__1, img__2,img__ = imread("E:/R.jpg", 0);

threshold(img__, img__1, 128, 255, THRESH_BINARY);

vector<vector<Point>> contours;
vector< Vec4i > hierarchy;

findContours(img__1, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_NONE);

Mat tmp = Mat::zeros(img__1.size(), CV_8U);
int k = 0;
for (size_t i = 0; i < contours.size(); i++)
{
    double area = contourArea(contours[i]);
    Rect rec = boundingRect(contours[i]);
    float ratio = rec.width / float(rec.height);

    if (area > 50 && area < 220 && hierarchy[i][2]<0 && (ratio > .5 && ratio < 2) ) # hierarchy[i][2]<0 stands for internal contours
    {
        k++;
        drawContours(tmp, contours, i, Scalar(255, 255, 255), -1);
    }
}
cout << "k= " << k << "\n";
imshow("1", img__1); 
imshow("2", tmp);
waitKey(0);

2

この問題を解決する方法はたくさんありますが、なんらかの特別な対策なしに単一の方法があるとは思えません。この問題に対する別の試みを次に示します。

エッジ情報を使用する代わりに、周囲のピクセルを中心値と比較するLBP(ローカルバイナリパターン)のようなフィルターを提案します。周囲のピクセルの特定のパーセンテージが中央のピクセルよりも大きい場合、中央のピクセルには255のラベルが付けられます。条件が満たされない場合、中央のピクセルには0のラベルが付けられます。

この強度ベースの方法は、パイプの中心がパイプのエッジより常に暗いという前提で実行されます。それは強度を比較しているので、ある程度のコントラストが残っている限り、うまく機能するはずです。

このプロセスにより、すべてのパイプのバイナリブロブといくつかのノイズを含む画像が得られます。サイズ、形状、fill_ratio、色などの既知の条件でそれらを削除する必要があります。条件は指定されたコードにあります。

import cv2
import matplotlib.pyplot as plt
import numpy as np

# Morphological function sets
def morph_operation(matinput):
  kernel =  cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))

  morph = cv2.erode(matinput,kernel,iterations=1)
  morph = cv2.dilate(morph,kernel,iterations=2)
  morph = cv2.erode(matinput,kernel,iterations=1)
  morph = cv2.dilate(morph,kernel,iterations=1)

  return morph


# Analyze blobs
def analyze_blob(matblobs,display_frame):

  _,blobs,_ = cv2.findContours(matblobs,cv2.RETR_LIST ,cv2.CHAIN_APPROX_SIMPLE)
  valid_blobs = []

  for i,blob in enumerate(blobs):
    rot_rect = cv2.minAreaRect(blob)
    b_rect = cv2.boundingRect(blob)


    (cx,cy),(sw,sh),angle = rot_rect
    rx,ry,rw,rh = b_rect

    box = cv2.boxPoints(rot_rect)
    box = np.int0(box)

    # Draw the segmented Box region
    frame = cv2.drawContours(display_frame,[box],0,(0,0,255),1)

    on_count = cv2.contourArea(blob)
    total_count = sw*sh
    if total_count <= 0:
      continue

    if sh > sw :
      temp = sw
      sw = sh
      sh = temp

    # minimum area
    if sw * sh < 20:
      continue

    # maximum area
    if sw * sh > 100:
      continue  

    # ratio of box
    rect_ratio = sw / sh
    if rect_ratio <= 1 or rect_ratio >= 3.5:
      continue

    # ratio of fill  
    fill_ratio = on_count / total_count
    if fill_ratio < 0.4 :
      continue

    # remove blob that is too bright
    if display_frame[int(cy),int(cx),0] > 75:
      continue


    valid_blobs.append(blob)

  if valid_blobs:
    print("Number of Blobs : " ,len(valid_blobs))
  cv2.imshow("display_frame_in",display_frame)

  return valid_blobs

def lbp_like_method(matinput,radius,stren,off):

  height, width = np.shape(matinput)

  roi_radius = radius
  peri = roi_radius * 8
  matdst = np.zeros_like(matinput)
  for y in range(height):
    y_ = y - roi_radius
    _y = y + roi_radius
    if y_ < 0 or _y >= height:
      continue


    for x in range(width):
      x_ = x - roi_radius
      _x = x + roi_radius
      if x_ < 0 or _x >= width:
        continue

      r1 = matinput[y_:_y,x_]
      r2 = matinput[y_:_y,_x]
      r3 = matinput[y_,x_:_x]
      r4 = matinput[_y,x_:_x]

      center = matinput[y,x]
      valid_cell_1 = len(r1[r1 > center + off])
      valid_cell_2 = len(r2[r2 > center + off])
      valid_cell_3 = len(r3[r3 > center + off])
      valid_cell_4 = len(r4[r4 > center + off])

      total = valid_cell_1 + valid_cell_2 + valid_cell_3 + valid_cell_4

      if total > stren * peri:
        matdst[y,x] = 255

  return matdst


def main_process():

  img = cv2.imread('image.jpg')    
  gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)



  # Blured to remove noise 
  blurred = cv2.GaussianBlur(gray,(3,3),-1)

  # Parameter tuning
  winsize = 5
  peri = 0.6
  off = 4

  matlbp = lbp_like_method(gray,winsize,peri,off)
  cv2.imshow("matlbp",matlbp)
  cv2.waitKey(1)

  matmorph = morph_operation(matlbp)
  cv2.imshow("matmorph",matmorph)
  cv2.waitKey(1)


  display_color = cv2.cvtColor(gray,cv2.COLOR_GRAY2BGR)
  valid_blobs = analyze_blob(matmorph,display_color)


  for b in range(len(valid_blobs)):
    cv2.drawContours(display_color,valid_blobs,b,(0,255,255),-1)


  cv2.imshow("display_color",display_color)
  cv2.waitKey(0)


if __name__ == '__main__':
  main_process()

LBPに似た処理の結果 ここに画像の説明を入力してください

形態学的プロセスで洗浄した後 ここに画像の説明を入力してください

赤いボックスがすべてのblob候補を示し、黄色のセグメントが、設定したすべての条件に合格したblobを示す最終結果。パイプバンドルの下および上にいくつかの誤警報がありますが、いくつかの境界条件で省略できます。 ここに画像の説明を入力してください

見つかったパイプの総数:943


コード、blobs、_ = cv2.findContours(matblobs、cv2.RETR_LIST、cv2.CHAIN_APPROX_SIMPLE)の実行中にこのエラーが発生しますValueError:解凍するのに十分な値がありません(3、2が必要です)
Donny

異なるバージョンのopencvを使用している必要があります。関数から受け取る最初のアンダースコア "_"を元のコードから削除するだけです。blobs、_ = cv2.findContours(matblobs、cv2.RETR_LIST、cv2.CHAIN_APPROX_SIMPLE)
yapws87
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.