주말 프로젝트 : scikit-learn을 사용한 수화 및 정적 제스처 인식

사람 손의 생생한 이미지를 보는 것만으로 수화 알파벳을 읽을 수있는 기계 학습 파이프 라인을 구축해 보겠습니다.

이 문제에는 두 부분이 있습니다.

  1. 정적 수화 제스처를 예측하는 다중 클래스 분류 기인 정적 제스처 인식기를 빌드합니다.
  2. 원시 이미지에서 손을 찾고 이미지의이 섹션을 정적 제스처 인식기 (다중 클래스 분류기)에 제공합니다.

여기에서이 프로젝트의 예제 코드와 데이터 세트를 얻을 수 있습니다.

첫째, 배경입니다.

제스처 인식은 시스템이 인간의 비전을 모방 할 수있게 해주는 컴퓨터 과학 분야 인 머신 비전 영역에서 열린 문제입니다. 제스처 인식은 인간-컴퓨터 상호 작용을 개선하는 데 많은 응용 프로그램을 가지고 있으며 그중 하나는 상징적 인 손 제스처의 비디오 시퀀스가 ​​자연어로 번역되는 수화 번역 분야입니다.

이를위한 다양한 고급 방법이 개발되었습니다. 여기에서는 scikit learn 및 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 개의 정적 수화 제스처 (J 제외 AY)를 나타내는 입력 이미지에 대해 훈련 된 다중 클래스 분류기입니다.

원시 이미지와 csv 파일을 사용하여 정적 제스처 인식기를 구축하는 것은 매우 간단합니다.

scikit learn 라이브러리의 다중 클래스 분류기를 사용하려면 먼저 데이터 세트를 구축해야합니다. 즉, 모든 이미지를 특성 벡터 (X)로 변환해야하며 모든 이미지에는 (Y)를 나타내는 수화 알파벳.

이제 핵심은 적절한 전략을 사용하여 이미지를 벡터화하고 분류기에 제공 할 의미있는 정보를 추출하는 것입니다. 단순 다중 클래스 분류기를 사용하려는 경우 원시 픽셀 값을 사용하는 것만으로는 작동하지 않습니다 (컨볼 루션 네트워크를 사용하는 것과 반대).

이미지를 벡터화하기 위해 Histogram of Oriented Gradients (HOG) 접근 방식을 사용합니다. 이는 이와 같은 문제에 대해 좋은 결과를 산출하는 것으로 입증되었습니다. 사용할 수있는 다른 기능 추출기는 로컬 이진 패턴 및 Haar 필터를 포함합니다.

암호:

get_data () 함수에서 pandas를 사용하여 CSV 파일을로드합니다. 두 함수 -crop ()및 convertToGrayToHog ()다중 클래스 분류기를 훈련하기 위해 필요한 돼지 벡터를 가져 와서 우리가 만들고있는 벡터 목록에 추가하는 데 사용됩니다.

# 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 learn 도구 상자에서 선택한 다중 클래스 분류 알고리즘을 사용하여 모델을 학습시킬 수 있습니다. 우리는 선형 커널과 함께 지원 벡터 분류를 사용하여 훈련했습니다.

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 부 : 로컬 라이저 빌드

이 부분은 첫 번째 부분에 비해 약간 더 많은 노력이 필요합니다.

일반적으로이 작업을 완료하는 데 다음 단계를 사용합니다.

  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

이진 분류기 훈련 :

데이터 세트가 준비되면 1 부에서 본 것과 똑같이 분류기 훈련을 수행 할 수 있습니다.

일반적으로이 경우 하드 네거티브 마이닝이라는 기술을 사용하여 오 탐지 수를 줄이고 분류기를 개선합니다. Random Forest Classifier를 사용하여 하드 네거티브 마이닝을 한두 번 반복하면 분류자가 허용 가능한 분류 정확도 (이 경우 80 % 이상)에 도달 할 수 있습니다.

동일한 샘플 구현을 위해 여기에서 코드를 살펴보십시오.

테스트 이미지에서 손 감지 :

이제 위의 분류기를 실제로 사용하기 위해 다양한 요인에 따라 테스트 이미지의 크기를 조정 한 다음 모든 요인에 대해 슬라이딩 창 접근 방식을 사용하여 관심 영역을 완벽하게 캡처하는 창을 선택합니다. 이는 모든 척도에서 이진 (손 / 손이 아닌) 분류기에 의해 할당 된 신뢰도 점수의 최대에 해당하는 영역을 선택하여 수행됩니다.

테스트 이미지의 크기를 조정해야합니다. 모든 이미지에 대해 설정된 크기의 창 (여기서는 128x128)을 실행하여 관심 영역을 선택하고 관심 영역이이 창 크기에 완벽하게 맞지 않을 수 있기 때문입니다. .

모든 규모에서 샘플 구현 및 전체 감지.

함께 모아서

두 부분이 모두 완료된 후 남은 일은 테스트 이미지가 제공 될 때 최종 출력을 얻기 위해 연속적으로 호출하는 것입니다.

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!