मैं अपने पंजा का पता कैसे लगा सकता हूं?


198

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

आप देखते हैं, मेरी अवधारणा का प्रमाण मूल रूप से समय के साथ प्रत्येक संवेदक का अधिकतम दबाव लेता था और प्रत्येक पंक्ति के योग की तलाश शुरू कर देता था, जब तक कि यह उस पर नहीं लगता! = 0.0। फिर यह स्तंभों के लिए भी ऐसा ही करता है और जैसे ही यह 2 से अधिक पंक्तियों को पाता है फिर से शून्य होता है। यह कुछ सूचकांक में न्यूनतम और अधिकतम पंक्ति और स्तंभ मान संग्रहीत करता है।

वैकल्पिक शब्द

जैसा कि आप आंकड़े में देख सकते हैं, यह ज्यादातर मामलों में काफी अच्छी तरह से काम करता है। हालाँकि, इस दृष्टिकोण के बहुत सारे पतन हैं (बहुत आदिम होने के अलावा):

  • मनुष्य के 'खोखले पैर' हो सकते हैं, जिसका अर्थ है कि पदचिह्न के भीतर कई खाली पंक्तियाँ हैं। चूंकि मुझे डर था कि यह (बड़े) कुत्तों के साथ भी हो सकता है, मैंने पंजा काटने से पहले कम से कम 2 या 3 खाली पंक्तियों का इंतजार किया।

    यह एक समस्या पैदा करता है यदि कई खाली पंक्तियों तक पहुँचने से पहले एक अलग कॉलम में किया गया दूसरा संपर्क, इस प्रकार क्षेत्र का विस्तार करता है। मुझे लगता है कि मैं स्तंभों की तुलना कर सकता हूं और देख सकता हूं कि क्या वे एक निश्चित मूल्य से अधिक हैं, उन्हें अलग-अलग पंजे होना चाहिए।

  • समस्या तब और बदतर हो जाती है जब कुत्ता बहुत छोटा होता है या अधिक गति से चलता है। क्या होता है कि सामने वाले पंजे के पंजे अभी भी संपर्क बना रहे हैं, जबकि हिंद पंजा के पैर की उंगलियों को सामने वाले पंजे के समान क्षेत्र में संपर्क बनाने की शुरुआत होती है!

    मेरी सरल स्क्रिप्ट के साथ, यह इन दोनों को विभाजित करने में सक्षम नहीं होगा, क्योंकि यह निर्धारित करना होगा कि उस क्षेत्र के कौन से फ्रेम किस पंजा से संबंधित हैं, जबकि वर्तमान में मुझे केवल सभी फ़्रेमों पर अधिकतम मान देखना होगा।

इसके गलत होने के उदाहरण कहाँ हैं:

वैकल्पिक शब्द वैकल्पिक शब्द

इसलिए अब मैं पंजे को पहचानने और अलग करने का एक बेहतर तरीका ढूंढ रहा हूं (जिसके बाद मुझे यह तय करने की समस्या हो जाएगी कि यह कौन सा पंजा है!)।

अपडेट करें:

मैं जो (भयानक!) उत्तर को लागू करने के लिए छेड़छाड़ कर रहा हूं, लेकिन मुझे अपनी फाइलों से वास्तविक पंजा डेटा निकालने में कठिनाई हो रही है।

वैकल्पिक शब्द

कोडेड_पॉव मुझे सभी अलग-अलग पंजे दिखाता है, जब अधिकतम दबाव छवि पर लागू होता है (ऊपर देखें)। हालांकि, समाधान प्रत्येक फ्रेम (ओवरलैपिंग पंजे को अलग करने के लिए) पर जाता है और चार आयत विशेषताओं को सेट करता है, जैसे निर्देशांक या ऊंचाई / चौड़ाई।

मैं यह पता नहीं लगा सकता कि इन विशेषताओं को कैसे लिया जाए और उन्हें कुछ चर में संग्रहीत किया जाए जो मैं माप डेटा पर लागू कर सकता हूं। चूँकि मुझे प्रत्येक पंजा के लिए यह जानना आवश्यक है कि इसका स्थान किस फ्रेम के दौरान है और इसे किस पंजे में जोड़ा जाता है (सामने / हिंद, बाएँ / दाएँ)।

