Vikend projekt: jezik znakova i prepoznavanje statičnih gesta pomoću scikit-learn

Izgradimo cjevovod za strojno učenje koji može čitati abecedu znakovnog jezika samo gledajući sirovu sliku čovjekove ruke.

Ovaj problem ima dva dijela:

  1. Izgradnja prepoznavača statičkih gesta, koji je višerazredni klasifikator koji predviđa statičke geste znakovnog jezika.
  2. Pronalaženje ruke na sirovoj slici i dodavanje ovog dijela slike u prepoznavač statične geste (višerazredni klasifikator).

Ovdje možete dobiti moj primjer koda i skupa podataka za ovaj projekt.

Prvo, malo pozadine.

Prepoznavanje gesta otvoren je problem u području strojnog vida, polju računalnih znanosti koje omogućuje sustavima oponašanje ljudskog vida. Prepoznavanje gesta ima mnogo primjena u poboljšanju interakcije čovjeka i računala, a jedan od njih je na polju Prevođenje znakovnog jezika, gdje je video slijed simboličnih gesta preveden na prirodni jezik.

Za iste je razvijen niz naprednih metoda. Ovdje ćemo pogledati kako izvršiti prepoznavanje statičnih gesta pomoću biblioteka scikit learn i scikit slika.

1. dio: Izrada prepoznavača statičkih gesta

Za ovaj dio koristimo skup podataka koji sadrži sirove slike i odgovarajuću csv datoteku s koordinatama koje označavaju granični okvir za ruku na svakoj slici. (Upotrijebite datoteku Dataset.zip za dobivanje uzorka skupa podataka. Izdvojite prema uputama u datoteci readme)

Ovaj je skup podataka organiziran prema korisniku, a struktura direktorija skupa podataka je sljedeća. Imena slika označavaju abecedu prikazanu slikom.

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

Prepoznavač statičke geste u osnovi je klasifikator više klasa koji se uvježbava na ulaznim slikama koje predstavljaju 24 statičke geste znakovnog jezika (AY, isključujući J).

Izgradnja prepoznavača statičkih gesta pomoću sirovih slika i csv datoteke prilično je jednostavna.

Da bismo koristili višerazredne klasifikatore iz scikit learn biblioteke, prvo ćemo morati izgraditi skup podataka - to jest, svaka slika mora se pretvoriti u vektor značajke (X) i svaka će slika imati oznaku koja odgovara abeceda znakovnog jezika koju označava (Y).

Ključno je sada koristiti prikladnu strategiju za vektorizaciju slike i izdvajanje značajnih informacija za unos u klasifikator. Jednostavna upotreba neobrađenih vrijednosti piksela neće raditi ako planiramo koristiti jednostavne klasifikatore s više klasa (za razliku od upotrebe Convolution Networks).

Za vektorizaciju naših slika koristimo pristup histograma orijentiranih gradijenata (HOG), jer je dokazano da daje dobre rezultate kod problema poput ovog. Ostali ekstraktori značajki koji se mogu koristiti uključuju lokalne binarne uzorke i Haar filtere.

Kodirati:

Za učitavanje CSV datoteke koristimo pande u funkciji get_data (). Dvije funkcije-obrezivanje ()i convertToGrayToHog ()koriste se za dobivanje potrebnog svinjskog vektora i njegovo dodavanje na popis vektora koje gradimo, kako bi se uvježbao višerazredni klasifikator.

# 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

Sljedeći je korak kodiranje izlaznih oznaka (Y-vrijednosti) u numeričke vrijednosti. To radimo pomoću sklearnovog kodera naljepnica.

U našem kodu to smo učinili kako slijedi:

Y_mul = self.label_encoder.fit_transform(Y_mul)

gdje je objekt label_encoder konstruiran na sljedeći način unutar konstruktora klase prepoznavača gesta:

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'])

Nakon što se to učini, model se može uvježbati pomoću bilo kojeg višerazrednog algoritma za klasifikaciju po vašem izboru iz scikit learn alata. Naše smo obučili koristeći klasifikaciju vektora podrške, s linearnim jezgrom.

Obuka modela pomoću sklearna ne uključuje više od dva retka koda. Evo kako to možete učiniti:

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

Hiperparametri (tj. C = 0,9 u ovom slučaju) mogu se podesiti pomoću pretraživanja mreže. Pročitajte više o ovome ovdje.

U ovom slučaju ne znamo puno podataka o podacima kao takvima (tj. Svinjskim vektorima). Stoga bi bilo dobro isprobati i koristiti algoritme poput xgboost (Extreme Gradient Boosting) ili Random Forest Classifiers i vidjeti kako ti algoritmi rade.

Dio 2: Izgradnja lokalizatora

Ovaj dio zahtijeva malo više napora u odnosu na prvi.

Općenito, pri izvršavanju ovog zadatka upotrijebit ćemo sljedeće korake.

  1. Izgradite skup podataka koji sadrži slike ruku i dijelova koji nisu ručni, koristeći zadani skup podataka i vrijednosti graničnog okvira za svaku sliku.
  2. Obučite binarni klasifikator za otkrivanje slika ruku ili ruku kojima se koristi gornji skup podataka.
  3. (Izborno) Koristite Hard Negative Mining za poboljšanje klasifikatora.
  4. Upotrijebite pristup kliznim prozorima s različitim mjerilima na slici upita da biste izolirali područje od interesa.

Ovdje nećemo koristiti nikakve tehnike obrade slika poput filtriranja, segmentacije boja itd. Scikit biblioteka slika koristi se za čitanje, obrezivanje, skaliranje, pretvaranje slika u sivu ljestvicu i izdvajanje svinjskih vektora.

Izrada skupa podataka "ruka / ruka":

Skup podataka mogao bi se izraditi pomoću bilo koje strategije koja vam se sviđa. Jedan od načina za to je generiranje slučajnih koordinata, a zatim provjeravanje omjera područja presijecanja i područja spajanja (tj. Stupnja preklapanja s danim graničnim okvirom) kako bi se utvrdilo je li to ručni presjek. (Drugi bi pristup mogao biti korištenje kliznog prozora za određivanje koordinata. Ali ovo je užasno sporo i nepotrebno)

""" 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

Obuka binarnog klasifikatora:

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. Using the above classifiers one after the other to perform the required task.

Suks and I worked on this project as part of the Machine Learning course that we took up in college. A big shout out to her for all her contributions!

Also, we wanted to mention Pyimagesearch, which is a wonderful blog that we used extensively while we were working on the project! Do check it out for content on image processing and opencv related content.

Cheers!