Mendeteksi beberapa persegi panjang dalam gambar

13

Saya mencoba mendeteksi jumlah pipa dalam gambar ini. Untuk ini, saya menggunakan deteksi berbasis OpenCV dan Python. Berdasarkan, pada jawaban yang ada untuk pertanyaan serupa, saya dapat menemukan langkah-langkah berikut

  1. Buka gambar
  2. Saring
  3. Terapkan Deteksi Tepi
  4. Gunakan Kontur
  5. Periksa hitungannya

masukkan deskripsi gambar di sini

Jumlah total pipa adalah ~ 909 ketika kami menghitung secara manual, beri atau ambil 4.

Setelah menerapkan filter

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

Saya mendapatkan gambar bertopeng ini

masukkan deskripsi gambar di sini

Ini terlihat cukup akurat dalam hal jumlah persegi panjang yang terlihat itu menunjukkan. Namun, ketika saya mencoba mengambil hitungan dan memplot kotak pembatas di atas gambar, ia mengambil banyak wilayah yang tidak diinginkan juga. Untuk lingkaran, HoughCircles memiliki cara untuk menentukan radius maks dan min. Apakah ada yang serupa untuk persegi panjang yang dapat meningkatkan akurasi. Juga, saya terbuka untuk saran untuk pendekatan alternatif untuk masalah ini.

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)

masukkan deskripsi gambar di sini

PEMBARUAN Berdasarkan jawaban kedua saya telah mengonversi kode c ++ ke kode python dan mendapatkan hasil yang lebih dekat tetapi masih kehilangan beberapa persegi panjang yang jelas.

masukkan deskripsi gambar di sini

Donny
sumber
pada gambar yang dibuat, lakukan operasi dilatasi. Kemudian deteksi hanya kontur dalam (level pertama).
Micka
dapatkah Anda memberikan gambar topeng Anda sebagai png?
Micka
1
Saya telah memperbarui pertanyaan dengan versi png
Donny
Apakah Anda memiliki kebenaran mendasar tentang berapa banyak pipa yang harus dideteksi?
TA
Satu hal yang bisa Anda coba adalah menyetel langkah ambang untuk meningkatkan deteksi yang hilang. Lihatlah ke ambang batas Otsu atau ambang batas adaptif. Namun, solusi Anda saat ini mungkin yang terbaik yang akan Anda dapatkan dengan menggunakan teknik pemrosesan gambar tradisional. Kalau tidak, Anda bisa melihat ke dalam / pembelajaran mesin
nathancy

Jawaban:

6

Tentu saja Anda dapat memfilternya berdasarkan area mereka. Saya mengambil gambar biner Anda dan melanjutkan pekerjaan seperti di bawah ini:

1- Lakukan pengulangan pada semua kontur yang Anda temukan dari findContour

2- Dalam pemeriksaan loop apakah setiap kontur, apakah kontur internal atau tidak

3- Dari kontur internal, periksa area mereka dan jika area tersebut dalam kisaran yang dapat diterima, periksa rasio lebar / tinggi masing-masing kontur dan akhirnya jika itu baik, hitung kontur itu sebagai pipa.

Saya melakukan metode di atas pada gambar biner Anda, dan menemukan 794 pipa :

masukkan deskripsi gambar di sini

(Beberapa kotak hilang, Anda harus mengubah parameter detektor tepi untuk mendapatkan lebih banyak kotak yang dapat dipisahkan dalam gambar.)

dan inilah kodenya (Ini c ++ tetapi mudah dikonversi menjadi 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);
MH304
sumber
2

Ada banyak metode untuk mengatasi masalah ini tetapi saya ragu akan ada satu metode tanpa semacam tindakan ad-hod. Berikut adalah upaya lain untuk masalah ini.

Alih-alih menggunakan informasi tepi, saya sarankan filter mirip LBP (pola biner lokal) yang membandingkan piksel sekitarnya dengan nilai tengah. Jika persentase tertentu dari piksel di sekitarnya lebih besar dari piksel tengah, piksel tengah akan berlabel 255. jika kondisinya tidak terpenuhi, maka piksel tengah akan diberi label 0.

Metode berbasis intensitas ini dijalankan dengan asumsi bahwa pusat pipa selalu lebih gelap daripada tepi pipa. Karena membandingkan intensitas, itu harus bekerja dengan baik selama beberapa kontras tetap ada.

Melalui proses ini, Anda akan mendapatkan gambar dengan gumpalan biner untuk setiap pipa dan beberapa suara. Anda harus menghapusnya dengan kondisi yang sudah diketahui sebelumnya seperti, ukuran, bentuk, fill_ratio, warna, dll. Kondisi ini dapat ditemukan dalam kode yang diberikan.

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

Hasil dari pemrosesan seperti LBP masukkan deskripsi gambar di sini

Setelah dibersihkan dengan proses morfologis masukkan deskripsi gambar di sini

Hasil akhir dengan kotak merah menunjukkan semua kandidat gumpalan dan segmen kuning menunjukkan gumpalan yang melewati semua kondisi yang kita tetapkan. Ada beberapa alarm palsu di bawah dan di atas bundel pipa tetapi mereka dapat dihilangkan dengan beberapa syarat batas. masukkan deskripsi gambar di sini

Total pipa ditemukan: 943

yapws87
sumber
Saya mendapatkan kesalahan ini saat menjalankan kode, gumpalan, _ = cv2.findContours (matblobs, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) ValueError: nilai tidak cukup untuk membongkar (diharapkan 3, didapat 2)
Donny
Anda harus menggunakan versi opencv yang berbeda. Yang perlu Anda lakukan adalah menghapus garis bawah pertama, "_", dari kode asli untuk menerima dari fungsi. gumpalan, _ = cv2.findContours (matblobs, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
yapws87