तो मैं प्रत्येक पंजे के लिए इन मूल्यों को निकालने के लिए रेक्टेंगल्स विशेषताओं का उपयोग कैसे कर सकता हूं?

मेरे पास मेरे सार्वजनिक ड्रॉपबॉक्स फ़ोल्डर में प्रश्न सेटअप में उपयोग किए गए माप हैं ( उदाहरण 1 , उदाहरण 2 , उदाहरण 3 )। किसी की दिलचस्पी के लिए, मैंने आपको आज तक बनाए रखने के लिए एक ब्लॉग भी बनाया है :-)


लगता है जैसे आपको पंक्ति / कॉलम एल्गोरिदम से दूर करना होगा, आप उपयोगी जानकारी सीमित कर रहे हैं।
तमारा वाइज्समैन

12
वाह! बिल्ली नियंत्रण सॉफ्टवेयर?
एलएक्सएक्स

यह डॉग डेटा वास्तव में @alxx है ;-) लेकिन हाँ, यह उनका निदान करने के लिए उपयोग किया जाएगा!
३० पर

4
क्यों? (कोई बात नहीं, यह जानकर ज्यादा मजा नहीं आता ...)
बेन रेगेंस्पैन

जवाबों:


358

यदि आप सिर्फ (अर्ध) सन्निहित क्षेत्र चाहते हैं, तो पहले से ही पायथन में एक आसान कार्यान्वयन है: SciPy का ndimage.morphology मॉड्यूल। यह एक काफी सामान्य छवि आकृति विज्ञान ऑपरेशन है।


मूल रूप से, आपके पास 5 चरण हैं:

def find_paws(data, smooth_radius=5, threshold=0.0001):
    data = sp.ndimage.uniform_filter(data, smooth_radius)
    thresh = data > threshold
    filled = sp.ndimage.morphology.binary_fill_holes(thresh)
    coded_paws, num_paws = sp.ndimage.label(filled)
    data_slices = sp.ndimage.find_objects(coded_paws)
    return object_slices
  1. यह सुनिश्चित करने के लिए इनपुट डेटा को थोड़ा धुंधला करें कि पंजे एक निरंतर पदचिह्न हैं। (यह सिर्फ एक बड़े कर्नेल ( structureविभिन्न scipy.ndimage.morphologyकार्यों के लिए kwarg ) का उपयोग करने के लिए अधिक कुशल होगा लेकिन यह किसी कारण के लिए ठीक से काम नहीं कर रहा है ...)

  2. सरणी को थ्रेसहोल्ड करें ताकि आपके पास उन स्थानों की बूलियन सरणी हो जहां दबाव कुछ सीमा मूल्य से अधिक है (यानी thresh = data > value)

  3. किसी भी आंतरिक छेद को भरें, ताकि आपके पास क्लीनर क्षेत्र हों ( filled = sp.ndimage.morphology.binary_fill_holes(thresh))

  4. अलग-अलग सन्निहित क्षेत्र खोजें ( coded_paws, num_paws = sp.ndimage.label(filled))। यह संख्या द्वारा कोडित क्षेत्रों के साथ एक सरणी देता है (प्रत्येक क्षेत्र एक अद्वितीय पूर्णांक (पंजे की संख्या तक 1) हर जगह शून्य के साथ एक सन्निहित क्षेत्र है)।

  5. का उपयोग कर सन्निहित क्षेत्रों को अलग करें data_slices = sp.ndimage.find_objects(coded_paws)। यह sliceवस्तुओं के tuples की एक सूची देता है , जिससे आप प्रत्येक पंजा के लिए डेटा का क्षेत्र प्राप्त कर सकते हैं [data[x] for x in data_slices]। इसके बजाय, हम इन स्लाइसों के आधार पर एक आयत तैयार करेंगे, जो थोड़ा और काम करती है।


