Görüntüdeki birden çok dikdörtgeni algılama


13

Bu resimdeki boru sayısını tespit etmeye çalışıyorum. Bunun için OpenCV ve Python tabanlı algılama kullanıyorum. Benzer soruların mevcut cevaplarına dayanarak, aşağıdaki adımları bulabildim

  1. Resmi aç
  2. Filtrele
  3. Kenar Algılama Uygula
  4. Kontür Kullan
  5. Sayıyı kontrol et

resim açıklamasını buraya girin

Elle saydığımızda veya aldığımızda toplam boru sayısı ~ 909'dur .

Filtreyi uyguladıktan sonra

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

Bu maskeli görüntüyü alıyorum

resim açıklamasını buraya girin

Bu, gösterdiği görünür dikdörtgenlerin sayısı açısından oldukça doğru görünüyor. Ancak, sayımı alıp sınırlayıcı kutuyu resmin üzerine çizmeye çalıştığımda, birçok istenmeyen bölgeyi de seçer. Daireler için HoughCircles, max ve min yarıçapını tanımlamanın bir yoluna sahiptir. Doğruluğu artırabilecek dikdörtgenler için benzer bir şey var mı? Ayrıca, bu soruna alternatif yaklaşımlar için önerilere açığım.

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)

resim açıklamasını buraya girin

GÜNCELLEME İkinci cevaba dayanarak c ++ kodunu python koduna dönüştürdüm ve daha yakın sonuçlar aldım ama yine de birkaç belirgin dikdörtgen üzerinde eksik kaldım.

resim açıklamasını buraya girin


çılgın görüntünüzde bir dilate operasyon yapın. Sonra sadece iç konturları tespit edin (birinci seviye).
Micka

maske resminizi png olarak sağlayabilir misiniz?
Micka

1
Soruyu png sürümü ile güncelledim
Donny

Kaç borunun tespit edilmesi gerektiği konusunda temel bir gerçeğiniz var mı?
TA

Deneyebileceğiniz bir şey, eksik tespitleri iyileştirmek için eşikleme adımını ayarlamak olabilir. Otsu eşiğine veya uyarlanabilir eşiğine bakın. Ancak, mevcut çözümünüz muhtemelen geleneksel görüntü işleme tekniklerini kullanarak alacağınız en iyisidir. Aksi takdirde derin / makine öğrenimi
nathancy

Yanıtlar:


6

Tabii onları bölgelerine göre filtreleyebilirsiniz. İkili görüntünüzü aldım ve çalışmaya aşağıdaki gibi devam ettim:

1- findContours'ta bulduğunuz tüm konturlarda bir döngü yapın

2- Döngüde her bir konturun dahili bir kontur olup olmadığını kontrol edin

3- İç konturlardan alanlarını kontrol edin ve alan kabul edilebilir aralıkta ise, her konturun genişlik / yükseklik oranını kontrol edin ve son olarak da iyi ise, bu konturu boru olarak sayın.

İkili görüntünüzde yukarıdaki yöntemi yaptım ve 794 boru buldum :

resim açıklamasını buraya girin

(Bazı kutular kaybolursa, görüntüde daha ayrılabilir kutular elde etmek için kenar dedektörünün parametrelerini değiştirmeniz gerekir.)

ve işte kod (Bu c ++ ama kolayca python dönüştürülebilir):

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

Bu sorunu çözmek için birçok yöntem vardır, ancak bir tür ek önlemler olmadan tek bir yöntem olacağını şüpheliyim. İşte bu soruna başka bir girişim.

Kenar bilgisini kullanmak yerine, çevreleyen pikseli orta değerle karşılaştıran LBP (yerel ikili desen) benzeri bir filtre öneririm. Çevreleyen pikselin belirli bir yüzdesi merkez pikselden daha büyükse, merkez piksel 255 olarak etiketlenir. Koşul karşılanmazsa, merkez piksel 0 olarak etiketlenir.

Bu yoğunluk esaslı yöntem, boru merkezinin boru kenarlarından her zaman daha koyu olduğu varsayımıyla gerçekleştirilir. Yoğunluğu karşılaştırdığı için, bazı kontrastlar kaldığı sürece iyi çalışmalıdır.

Bu işlem sayesinde, her boru ve bazı sesler için ikili lekeler içeren bir görüntü elde edersiniz. Bunları boyut, şekil, dolgu_ oranı, renk vb. Gibi önceden bilinen bir durumla kaldırmanız gerekecektir. Koşul verilen kodda bulunabilir.

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 benzeri işlemenin sonucu resim açıklamasını buraya girin

Morfolojik işlemle temizledikten sonra resim açıklamasını buraya girin

Tüm damla adaylarını gösteren kırmızı kutular ve belirlediğimiz tüm koşulu geçen blokları gösteren sarı bölümler ile nihai sonuç. Boru demetinin altında ve üstünde bazı yanlış alarmlar vardır, ancak bazı sınır koşullarında göz ardı edilebilir. resim açıklamasını buraya girin

Bulunan toplam boru: 943


Kodu çalıştırırken bu hatayı alıyorum, blobs, _ = cv2.findContours (matblobs, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) ValueError: açmak için yeterli değer yok (beklenen 3, 2 var)
Donny

farklı bir opencv sürümü kullanıyor olmanız gerekir. Yapmanız gereken tek şey fonksiyondan almak için orijinal koddan ilk alt çizgi olan "_" yi kaldırmaktır. lekeler, _ = cv2.findContours (matblobs, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
yapws87
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.