Πρόγραμμα Σαββατοκύριακου: νοηματική γλώσσα και αναγνώριση στατικής χειρονομίας χρησιμοποιώντας scikit-learn

Ας φτιάξουμε έναν αγωγό μηχανικής μάθησης που μπορεί να διαβάσει το αλφάβητο της νοηματικής γλώσσας κοιτάζοντας απλώς μια ακατέργαστη εικόνα του χεριού ενός ατόμου.

Αυτό το πρόβλημα έχει δύο μέρη:

  1. Δημιουργία αναγνώρισης στατικής χειρονομίας, που είναι ένας ταξινομητής πολλαπλών τάξεων που προβλέπει τις στατικές χειρονομίες νοηματικής γλώσσας.
  2. Εντοπισμός του χεριού στην ακατέργαστη εικόνα και τροφοδοσία αυτού του τμήματος της εικόνας στο στατικό αναγνωριστικό χειρονομίας (ο ταξινομητής πολλαπλών τάξεων).

Μπορείτε να λάβετε τον κωδικό παραδείγματος και το σύνολο δεδομένων για αυτό το έργο εδώ

Πρώτον, κάποιο υπόβαθρο.

Η αναγνώριση χειρονομίας είναι ένα ανοιχτό πρόβλημα στον τομέα της μηχανικής όρασης, ένα πεδίο της επιστήμης των υπολογιστών που επιτρέπει στα συστήματα να μιμούνται την ανθρώπινη όραση. Η αναγνώριση χειρονομίας έχει πολλές εφαρμογές στη βελτίωση της αλληλεπίδρασης ανθρώπου-υπολογιστή και μία από αυτές είναι στον τομέα της Μετάφρασης Νοηματικής Γλώσσας, όπου μια ακολουθία βίντεο συμβολικών χειρονομιών μεταφράζεται σε φυσική γλώσσα.

Έχει αναπτυχθεί μια σειρά προηγμένων μεθόδων για το ίδιο. Εδώ, θα δούμε πώς να κάνουμε αναγνώριση στατικής χειρονομίας χρησιμοποιώντας τις βιβλιοθήκες scikit learning και scikit.

Μέρος 1: Δημιουργία αναγνώρισης στατικής χειρονομίας

Για αυτό το μέρος, χρησιμοποιούμε ένα σύνολο δεδομένων που περιλαμβάνει ακατέργαστες εικόνες και ένα αντίστοιχο αρχείο csv με συντεταγμένες που υποδεικνύουν το πλαίσιο οριοθέτησης για το χέρι σε κάθε εικόνα. (Χρησιμοποιήστε το αρχείο Dataset.zip για να λάβετε το δείγμα συνόλου δεδομένων. Εξαγωγή σύμφωνα με τις οδηγίες στο αρχείο readme)

Αυτό το σύνολο δεδομένων είναι οργανωμένο από τον χρήστη και η δομή καταλόγου του συνόλου δεδομένων έχει ως εξής. Τα ονόματα των εικόνων υποδεικνύουν το αλφάβητο που αντιπροσωπεύεται από την εικόνα.

dataset |----user_1 |---A0.jpg |---A1.jpg |---A2.jpg |---... |---Y9.jpg |----user_2 |---A0.jpg |---A1.jpg |---A2.jpg |---... |---Y9.jpg |---- ... |---- ...

Το αναγνωριστικό στατικής χειρονομίας είναι ουσιαστικά ένας ταξινομητής πολλαπλών τάξεων που εκπαιδεύεται σε εικόνες εισόδου που αντιπροσωπεύουν τις 24 στατικές χειρονομίες νοηματικής γλώσσας (AY, εξαιρουμένου του J).

Η δημιουργία ενός αναγνωριστικού στατικής χειρονομίας χρησιμοποιώντας τις αρχικές εικόνες και το αρχείο csv είναι αρκετά απλή.