नीचे दिए गए दो एनिमेशन आपके "ओवरलैपिंग पंजे" और "ग्रुपेड पंजे" उदाहरण डेटा दिखाते हैं। यह तरीका पूरी तरह से काम करता है। (और जो कुछ भी इसके लायक है, यह मेरी मशीन पर नीचे दी गई GIF छवियों की तुलना में बहुत आसानी से चलता है, इसलिए पंजा पहचान एल्गोरिथ्म काफी तेज है ...)

ओवरलैपिंग पंजे समूहबद्ध पंजे


यहाँ एक पूर्ण उदाहरण है (अब अधिक विस्तृत विवरण के साथ)। इसका अधिकांश हिस्सा इनपुट पढ़ रहा है और एक एनीमेशन बना रहा है। वास्तविक पंजे की पहचान कोड की केवल 5 लाइनें हैं।

import numpy as np
import scipy as sp
import scipy.ndimage

import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

def animate(input_filename):
    """Detects paws and animates the position and raw data of each frame
    in the input file"""
    # With matplotlib, it's much, much faster to just update the properties
    # of a display object than it is to create a new one, so we'll just update
    # the data and position of the same objects throughout this animation...

    infile = paw_file(input_filename)

    # Since we're making an animation with matplotlib, we need 
    # ion() instead of show()...
    plt.ion()
    fig = plt.figure()
    ax = fig.add_subplot(111)
    fig.suptitle(input_filename)

    # Make an image based on the first frame that we'll update later
    # (The first frame is never actually displayed)
    im = ax.imshow(infile.next()[1])

    # Make 4 rectangles that we can later move to the position of each paw
    rects = [Rectangle((0,0), 1,1, fc='none', ec='red') for i in range(4)]
    [ax.add_patch(rect) for rect in rects]

    title = ax.set_title('Time 0.0 ms')

    # Process and display each frame
    for time, frame in infile:
        paw_slices = find_paws(frame)

        # Hide any rectangles that might be visible
        [rect.set_visible(False) for rect in rects]

        # Set the position and size of a rectangle for each paw and display it
        for slice, rect in zip(paw_slices, rects):
            dy, dx = slice
            rect.set_xy((dx.start, dy.start))
            rect.set_width(dx.stop - dx.start + 1)
            rect.set_height(dy.stop - dy.start + 1)
            rect.set_visible(True)

        # Update the image data and title of the plot
        title.set_text('Time %0.2f ms' % time)
        im.set_data(frame)
        im.set_clim([frame.min(), frame.max()])
        fig.canvas.draw()

def find_paws(data, smooth_radius=5, threshold=0.0001):
    """Detects and isolates contiguous regions in the input array"""
    # Blur the input data a bit so the paws have a continous footprint 
    data = sp.ndimage.uniform_filter(data, smooth_radius)
    # Threshold the blurred data (this needs to be a bit > 0 due to the blur)
    thresh = data > threshold
    # Fill any interior holes in the paws to get cleaner regions...
    filled = sp.ndimage.morphology.binary_fill_holes(thresh)
    # Label each contiguous paw
    coded_paws, num_paws = sp.ndimage.label(filled)
    # Isolate the extent of each paw
    data_slices = sp.ndimage.find_objects(coded_paws)
    return data_slices

def paw_file(filename):
    """Returns a iterator that yields the time and data in each frame
    The infile is an ascii file of timesteps formatted similar to this:

    Frame 0 (0.00 ms)
    0.0 0.0 0.0
    0.0 0.0 0.0

    Frame 1 (0.53 ms)
    0.0 0.0 0.0
    0.0 0.0 0.0
    ...
    """
    with open(filename) as infile:
        while True:
            try:
                time, data = read_frame(infile)
                yield time, data
            except StopIteration:
                break

def read_frame(infile):
    """Reads a frame from the infile."""
    frame_header = infile.next().strip().split()
    time = float(frame_header[-2][1:])
    data = []
    while True:
        line = infile.next().strip().split()
        if line == []:
            break
        data.append(line)
    return time, np.array(data, dtype=np.float)

if __name__ == '__main__':
    animate('Overlapping paws.bin')
    animate('Grouped up paws.bin')
    animate('Normal measurement.bin')

