Phát hiện nhiều hình chữ nhật trong ảnh


13

Tôi đang cố gắng phát hiện số lượng đường ống trong bức tranh này. Đối với điều này, tôi đang sử dụng OpenCV và phát hiện dựa trên Python. Dựa trên các câu trả lời hiện có cho các câu hỏi tương tự, tôi đã có thể đưa ra các bước sau

  1. Mở hình ảnh
  2. Lọc nó
  3. Áp dụng phát hiện cạnh
  4. Sử dụng đường viền
  5. Kiểm tra số lượng

nhập mô tả hình ảnh ở đây

Tổng số đường ống là ~ 909 khi chúng ta đếm bằng tay cho hoặc lấy 4.

Sau khi áp dụng bộ lọc

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

Tôi nhận được hình ảnh đeo mặt nạ này

nhập mô tả hình ảnh ở đây

Điều này có vẻ khá chính xác về số lượng hình chữ nhật có thể nhìn thấy mà nó hiển thị. Tuy nhiên, khi tôi cố gắng đếm và vẽ ô giới hạn ở trên cùng của hình ảnh, nó cũng chọn rất nhiều vùng không mong muốn. Đối với các vòng tròn, HoughCircles có cách xác định bán kính tối đa và tối thiểu. Có một cái gì đó tương tự cho hình chữ nhật có thể cải thiện độ chính xác. Ngoài ra, tôi sẵn sàng đề xuất các phương pháp tiếp cận thay thế cho vấn đề này.

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)

nhập mô tả hình ảnh ở đây

CẬP NHẬT Dựa trên câu trả lời thứ hai, tôi đã chuyển đổi mã c ++ thành mã python và có kết quả gần hơn nhưng vẫn bỏ lỡ một vài hình chữ nhật rõ ràng.

nhập mô tả hình ảnh ở đây


trên hình ảnh điên rồ của bạn, petform một hoạt động giãn. Sau đó chỉ phát hiện các đường viền bên trong (cấp độ đầu tiên).
Micka

bạn có thể cung cấp hình ảnh mặt nạ của bạn như png?
Micka

1
Tôi đã cập nhật câu hỏi với phiên bản png
Donny

Bạn có một sự thật nền tảng về bao nhiêu đường ống nên được phát hiện?
TA

Một điều bạn có thể thử có thể là điều chỉnh bước ngưỡng để cải thiện các phát hiện bị thiếu. Nhìn vào ngưỡng của Otsu hoặc ngưỡng thích ứng. Tuy nhiên, giải pháp hiện tại của bạn có lẽ là giải pháp tốt nhất bạn sẽ sử dụng các kỹ thuật xử lý ảnh truyền thống. Nếu không, bạn có thể nhìn vào sâu học tập / máy
nathancy

Câu trả lời:


6

Tất nhiên bạn có thể lọc chúng theo khu vực của họ. Tôi lấy hình ảnh nhị phân của bạn và tiếp tục công việc như dưới đây:

1- Thực hiện một vòng lặp trên tất cả các đường viền bạn tìm thấy từ findContours

2- Trong vòng kiểm tra xem mỗi đường viền, có phải là đường viền bên trong hay không

3- Từ những đường viền bên trong, kiểm tra khu vực của chúng và nếu khu vực đó nằm trong phạm vi chấp nhận được, hãy kiểm tra tỷ lệ chiều rộng / chiều cao của mỗi đường viền và cuối cùng nếu nó cũng tốt, hãy tính đường viền đó là một đường ống.

Tôi đã thực hiện phương pháp trên trên hình ảnh nhị phân của bạn và tìm thấy 794 ống :

nhập mô tả hình ảnh ở đây

(Tuy nhiên, một số hộp bị mất, Bạn nên thay đổi các tham số của bộ dò cạnh để có được các hộp tách biệt hơn trong ảnh.)

và đây là mã (Đó là c ++ nhưng dễ dàng chuyển đổi thành 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

Có nhiều phương pháp để giải quyết vấn đề này nhưng tôi nghi ngờ sẽ có một phương pháp duy nhất mà không có một số biện pháp quảng cáo. Đây là một nỗ lực khác cho vấn đề này.

Thay vì sử dụng thông tin cạnh, tôi đề xuất bộ lọc giống như LBP (mẫu nhị phân cục bộ) so sánh pixel xung quanh với giá trị trung tâm. Nếu một tỷ lệ phần trăm pixel xung quanh lớn hơn pixel trung tâm, pixel trung tâm sẽ được gắn nhãn 255. nếu điều kiện không được đáp ứng, thì pixel trung tâm sẽ được gắn nhãn 0.

Phương pháp dựa trên cường độ này được thực hiện dựa trên giả định rằng tâm ống luôn tối hơn các cạnh ống. Vì nó đang so sánh cường độ, nên nó sẽ hoạt động tốt miễn là vẫn còn một số độ tương phản.

Thông qua quá trình này, bạn sẽ có được một hình ảnh với các đốm nhị phân cho mỗi đường ống và một số tiếng ồn. Bạn sẽ phải loại bỏ chúng với một số điều kiện đã biết trước như, kích thước, hình dạng, fill_ratio, màu sắc, v.v. Điều kiện có thể được tìm thấy trong mã đã cho.

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()

Kết quả từ quá trình xử lý giống như LBP nhập mô tả hình ảnh ở đây

Sau khi làm sạch với quá trình hình thái nhập mô tả hình ảnh ở đây

Kết quả cuối cùng với các ô màu đỏ hiển thị tất cả các ứng cử viên blob và các phân đoạn màu vàng hiển thị các đốm màu vượt qua tất cả các điều kiện chúng tôi đặt ra. Có một số báo động sai bên dưới và trên cùng của bó ống nhưng chúng có thể được bỏ qua với một số điều kiện biên. nhập mô tả hình ảnh ở đây

Tổng số ống tìm thấy: 943


Tôi nhận được lỗi này khi chạy mã, blobs, _ = cv2.findContours (matblobs, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) ValueError: không đủ giá trị để giải nén (dự kiến 3, có 2)
Donny

bạn phải sử dụng một phiên bản khác của opencv. Tất cả những gì bạn cần làm là xóa dấu gạch dưới đầu tiên, "_", khỏi mã gốc để nhận từ hàm. các đốm màu, _ = cv2.findContours (matblobs, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
yapws87
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.