블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

연예인 얼굴 인식 서비스를 만들어보자 #1 - 학습데이타 준비하기


조대협 (http://bcho.tistory.com)


CNN 에 대한 이론 공부와 텐서 플로우에 대한 기본 이해를 끝내서 실제로 모델을 만들어보기로 하였다.

CNN을 이용한 이미지 인식중 대중적인 주제로 얼굴 인식 (Face recognition)을 주제로 잡아서, 이 모델을 만들기로 하고 아직 실력이 미흡하여 호주팀에서 일하고 있는 동료인 Win woo 라는 동료에게 모델과 튜토리얼 개발을 부탁하였다.


이제 부터 연재하는 연예인 얼굴 인식 서비스는 Win woo 가 만든 코드를 기반으로 하여 설명한다. (코드 원본 주소 : https://github.com/wwoo/tf_face )

얼굴 데이타를 내려 받자

먼저 얼굴 인식 모델을 만들려면, 학습을 시킬 충분한 데이타가 있어야 한다. 사람 얼굴을 일일이 구할 수 도 없고, 구글이나 네이버에서 일일이 저장할 수 도 없기 때문에, 공개된 데이타셋을 활용하였는데, PubFig (Public Figures Face Database - http://www.cs.columbia.edu/CAVE/databases/pubfig/) 를 사용하였다.



이 데이타셋에는 약 200명에 대한 58,000여장의 이미지를 저장하고 있는데, 이 중의 일부만을 사용하였다.

Download 페이지로 가면, txt 파일 형태 (http://www.cs.columbia.edu/CAVE/databases/pubfig/download/dev_urls.txt) 로 아래와 같이


Abhishek Bachan 1 http://1.bp.blogspot.com/_Y7rzCyUABeI/SNIltEyEnjI/AAAAAAAABOg/E1keU_52aFc/s400/ash_abhishek_365x470.jpg 183,60,297,174 f533da9fbd1c770428c8961f3fa48950
Abhishek Bachan 2 http://1.bp.blogspot.com/_v9nTKD7D57Q/SQ3HUQHsp_I/AAAAAAAAQuo/DfPcHPX2t_o/s400/normal_14thbombaytimes013.jpg 49,71,143,165 e36a8b24f0761ec75bdc0489d8fd570b
Abhishek Bachan 3 http://2.bp.blogspot.com/_v9nTKD7D57Q/SL5KwcwQlRI/AAAAAAAANxM/mJPzEHPI1rU/s400/ERTYH.jpg 32,68,142,178 583608783525c2ac419b41e538a6925d


사람이름, 이미지 번호, 다운로드 URL, 사진 크기, MD5 체크섬을 이 필드로 저장되어 있다.

이 파일을 이용하여 다운로드 URL에서 사진을 다운받아서, 사람이름으로된 폴더에 저장한다.

물론 수동으로 할 수 없으니 HTTP Client를 이용하여, URL에서 사진을 다운로드 하게 하고, 이를 사람이름 폴더 별로 저장하도록 해야 한다.


HTTP Client를 이용하여 파일을 다운로드 받는 코드는 일반적인 코드이기 때문에 별도로 설명하지 않는다.

본인의 경우에는 Win이 만든 https://github.com/wwoo/tf_face/blob/master/tf/face_extract/pubfig_get.py 코드를 이용하여 데이타를 다운로드 받았다.

사용법은  https://github.com/wwoo/tf_face 에 나와 있는데,


$> python tf/face_extract/pubfig_get.py tf/face_extract/eval_urls.txt ./data

를 실행하면 ./data 디렉토리에 이미지를 다운로드 받아서 사람 이름별 폴더에 저장해준다.

evals_urls.txt에는 위에서 언급한 dev_urls.txt 형태의 데이타가 들어간다.


사람 종류가 너무 많으면 데이타를 정재하는 작업이 어렵고, (왜 어려운지는 뒤에 나옴) 학습 시간이 많이 걸리기 때문에, 약 47명의 데이타를 다운로드 받아서 작업하였다.

쓰레기 데이타 골라내기

데이타를 다운받고 나니, 아뿔사!! PubFig 데이타셋이 오래되어서 없는 이미지도 있고 학습에 적절하지 않은 이미지도 있다.


주로 학습에 적절하지 않은 데이타는 한 사진에 두사람 이상의 얼굴이 있거나, 이미지가 사라져서 위의 우측 그림처럼, 이미지가 없는 형태로 나오는 경우인데, 이러한 데이타는 어쩔 수 없이 눈으로 한장한장 다 걸러내야만 하였다.

아마 이 작업이 가장 오랜 시간이 걸린 작업이 아닐까도 한다. 더불어서 머신러닝이 정교한 수학이나 알고리즘이 아니라 노가다라고 불리는 이유를 알았다.

얼굴 추출하기

다음 학습에 가능한 데이타를 잘 골라내었으면, 학습을 위해서 사진에서 얼굴만을 추출해내야 한다. 포토샵으로 일일이 할 수 없기 때문에 얼굴 영역을 인식하는 API를 사용하기로한다. OPEN CV와 같은 오픈소스 라이브러리를 사용할 수 도 있지만 구글의 VISION API의 경우 얼굴 영역을 아주 잘 잘라내어주고, 코드 수십줄만 가지고도 얼굴 영역을 알아낼 수 있기 때문에 구글 VISION API를 사용하였다.

https://cloud.google.com/vision/




VISION API ENABLE 하기

VISION API를 사용하기 위해서는 해당 구글 클라우드 프로젝트에서 VISION API를 사용하도록 ENABLE 해줘야 한다.

VISION API를 ENABLE하기 위해서는 아래 화면과 같이 구글 클라우드 콘솔 > API Manager 들어간후




+ENABLE API를 클릭하여 아래 그림과 같이 Vision API를 클릭하여 ENABLE 시켜준다.




SERVICE ACCOUNT 키 만들기

다음으로 이 VISION API를 호출하기 위해서는 API 토큰이 필요한데, SERVICE ACCOUNT 라는 JSON 파일을 다운 받아서 사용한다.

구글 클라우드 콘솔에서 API Manager로 들어간후 Credentials 메뉴에서 Create creadential 메뉴를 선택한후, Service account key 메뉴를 선택한다



다음 Create Service Account key를 만들도록 하고, accountname과 id와 같은 정보를 넣는다. 이때 중요한것이 이 키가 가지고 있는 사용자 권한을 설정해야 하는데, 편의상 모든 권한을 가지고 있는  Project Owner 권한으로 키를 생성한다.


(주의. 실제 운영환경에서 전체 권한을 가지는 키는 보안상의 위험하기 때문에 특정 서비스에 대한 접근 권한만을 가지도록 지정하여 Service account를 생성하기를 권장한다.)




Service account key가 생성이 되면, json 파일 형태로 다운로드가 된다.

여기서는 terrycho-ml-80abc460730c.json 이름으로 저장하였다.


예제 코드

그럼 예제를 보자 코드의 전문은 https://github.com/bwcho75/facerecognition/blob/master/com/terry/face/extract/crop_face.py 에 있다.


이 코드는 이미지 파일이 있는 디렉토리를 지정하고, 아웃풋 디렉토리를 지정해주면 이미지 파일을 읽어서 얼굴이 있는지 없는지를 체크하고 얼굴이 있으면, 얼굴 부분만 잘라낸 후에, 얼굴 사진을 96x96 사이즈로 리사즈 한후에,

70%의 파일들은 학습용으로 사용하기 위해서 {아웃풋 디렉토리/training/} 디렉토리에 저장하고

나머지 30%의 파일들은 검증용으로 사용하기 위해서 {아웃풋 디렉토리/validate/} 디렉토리에 저장한다.


그리고 학습용 파일 목록은 다음과 같이 training_file.txt에 파일 위치,사람명(라벨) 형태로 저장하고

/Users/terrycho/traning_datav2/training/wsmith.jpg,Will Smith

/Users/terrycho/traning_datav2/training/wsmith061408.jpg,Will Smith

/Users/terrycho/traning_datav2/training/wsmith1.jpg,Will Smith


검증용 파일들은 validate_file.txt에 마찬가지로  파일위치와, 사람명(라벨)을 저장한다.

사용 방법은 다음과 같다.

python com/terry/face/extract/crop_face.py “원본 파일이있는 디렉토리" “아웃풋 디렉토리"

(원본 파일 디렉토리안에는 {사람이름명} 디렉토리 아래에 사진들이 쭈욱 있는 구조라야 한다.)


자 그러면, 코드의 주요 부분을 살펴보자


VISION API 초기화 하기

  def __init__(self):

       # initialize library

       #credentials = GoogleCredentials.get_application_default()

       scopes = ['https://www.googleapis.com/auth/cloud-platform']

       credentials = ServiceAccountCredentials.from_json_keyfile_name(

                       './terrycho-ml-80abc460730c.json', scopes=scopes)

       self.service = discovery.build('vision', 'v1', credentials=credentials)


초기화 부분은 Google Vision API를 사용하기 위해서 OAuth 인증을 하는 부분이다.

scope를 googleapi로 정해주고, 인증 방식을 Service Account를 사용한다. credentials 부분에 service account key 파일인 terrycho-ml-80abc460730c.json를 지정한다.


얼굴 영역 찾아내기

다음은 이미지에서 얼굴을 인식하고, 얼굴 영역(사각형) 좌표를 리턴하는 함수를 보자


   def detect_face(self,image_file):

       try:

           with io.open(image_file,'rb') as fd:

               image = fd.read()

               batch_request = [{

                       'image':{

                           'content':base64.b64encode(image).decode('utf-8')

                           },

                       'features':[{

                           'type':'FACE_DETECTION',

                           'maxResults':MAX_RESULTS,

                           }]

                       }]

               fd.close()

       

           request = self.service.images().annotate(body={

                           'requests':batch_request, })

           response = request.execute()

           if 'faceAnnotations' not in response['responses'][0]:

                print('[Error] %s: Cannot find face ' % image_file)

                return None

               

           face = response['responses'][0]['faceAnnotations']

           box = face[0]['fdBoundingPoly']['vertices']

           left = box[0]['x']

           top = box[1]['y']

               

           right = box[2]['x']

           bottom = box[2]['y']

               

           rect = [left,top,right,bottom]

               

           print("[Info] %s: Find face from in position %s" % (image_file,rect))

           return rect

       except Exception as e:

           print('[Error] %s: cannot process file : %s' %(image_file,str(e)) )

 

VISION API를 이용하여, 얼굴 영역을 추출하는데, 위의 코드에서 처럼 image_file을 읽은후에, batch_request라는 문자열을 만든다. JSON 형태의 문자열이 되는데, 이때 image라는 항목에 이미지 데이타를 base64 인코딩 방식으로 인코딩해서 전송한다. 그리고 VISION API는 얼굴인식뿐 아니라 사물 인식, 라벨인식등 여러가지 기능이 있기 때문에 그중에서 타입을 ‘FACE_DETECTION’으로 정의하여 얼굴 영역만 인식하도록 한다.


request를 만들었으면, VISION API로 요청을 보내면 응답이 오는데, 이중에서 response 엘리먼트의 첫번째 인자 ( [‘responses’][0] )은 첫번째 얼굴은 뜻하는데, 여기서 [‘faceAnnotation’]을 하면 얼굴에 대한 정보만을 얻을 수 있다. 이중에서  [‘fdBoundingPoly’] 값이 얼굴 영역을 나타내는 사각형이다. 이 갑ㄱㅅ을 읽어서 left,top,right,bottom 값에 세팅한 후 리턴한다.


얼굴 잘라내고 리사이즈 하기

앞의 detect_face에서 찾아낸 얼굴 영역을 가지고 그 부분만 전체 사진에서 잘라내고, 잘라낸 얼굴을 학습에 적합하도록 같은 크기 (96x96)으로 리사이즈 한다.

이런 이미지 처리를 위해서 PIL (Python Imaging Library - http://www.pythonware.com/products/pil/)를 사용하였다.

   def crop_face(self,image_file,rect,outputfile):

       try:

           fd = io.open(image_file,'rb')

           image = Image.open(fd)  

           crop = image.crop(rect)

           im = crop.resize(IMAGE_SIZE,Image.ANTIALIAS)

           im.save(outputfile,"JPEG")

           fd.close()

           print('[Info] %s: Crop face %s and write it to file : %s' %(image_file,rect,outputfile) )

       except Exception as e:

           print('[Error] %s: Crop image writing error : %s' %(image_file,str(e)) )

image_file을 인자로 받아서 , rect 에 정의된 사각형 영역 만큼 crop를 해서 잘라내고, resize 함수를 이용하여 크기를 96x96으로 조정한후 (참고 IMAGE_SIZE = 96,96 로 정의되어 있다.) outputfile 경로에 저장하게 된다.        


실행을 해서 정재된 데이타는 다음과 같다.


생각해볼만한점들

이 코드는 간단한 토이 프로그램이기 때문에 간단하게 작성했지만 실제 운영환경에 적용하기 위해서는 몇가지 고려해야 할 사항이 있다.

먼저, 이 코드는 싱글 쓰레드로 돌기 때문에 속도가 상대적으로 느리다 그래서 멀티 쓰레드로 코드를 수정할 필요가 있으며, 만약에 수백만장의 사진을 정재하기 위해서는 한대의 서버로 되지 않기 때문에, 원본 데이타를 여러 서버로 나눠서 처리할 수 있는 분산 처리 구조가 고려되어야 한다.

또한, VISION API로 사진을 전송할때는 BASE64 인코딩된 구조로 서버에 이미지를 직접 전송하기 때문에, 자칫 이미지 사이즈들이 크면 네트워크 대역폭을 많이 잡아먹을 수 있기 때문에 가능하다면 식별이 가능한 크기에서 리사이즈를 한 후에, 서버로 전송하는 것이 좋다. 실제로 필요한 얼굴 크기는 96x96 픽셀이기 때문에 필요없이 1000만화소 고화질의 사진들을 전송해서 네트워크 비용을 낭비하지 않기를 바란다.


다음은 이렇게 정재한 파일들을 텐서플로우에서 읽어서 학습 데이타로 활용하는 방법에 대해서 알아보겠다.


Google Cloud Vision API 사용하기


구글 클라우드 비젼 API 사용하기

조대협 (http://bcho.tistory.com)






빅데이타와 머신러닝과 같은 기술이 요즘 인터넷을 매우고 있는 시대에, 구글이 얼마전 이미지 디텍션 (Image detection)이 가능한, Cloud Vision API라는 오픈 API를 발표하였다. 현재는 베타버전 상태인데, 호기심에 빠르게 한번 테스트를 해봤다.


node.js를 이용하여, 간단한 테스트 프로그램을 만들어서 테스트를 해봤는데, 구현에 걸리는 시간은 불과 10분이 안된듯... (node.js는 역시 프로토타이핑용으로는 정말 좋은듯)


Cloud Vision API 억세스 권한 얻기


Cloud Vision API는 현재 베타 상태이다. 접근을 하려면 별도로 요청을 해야 접근 권한을 받을 수 있다. https://cloud.google.com/vision/ 에서 권한을 신청하면 심사를 하고 권한을 준다. 



Cloud Vision API 활성화 하기

권한을 얻은 후에는 Google Cloud Platform API 관리자 콘솔에 접속해서 Cloud Vision API 사용을 활성화해야 한다. 



다음으로 API를 외부에서 호출하기 위한 API 키를 발급받아야 하는데, Google Cloud Platform API에서는 API 키에 대한 다양한 접근 방식을 제공한다. OAuth 방식의 접근, 서버를 위한 API 키 발급 방식등 여러 방식을 지원하는데, 이 예제에서는 서비스 계정키를 JSON 형태로 다운 받아서 사용하도록 한다.


계정키를 생성 하는 방법은 Google Cloud Platform 콘솔에서 “사용자 인증 정보” 메뉴에서 “서비스 계정 키 만들기” 메뉴를 통해서 키를 생성하고 JSON 파일을 다운로드 받을 수 있다.





Cloud Vision API 호출 하기

https://cloud.google.com/vision/docs/getting-started?utm_source=product-announcement&utm_medium=email&utm_campaign=2016-02-Vision-API&utm_content=NoFT 


를 보면 Cloud Vision API를 호출하는 방법이 자세하게 설명되어 있다. 제공하는 기능에 비해서 API 사용법은 무지 간단한데, JSON으로 REST 방식으로 API를 호출하면 분석 결과를 JSON으로 리턴해준다. 이때 이미지 파일은 이 JSON에 base64 인코딩으로 첨부를 하면 된다. 어렵지 않으니 문서를 한번 쭈욱 보면서 흐름을 따라가보기를 권장한다. 


node.js 모듈 준비하기

base64 인코딩 모듈을 넣어서 샘플 요청을 만드는 것 조차 귀찮아서 node.js의 npm모듈이 이미 있는지 찾아보기로 하였다. 역시나 있다. 테스트를 위해서 사용한 모듈은 google-vision-api-client 라는 모듈이다.https://www.npmjs.com/package/google-vision-api-client


%npm install google-vision-api-client


명령을 사용해서 모듈을 설치한다. 설치가 끝난후에, 모듈이 설치된 디렉토리 (내 경우에는 “/Users/terry/node_modules/google-visionapi-client” - Mac OS)에 들어가서 index.js 파일을 열어보자. 이 모듈은 Cloud Vision API가 예전 알파버전이었을때 개발된 후 업데이트가 되지 않아서 현재 베타 버전의 Cloud Vision API호출이 안된다. Cloud Vision API의 End point가 변경되었기 때문인데.

index.js 파일에서 baseurl의 값을 다음과 같이 바꿔주자. (Cloud vision API의 베타 버전 URL로 변경)


var baseurl = ‘https://vision.googleapis.com/v1/images:annotate';


이제 Cloud Vision API를 호출하기 위한 준비가 끝났다.


node.js로 Cloud Vision API 호출 하기

이제 google-vision-api-client를 이용하여 API를 호출해보자.다음은 google-vision-api-client에서 제공하는 예제이다.


var vision = require('google-vision-api-client');

var requtil = vision.requtil;

 

//Prepare your service account from trust preview certificated project

var jsonfile = '/Users/terry/dev/ws/nodejs/GoogleVisionAPISample/My Project-eee0a2d4532a.json';

 

//Initialize the api

vision.init(jsonfile);

 

//Build the request payloads

var d = requtil.createRequests().addRequest(

requtil.createRequest('/Users/terry/images/dale2.jpg')

.withFeature('FACE_DETECTION', 3)

.withFeature('LABEL_DETECTION', 2)

.build());

 

//Do query to the api server

vision.query(d, function(e, r, d){

if(e) console.log('ERROR:', e);

  console.log(JSON.stringify(d));

});



<예제. visionAPI.js >

코드를 작성하고, jsonfile 경로에 앞에서 다운받은 서비스 계정키 JSON 파일의 경로를 적어주면 된다.


그리고, createRequest 부분에, Google Cloud Vision API로 분석하고자 하는 이미지 파일명을 적고, withFeature라는 메서드를 이용해서 어떤 분석을 할것인지를 명시한다. (이 부분은 뒤에서 다시 설명한다.)


그러면 다음 명령을 통해서 실행을 해보자


%node visionAPI.js


실행을 하면 실행 결과를 json 형태로 리턴을 해주는데, 

테스트에서 사용한 이미지와 결과는 다음과 같다.



<그림. 테스트에서 사용한 이미지 >



{  

   "responses":[  

      {  

         "faceAnnotations":[  

            {  

               "boundingPoly":{  

                  "vertices":[  

                     {  

                        "x":122,

                        "y":52

                     },

                     {  

                        "x":674,

                        "y":52

                     },

                     {  

                        "x":674,

                        "y":693

                     },

                     {  

                        "x":122,

                        "y":693

                     }

                  ]

               },

               "fdBoundingPoly":{  

                  "vertices":[  

                     {  

                        "x":176,

                        "y":208

                     },

                             중략...

                     }

                  ]

               },

               "landmarks":[  

                  {  

                     "type":"LEFT_EYE",

                     "position":{  

                        "x":282.99844,

                        "y":351.67017,

                        "z":-0.0033840234

                     }

                  },

                  {  

                     "type":"RIGHT_EYE",

                     "position":{  

                        "x":443.8624,

                        "y":336.31445,

                        "z":-35.029751

                     }

                  },

               중략...

                  }

               ],

               "rollAngle":-3.8402841,

               "panAngle":-12.196975,

               "tiltAngle":-0.68598062,

               "detectionConfidence":0.8096019,

               "landmarkingConfidence":0.64295566,

               "joyLikelihood":"LIKELY",

               "sorrowLikelihood":"VERY_UNLIKELY",

               "angerLikelihood":"VERY_UNLIKELY",

               "surpriseLikelihood":"VERY_UNLIKELY",

               "underExposedLikelihood":"VERY_UNLIKELY",

               "blurredLikelihood":"VERY_UNLIKELY",

               "headwearLikelihood":"VERY_UNLIKELY"

            }

         ],

         "labelAnnotations":[  

            {  

               "mid":"/m/068jd",

               "description":"photograph",

               "score":0.92346138

            },

            {  

               "mid":"/m/09jwl",

               "description":"musician",

               "score":0.86925673

            }

         ]

      }

   ]

}

<그림. 결과 JSON>


결과를 살펴보면, 눈코입의 위치와, 감정상태등을 상세하게 리턴해주는 것을 볼 수 있다.

joyLikeihood 는 기쁨 감정, sorrowLikeihood는 슬픈 감정들을 나타내는데, 

Cloud Vision API는 여러개의 Feature를 동시에 분석이 가능하다.


예를 들어 얼굴 인식과 로고 인식을 같이 활용하여, 특정 브랜드를 보고 있을때 사람들의 얼굴 표정이 어떤지를 분석함으로써 대략적인 브랜드에 대한 반응을 인식한다던지. 특정 랜드마크와 표정 분석을 통해서 장소에 대한 분석 (재미있는 곳인지? 슬픈 곳인지.) 등으로 활용이 가능하다.


Cloud Vision API의 이미지 분석 기능


Cloud Vision API는 여러가지 형태의 이미지 분석 기능을 제공하는데, 대략적인 내용을 훝어보면 다음과 같다.


  • Label Detection - 사진속에 있는 사물을 찾아준다. 가구, 동물 , 음식등을 인지해서 리턴해준다.
  • Logo Detection - 사진속에서 회사 로고와 같은 로고를 찾아준다.
  • Landmark Detection - 사진속에서 유명한 랜드 마크 (남산 타워, 경복궁등과 같은 건축물이나 자연 경관 이름)를 찾아준다.
  • Face Detection - 사진속에서 사람 얼굴을 찾아준다. 이게 좀 재미있는데 눈코입의 위치등을 리턴하는 것을 물론 표정을 분석하여 감정 상태를 분석하여 리턴해준다. 화가 났는지 기쁜 상태인지 슬픈 상태인지
  • Safe Search Detection - 사진 컨텐츠의 위험도(? 또는 건전성)을 검출해주는데, 성인 컨텐츠, 의학 컨텐츠, 폭력 컨텐츠등의 정도를 검출해준다.
  • Optical Character Recognition - 문자 인식


그외에도 몇가지 추가적인 Feature를 제공하고 있으니 https://cloud.google.com/vision/ 문서를 참고하기 바란다.


Cloud Vision API는 무슨 의미를 제공하는가?


사실 Cloud Vision API 자체로만으로도 대단히 흥미로운 기능을 가지고 있지만, Cloud Vision API는 또 다른 의미를 가지고 있다고 본다.

머신러닝이나 빅데이타, 그리고 인공 지능은 데이타 과학자와 대규모 하드웨어 자원을 가지고 있는 업체의 전유물이었지만, 근래에는 이러한 빅데이타 관련 기술들이 클라우드를 기반으로 하여 API로 제공됨으로써, 누구나 쉽게 빅데이타 기반의 분석 기술을 쉽게 활용할 수 있는 시대가 되어가고 있다.

이미 구글이나 Microsoft의 경우 머신러닝 알고리즘을 클라우드 API로 제공하고 있고, 대규모 데이타 분석의 경우에도 Google Analytics나 Yahoo Flurry등을 통해서 거의 무료로 제공이 되고 있다. (코드 몇줄이면 앱에 추가도 가능하다.)

이러한 접근성을 통해서 많은 서비스와 앱들이 고급 데이타 분석 알고리즘과 인공지능 기능들을 사용할 수 있는 보편화의 시대에 들어 선것이 아닐까?