अपडेट: जहां तक ​​यह पहचानना है कि कौन सा पंजा किस समय संवेदक के संपर्क में है, इसका सबसे सरल उपाय यही विश्लेषण करना है, लेकिन एक ही बार में सभी डेटा का उपयोग करें। (यानी इनपुट को 3 डी ऐरे में स्टैक करें, और इसके साथ काम करें, व्यक्तिगत समय सीमा के बजाय।) क्योंकि SciPy के ndimage फ़ंक्शन एन-डायमेंशनल सरणियों के साथ काम करने के लिए हैं, हमें मूल पंजा-खोज फ़ंक्शन को संशोधित करने की आवश्यकता नहीं है। बिल्कुल भी।

# This uses functions (and imports) in the previous code example!!
def paw_regions(infile):
    # Read in and stack all data together into a 3D array
    data, time = [], []
    for t, frame in paw_file(infile):
        time.append(t)
        data.append(frame)
    data = np.dstack(data)
    time = np.asarray(time)

    # Find and label the paw impacts
    data_slices, coded_paws = find_paws(data, smooth_radius=4)

    # Sort by time of initial paw impact... This way we can determine which
    # paws are which relative to the first paw with a simple modulo 4.
    # (Assuming a 4-legged dog, where all 4 paws contacted the sensor)
    data_slices.sort(key=lambda dat_slice: dat_slice[2].start)

    # Plot up a simple analysis
    fig = plt.figure()
    ax1 = fig.add_subplot(2,1,1)
    annotate_paw_prints(time, data, data_slices, ax=ax1)
    ax2 = fig.add_subplot(2,1,2)
    plot_paw_impacts(time, data_slices, ax=ax2)
    fig.suptitle(infile)

def plot_paw_impacts(time, data_slices, ax=None):
    if ax is None:
        ax = plt.gca()

    # Group impacts by paw...
    for i, dat_slice in enumerate(data_slices):
        dx, dy, dt = dat_slice
        paw = i%4 + 1
        # Draw a bar over the time interval where each paw is in contact
        ax.barh(bottom=paw, width=time[dt].ptp(), height=0.2, 
                left=time[dt].min(), align='center', color='red')
    ax.set_yticks(range(1, 5))
    ax.set_yticklabels(['Paw 1', 'Paw 2', 'Paw 3', 'Paw 4'])
    ax.set_xlabel('Time (ms) Since Beginning of Experiment')
    ax.yaxis.grid(True)
    ax.set_title('Periods of Paw Contact')

def annotate_paw_prints(time, data, data_slices, ax=None):
    if ax is None:
        ax = plt.gca()

    # Display all paw impacts (sum over time)
    ax.imshow(data.sum(axis=2).T)

    # Annotate each impact with which paw it is
    # (Relative to the first paw to hit the sensor)
    x, y = [], []
    for i, region in enumerate(data_slices):
        dx, dy, dz = region
        # Get x,y center of slice...
        x0 = 0.5 * (dx.start + dx.stop)
        y0 = 0.5 * (dy.start + dy.stop)
        x.append(x0); y.append(y0)

        # Annotate the paw impacts         
        ax.annotate('Paw %i' % (i%4 +1), (x0, y0),  
            color='red', ha='center', va='bottom')

    # Plot line connecting paw impacts
    ax.plot(x,y, '-wo')
    ax.axis('image')
    ax.set_title('Order of Steps')

वैकल्पिक शब्द


वैकल्पिक शब्द


वैकल्पिक शब्द


82
मैं यह भी बताना शुरू नहीं कर सकता कि आप कितना भयानक जवाब दे रहे हैं!
इवो ​​फ्लिप्से

1
@ इवो: हाँ, मुझे जो कुछ और भी बहुत अच्छा लगेगा :), लेकिन क्या मुझे एक नया सवाल शुरू करना चाहिए, या शायद @Joe, अगर आप यहां जवाब देंगे? stackoverflow.com/questions/2546780/…
unutbu

