ตรวจจับหลายสี่เหลี่ยมในภาพ


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)

ป้อนคำอธิบายรูปภาพที่นี่

UPDATE จากคำตอบที่สองฉันได้แปลงรหัส c ++ เป็นรหัสไพ ธ อนและได้ผลลัพธ์ที่ใกล้เคียงขึ้น

ป้อนคำอธิบายรูปภาพที่นี่


บนรูปภาพที่ถูกสาปของคุณทำให้รูปแบบการขยายขยาย จากนั้นตรวจสอบรูปทรงภายในเท่านั้น (ระดับแรก)
มิกคา

คุณสามารถให้ภาพหน้ากากเป็น png ได้ไหม
มิกคา

1
ฉันได้อัปเดตคำถามด้วยรุ่น png แล้ว
Donny

คุณมีความจริงพื้นฐานเกี่ยวกับจำนวนท่อที่ควรตรวจจับหรือไม่?
TA

สิ่งหนึ่งที่คุณสามารถลองได้คือการปรับแต่งขั้นตอนการนวดเพื่อปรับปรุงการตรวจจับที่หายไป มองดูการตีลังกาของ Otsu หรือการปรับเกณฑ์ อย่างไรก็ตามโซลูชันปัจจุบันของคุณน่าจะดีที่สุดที่คุณจะใช้เทคนิคการประมวลผลภาพแบบดั้งเดิม มิฉะนั้นคุณสามารถตรวจสอบการเรียนรู้ของเครื่อง / ลึก
nathancy

คำตอบ:


6

แน่นอนคุณสามารถกรองพวกเขาตามพื้นที่ของพวกเขา ฉันถ่ายภาพไบนารีของคุณและทำงานต่อไปดังนี้:

1- วนรอบรูปทรงทั้งหมดที่คุณพบจาก findContours

2- ในการตรวจสอบลูปถ้าแต่ละ contour เป็น contour ภายในหรือไม่

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 ป้อนคำอธิบายรูปภาพที่นี่

หลังจากทำความสะอาดด้วยกระบวนการทางสัณฐานวิทยา ป้อนคำอธิบายรูปภาพที่นี่

ผลสุดท้ายด้วยกล่องสีแดงแสดงผู้สมัครหยดและกลุ่มสีเหลืองแสดง blobs ที่ผ่านเงื่อนไขทั้งหมดที่เราตั้งไว้ มีการเตือนที่ผิดพลาดด้านล่างและด้านบนของชุดท่อ แต่สามารถละเว้นได้ด้วยเงื่อนไขขอบเขต ป้อนคำอธิบายรูปภาพที่นี่

พบท่อทั้งหมด: 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.