छवि में कई आयतों का पता लगाएं


13

मैं इस तस्वीर में पाइपों की गिनती का पता लगाने की कोशिश कर रहा हूं। इसके लिए, मैं OpenCV और पायथन-आधारित पहचान का उपयोग कर रहा हूं। इसी तरह के सवालों के मौजूदा जवाब के आधार पर, मैं निम्नलिखित चरणों के साथ आने में सक्षम था

  1. छवि खोलें
  2. इसे छान लें
  3. एज डिटेक्शन लागू करें
  4. Contours का उपयोग करें
  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 संस्करण के साथ सवाल अपडेट किया है
डोनी

क्या आपके पास एक जमीनी सच्चाई है कि कितने पाइपों का पता लगाया जाना चाहिए?
TA

एक चीज जिसे आप आजमा सकते हैं, वह गायब डिटेल्स को सुधारने के लिए थ्रेसहोल्डिंग स्टेप को ट्यून करने की हो सकती है। ओत्सु की थ्रेशोल्डिंग या अनुकूली थ्रॉल्डिंग में देखें। हालांकि, आपका वर्तमान समाधान संभवतः सबसे अच्छा है जो आप पारंपरिक छवि प्रसंस्करण तकनीकों का उपयोग करने वाले हैं। अन्यथा आप डीप / मशीन लर्निंग में देख सकते हैं
नथनसी

जवाबों:


6

बेशक आप उन्हें उनके क्षेत्र के अनुसार फ़िल्टर कर सकते हैं। मैंने आपकी बाइनरी इमेज ली और नीचे दिए अनुसार काम जारी रखा:

1-आप सभी कांटों पर एक लूप करें जो आपको findContours से मिला है

2- लूप चेक में अगर प्रत्येक समोच्च, एक आंतरिक समोच्च है या नहीं

3 - उन लोगों से जो आंतरिक रूप से भिन्न हैं, उनके क्षेत्र की जाँच करें और यदि क्षेत्र स्वीकार्य सीमा में है, तो प्रत्येक समोच्च की चौड़ाई / ऊँचाई के अनुपात की जाँच करें और अंत में यदि यह बहुत अच्छा है, तो उस समोच्च को एक पाइप के रूप में गिनें।

मैंने आपकी बाइनरी छवि पर उपरोक्त विधि की, और 794 पाइप पाए :

यहां छवि विवरण दर्ज करें

(हालांकि कुछ बॉक्स खो गए हैं, आपको छवि में अधिक वियोज्य बॉक्स प्राप्त करने के लिए एज डिटेक्टर के मापदंडों को बदलना चाहिए।)

और यहाँ कोड है (यह c ++ है लेकिन आसानी से अजगर के लिए परिवर्तनीय है):

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 जैसी प्रक्रिया से परिणाम यहां छवि विवरण दर्ज करें

आकृति विज्ञान प्रक्रिया के साथ सफाई के बाद यहां छवि विवरण दर्ज करें

अंतिम परिणाम लाल बक्से के साथ सभी बूँद उम्मीदवारों और पीले खंडों को दिखाते हुए दिखाई देते हैं जो हमारे द्वारा निर्धारित सभी शर्त को पार करते हैं। पाइप बंडल के नीचे और ऊपर कुछ झूठे अलार्म हैं लेकिन उन्हें कुछ सीमा स्थितियों के साथ छोड़ा जा सकता है। यहां छवि विवरण दर्ज करें

कुल पाइप मिला: 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.