2
मैं वास्तव में सिर्फ .png बाहर फेंक दिया, और एक किया convert *.png output.gif। मैंने निश्चित रूप से इमेजमाजिक से पहले अपने मशीन को अपने घुटनों पर लाया था, हालांकि इस उदाहरण के लिए यह ठीक काम करता था। अतीत में, मैंने इस स्क्रिप्ट का उपयोग किया है: svn.effbot.python-hosting.com/pil/Scripts/gifmaker.py , व्यक्तिगत फ्रेम को सहेजे बिना सीधे अजगर से एक एनिमेटेड जिफ लिखने के लिए। उम्मीद है की वो मदद करदे! मैं @unutbu उल्लेख किए गए प्रश्न पर एक उदाहरण पोस्ट करूंगा।
जो किंगटन

1
जानकारी के लिए धन्यवाद, @ जो। मेरी समस्या का एक हिस्सा उपयोग करने के लिए की उपेक्षा की गई थी bbox_inches='tight'में plt.savefig, अन्य था अधीरता :)
unutbu

4
पवित्र गाय, मुझे सिर्फ वाह कहना है कि यह उत्तर कितना महान है।
andersoj

4

मैं छवि का पता लगाने में कोई विशेषज्ञ नहीं हूं, और मैं पायथन को नहीं जानता, लेकिन मैं इसे एक अजीब ...

अलग-अलग पंजे का पता लगाने के लिए, आपको सबसे पहले केवल कुछ छोटे दहलीज से अधिक दबाव के साथ सब कुछ का चयन करना चाहिए, बिल्कुल भी दबाव नहीं। हर पिक्सेल / बिंदु जो इसके ऊपर है, उसे "चिह्नित" किया जाना चाहिए। फिर, सभी "चिह्नित" पिक्सल से सटे प्रत्येक पिक्सेल चिह्नित हो जाता है, और यह प्रक्रिया कुछ समय दोहराई जाती है। पूरी तरह से जुड़े हुए द्रव्यमान का निर्माण होगा, इसलिए आपके पास अलग-अलग वस्तुएं हैं। फिर, प्रत्येक "ऑब्जेक्ट" में एक न्यूनतम और अधिकतम x और y मान होता है, इसलिए बाउंडिंग बॉक्स को उनके चारों ओर बड़े करीने से पैक किया जा सकता है।

स्यूडोकोड:

(MARK) ALL PIXELS ABOVE (0.5)

(MARK) ALL PIXELS (ADJACENT) TO (MARK) PIXELS

REPEAT (STEP 2) (5) TIMES

SEPARATE EACH TOTALLY CONNECTED MASS INTO A SINGLE OBJECT

MARK THE EDGES OF EACH OBJECT, AND CUT APART TO FORM SLICES.

इसके बारे में ऐसा करना चाहिए।


0

नोट: मैं पिक्सेल कहता हूं, लेकिन यह पिक्सल के औसत का उपयोग करने वाले क्षेत्र हो सकते हैं। अनुकूलन एक और मुद्दा है ...

लगता है कि आपको प्रत्येक पिक्सेल के लिए एक फ़ंक्शन (समय के साथ दबाव) का विश्लेषण करने की आवश्यकता है और यह निर्धारित करें कि फ़ंक्शन कहाँ बदल जाता है (जब यह दूसरी दिशा में> एक्स बदलता है तो इसे काउंटर त्रुटियों के लिए बारी माना जाता है)।

यदि आप जानते हैं कि यह किस फ्रेम में बदल जाता है, तो आप उस फ्रेम को जान पाएंगे जहां दबाव सबसे कठिन था और आपको पता चल जाएगा कि दो पंजे के बीच यह सबसे कम कहां था। सिद्धांत रूप में, आप तब उन दो फ़्रेमों को जानते होंगे, जहाँ पंजे सबसे अधिक कठोर होते हैं और उन अंतरालों की औसत गणना कर सकते हैं।

जिसके बाद मुझे यह तय करने में समस्या होगी कि यह कौन सा पंजा है!

यह पहले की तरह ही दौरा है, यह जानकर कि प्रत्येक पंजा सबसे अधिक दबाव लागू करता है जो आपको तय करने में मदद करता है।

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