Για να χρησιμοποιήσουμε τους ταξινομητές πολλαπλών κατηγοριών από τη βιβλιοθήκη scikit learning, θα χρειαστεί πρώτα να δημιουργήσουμε το σύνολο δεδομένων - δηλαδή, κάθε εικόνα πρέπει να μετατραπεί σε ένα φορέα χαρακτηριστικών (X) και κάθε εικόνα θα έχει μια ετικέτα που να αντιστοιχεί στο αλφάβητο νοηματικής γλώσσας που δηλώνει (Y).

Το κλειδί τώρα είναι να χρησιμοποιήσετε μια κατάλληλη στρατηγική για να διανύσετε την εικόνα και να εξαγάγετε σημαντικές πληροφορίες για να τροφοδοτήσετε τον ταξινομητή. Η απλή χρήση των τιμών των ακατέργαστων εικονοστοιχείων δεν θα λειτουργήσει αν σκοπεύουμε να χρησιμοποιήσουμε απλούς ταξινομητές πολλαπλών κατηγοριών (σε αντίθεση με τη χρήση δικτύων Convolution).

Για να διανυσματοποιήσουμε τις εικόνες μας, χρησιμοποιούμε την προσέγγιση Histogram of Oriented Gradients (HOG), καθώς έχει αποδειχθεί ότι έχει καλά αποτελέσματα σε προβλήματα όπως αυτό. Άλλα εργαλεία εξαγωγής που μπορούν να χρησιμοποιηθούν περιλαμβάνουν τοπικά δυαδικά μοτίβα και φίλτρα Haar.

Κώδικας:

Χρησιμοποιούμε pandas στη συνάρτηση get_data () για να φορτώσουμε το αρχείο CSV. Δύο συναρτήσεις-περικοπή ()και μετατροπήToGrayToHog ()χρησιμοποιούνται για να πάρουμε τον απαιτούμενο φορέα γουρούνι και να τον προσαρτήσουμε στη λίστα διανυσμάτων που χτίζουμε, προκειμένου να εκπαιδεύσουμε τον ταξινομητή πολλαπλών κατηγοριών.

# returns hog vector of a particular image vector def convertToGrayToHOG(imgVector): rgbImage = rgb2gray(imgVector) return hog(rgbImage) # returns cropped image def crop(img, x1, x2, y1, y2, scale): crp=img[y1:y2,x1:x2] crp=resize(crp,((scale, scale))) return crp #loads data for multiclass classification def get_data(user_list, img_dict, data_directory): X = [] Y = [] for user in user_list: user_images = glob.glob(data_directory+user+'/*.jpg') boundingbox_df = pd.read_csv(data_directory + user + '/' + user + '_loc.csv') for rows in boundingbox_df.iterrows(): cropped_img = crop( img_dict[rows[1]['image']], rows[1]['top_left_x'], rows[1]['bottom_right_x'], rows[1]['top_left_y'], rows[1]['bottom_right_y'], 128 ) hogvector = convertToGrayToHOG(cropped_img) X.append(hogvector.tolist()) Y.append(rows[1]['image'].split('/')[1][0]) return X, Y

Το επόμενο βήμα είναι να κωδικοποιήσετε τις ετικέτες εξόδου (οι τιμές Y) σε αριθμητικές τιμές. Αυτό το κάνουμε χρησιμοποιώντας τον κωδικοποιητή ετικετών του sklearn.

Στον κώδικα μας, το κάναμε ως εξής:

Y_mul = self.label_encoder.fit_transform(Y_mul)

όπου, το αντικείμενο label_encoder κατασκευάζεται ως εξής εντός του κατασκευαστή κλάσης αναγνώρισης χειρονομίας:

self.label_encoder = LabelEncoder().fit(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y'])

Μόλις γίνει αυτό, το μοντέλο μπορεί να εκπαιδευτεί χρησιμοποιώντας οποιονδήποτε αλγόριθμο ταξινόμησης πολλαπλών κατηγοριών της επιλογής σας από την εργαλειοθήκη scikit learning. Έχουμε εκπαιδεύσει τη δική μας χρησιμοποιώντας το Support Vector Classification, με γραμμικό πυρήνα.

Η εκπαίδευση ενός μοντέλου με χρήση sklearn δεν περιλαμβάνει περισσότερες από δύο γραμμές κώδικα. Δείτε πώς το κάνετε:

svcmodel = SVC(kernel='linear', C=0.9, probability=True) self.signDetector = svcmodel.fit(X_mul, Y_mul) 

Οι υπερπαραμέτρους (δηλαδή, C = 0,9 σε αυτήν την περίπτωση) μπορούν να συντονιστούν χρησιμοποιώντας μια αναζήτηση πλέγματος. Διαβάστε περισσότερα για αυτό εδώ.

Σε αυτήν την περίπτωση, δεν γνωρίζουμε πολλά για τα ίδια τα δεδομένα (δηλαδή, τα διανύσματα χοίρου). Επομένως, θα ήταν καλή ιδέα να δοκιμάσετε και να χρησιμοποιήσετε αλγόριθμους όπως xgboost (Extreme Gradient Boosting) ή Random Forest Classifiers και να δείτε πώς αποδίδουν αυτοί οι αλγόριθμοι.

Μέρος 2: Δημιουργία του Localizer

Αυτό το μέρος απαιτεί λίγο περισσότερη προσπάθεια σε σύγκριση με το πρώτο.

Σε γενικές γραμμές, θα χρησιμοποιήσουμε τα ακόλουθα βήματα για την ολοκλήρωση αυτής της εργασίας.

  1. Δημιουργήστε ένα σύνολο δεδομένων που περιλαμβάνει εικόνες χεριών και μερών που δεν είναι χειρός, χρησιμοποιώντας το δεδομένο σύνολο δεδομένων και τις τιμές πλαισίου οριοθέτησης για κάθε εικόνα.
  2. Εκπαιδεύστε έναν δυαδικό ταξινομητή για να εντοπίσετε εικόνες χειρός / όχι με το χέρι χρησιμοποιώντας το παραπάνω σύνολο δεδομένων.
  3. (Προαιρετικό) Χρησιμοποιήστε το Hard Negative Mining για να βελτιώσετε τον ταξινομητή.
  4. Χρησιμοποιήστε μια προσέγγιση συρόμενου παραθύρου με διάφορες κλίμακες , στην εικόνα ερωτήματος για να απομονώσετε την περιοχή ενδιαφέροντος.

Εδώ, δεν πρόκειται να χρησιμοποιήσουμε τεχνικές επεξεργασίας εικόνων όπως φιλτράρισμα, τμηματοποίηση χρωμάτων κ.λπ. Η βιβλιοθήκη εικόνων scikit χρησιμοποιείται για την ανάγνωση, περικοπή, κλίμακα, μετατροπή εικόνων σε γκρι κλίμακα και εξαγωγή διανυσμάτων γουρουνιών.

Δημιουργία συνόλου δεδομένων χειρός / όχι χεριού:

Το σύνολο δεδομένων θα μπορούσε να δημιουργηθεί χρησιμοποιώντας οποιαδήποτε στρατηγική θέλετε Ένας τρόπος για να το κάνετε αυτό, είναι να δημιουργήσετε τυχαίες συντεταγμένες και, στη συνέχεια, να ελέγξετε την αναλογία της περιοχής διασταύρωσης προς την περιοχή της ένωσης (δηλαδή, ο βαθμός επικάλυψης με το δεδομένο πλαίσιο οριοθέτησης) για να προσδιορίσετε εάν πρόκειται για ένα τμήμα εκτός χεριού. (Μια άλλη προσέγγιση θα μπορούσε να είναι η χρήση ενός συρόμενου παραθύρου για τον προσδιορισμό των συντεταγμένων. Αλλά αυτό είναι τρομερά αργό και περιττό)

""" This function randomly generates bounding boxes Returns hog vector of those cropped bounding boxes along with label Label : 1 if hand ,0 otherwise """ def buildhandnothand_lis(frame,imgset): poslis =[] neglis =[] for nameimg in frame.image: tupl = frame[frame['image']==nameimg].values[0] x_tl = tupl[1] y_tl = tupl[2] side = tupl[5] conf = 0 dic = [0, 0] arg1 = [x_tl,y_tl,conf,side,side] poslis.append( convertToGrayToHOG(crop(imgset[nameimg], x_tl,x_tl+side,y_tl,y_tl+side))) while dic[0] <= 1 or dic[1] < 1: x = random.randint(0,320-side) y = random.randint(0,240-side) crp = crop(imgset[nameimg],x,x+side,y,y+side) hogv = convertToGrayToHOG(crp) arg2 = [x,y, conf, side, side] z = overlapping_area(arg1,arg2) if dic[0] <= 1 and z <= 0.5: neglis.append(hogv) dic[0] += 1 if dic[0]== 1: break label_1 = [1 for i in range(0,len(poslis)) ] label_0 = [0 for i in range(0,len(neglis))] label_1.extend(label_0) poslis.extend(neglis) return poslis,label_1

Εκπαίδευση δυαδικού ταξινομητή:

Once the data set is ready, training the classifier can be done exactly as seen before in part 1.

Usually, in this case, a technique called Hard Negative Mining is employed to reduce the number of false positive detections and improve the classifier. One or two iterations of hard negative mining using a Random Forest Classifier, is enough to ensure that your classifier reaches acceptable classification accuracies, which in this case is anything above 80%.

Take a look at the code here for a sample implementation of the same.

Detecting hands in test images:

Now, to actually use the above classifier, we scale the test image by various factors and then use a sliding window approach on all of them to pick the window which captures the region of interest perfectly. This is done by selecting the region corresponding to the max of the confidence scores allotted by the binary (hand/not-hand) classifier across all scales.

The test images need to be scaled because, we run a set sized window (in our case, it is 128x128) across all images to pick the region of interest and it is possible that the region of interest does not fit perfectly into this window size.

Sample implementation and overall detection across all scales.

Putting it all together

After both parts are complete, all that’s left to do is to call them in succession to get the final output when provided with a test image.

That is, given a test image, we first get the various detected regions across different scales of the image and pick the best one among them. This region is then cropped out, rescaled (to 128x128) and its corresponding hog vector is fed to the multi-class classifier (i.e., the gesture recognizer). The gesture recognizer then predicts the gesture denoted by the hand in the image.

Key points

To summarize, this project involves the following steps. The links refer to the relevant code in the github repository.

  1. Building the hand/not-hand dataset.
  2. Converting all the images i.e., cropped sections with the gestures and the hand, not-hand images, to its vectorized form.
  3. Building a binary classifier for detecting the section with the hand and building a multi-class classifier for identifying the gesture using these data sets.
  4. Χρησιμοποιώντας τους παραπάνω ταξινομητές ο ένας μετά τον άλλο για την εκτέλεση της απαιτούμενης εργασίας.

Οι Suks και εγώ εργαστήκαμε σε αυτό το έργο ως μέρος του μαθήματος Machine Learning που παρακολουθήσαμε στο κολέγιο. Μια μεγάλη φωνή σε αυτήν για όλες τις συνεισφορές της!

Επίσης, θέλαμε να αναφέρουμε το Pyimagesearch, το οποίο είναι ένα υπέροχο blog που χρησιμοποιήσαμε εκτενώς ενώ εργαζόμασταν στο έργο! Ελέγξτε το για περιεχόμενο σχετικά με την επεξεργασία εικόνων και το σχετικό με το OpenCV περιεχόμενο.

Στην υγειά σας!