휴대폰으로 사진을 열심히 모았지만, 너무 많아 태그를 입력할 엄두가 나지 않았다. 입력할 때 파일의 정보를 추출하여 데이터 베이스에 정리했다. 언젠가 얼굴을 인식하는 소프트웨어를 응용하여 판별하려고 했는데, 2016년 정도에 개발된 openface를 찾았고 이를 사용하기로 했다. 인터넷의 누군가 좋은 튜토리얼을 (영문)만들었고 이를 많이 참조했다. 또한 openface가 수정없이 그대로 사용할 정도의 완성도를 보여준다. docker 이미지로 제공되는데 바로 사용할 수 있다.
목표한 4명의 얼굴을 학습시켰고, 이 모델을 기반으로 모든 파일을 분류 했다. 모델을 만들기는 금방인데, 사진에서 얼굴찾기, 그 얼굴이 누구인지는 확인하기 위해서는 많은 시간(내가 가진 PC로 3일..)을 썼다. 얼굴 인식 후 이를 파일로 기록하였고 아래와 같다.
=== /root/picture/사진/20150211_202325.jpg === === /root/picture/사진/MyPhoto_0149.jpg === Predict Daewon @ x=2075 with 0.66 confidence. === /root/picture/사진/20140412_162452.jpg === List of faces in image from left to right Predict Minsu @ x=1377 with 0.63 confidence. Predict Daewon @ x=1549 with 0.86 confidence. Predict Minsu @ x=1780 with 0.60 confidence. Predict Daewon @ x=1893 with 1.00 confidence. === /root/picture/사진/20130219_152444.jpg === Predict Daewon @ x=63 with 0.52 confidence. === /root/picture/사진/20121208_110821.jpg === === /root/picture/사진/사진110702_003.jpg === === /root/picture/사진/사진120207_010.jpg === Predict Minsu @ x=732 with 0.59 confidence. === /root/picture/사진/20140405_160811.jpg === Predict Miae @ x=1288 with 0.99 confidence. === /root/picture/사진/20140406_085437_ShearesAve.jpg === Predict Daewon @ x=775 with 0.98 confidence. === /root/picture/사진/20131013_134752_과천동.jpg ===
가끔 한 파일에 여러 이름을 기록하는데, 아마 학습하지 않은 인물이 사진에 있기 때문이다. 신뢰도 0.8 미만이면 믿을 수 없다고 판단하여 지웠다. 공백을 지우고, 여러 행을 한 행으로 만들기 위해 다음 python 스크립을 사용 했다. sed를 사용하려 했으나, python이 낫다. docker로 디렉토리를 root 아래에 바로 마운트하여 경로에 root가 포함되었다. vim으로 해당 경로로 모두 변경했다. 데이터베이스에 굳이 신뢰도와 얼굴 위치를 넣지 않아도 되어 모두 지웠다.
#_*_ coding: utf-8 _*_ import re import sys from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL) result_file = open('./resultv3.txt','r') flag_found_path=False; flag_found_prediction=False; num_lines = sum(1 for line in open('./resultv3.txt')) for x in range(num_lines): context=result_file.readline() #path를 찾는부분. path = re.compile("=== .* ===") searched_path = path.search(context) #predict를 찾는 부분 prediction = re.compile("Predict.*") searched_prediction = prediction.search(context) #===로 시작하는 부분. if searched_path != None: flag_found_path=True #간단한 버전. path_str = searched_path.group() print('\n'+path_str,end=',') if searched_prediction != None: predict_str = searched_prediction.group() print(predict_str,end=',')
대부분 코드를 stack overflow에서 찾아 사용했다.
아래와 같은 “파일 경로” 대 “사람 이름”을 만들 수 있다.
/imageOtherPar/사진/20130225_163915.jpg,미애 /imageOtherPar/사진/20121224_203050.jpg,채윤 /imageotherpar/사진/20130926_151133_人大会堂西路.jpg,채윤 /imageotherpar/사진/20130926_151133_人大会堂西路.jpg,대원 /imageOtherPar/사진/사진110612_018.jpg,민수 /imageOtherPar/사진/사진110612_018.jpg,대원 /imageOtherPar/사진/20130128_212312.jpg,민수 /imageOtherPar/사진/사진110612_004.jpg,민수 /imageOtherPar/사진/20150331_131747.jpg,미애 /imageOtherPar/사진/20150505_142947.jpg,민수 /imageOtherPar/사진/20150505_141543.jpg,대원 /imageOtherPar/사진/20140405_160859.jpg,미애 /imageOtherPar/사진/사진111016_021.jpg,미애
이 파일을 별도 테이블을 만들어 굳이 python으로 mysql에 입력했다.
#_*_ coding: utf-8 _*_ import pymysql import re import sys import datetime from signal import signal, SIGPIPE, SIG_DFL #pipe를 사용하기 위해. signal(SIGPIPE,SIG_DFL) present = datetime.datetime.now() print(present.date()) conn = pymysql.connect(host='localhost', user='*****', password='*****', db='*****', charset='utf8') #column을 접근하기 위해 Dictionary cursor 사용 #curs = conn.cursor(pymysql.cursors.DictCursor) curs = conn.cursor() #read path from file result_file = open('./찾을수있는얼굴v3.txt','r') num_lines = sum(1 for line in open('./찾을수있는얼굴v3.txt')) #이름을 ID로 변경. #민수-2, 미애-9, 대원-10, 채윤-6 for x in range(num_lines): #한 줄을 읽음. context=result_file.readline() #경로 찾음. path = re.compile("\/.*jpg") searched_path = path.search(context) #tag 확인. #tag_from_file = re.compile("민수|대원|미애|채윤") tag_from_file = re.compile("jpg,.*$") tag_searched_from_file_tmp = tag_from_file.search(context) if tag_searched_from_file_tmp != None: #print(tag_searched_from_file_tmp.group()) tmp_start = tag_searched_from_file_tmp.start() tmp_end = tag_searched_from_file_tmp.end() #file에서 찾은 태그. tag_searched_from_file = context[tmp_start+4:tmp_end] print(tag_searched_from_file) #print(type(tag_searched_from_file)) if searched_path != None: found_path = searched_path.group() print(found_path) #sql = "select '" + found_path + "' from picture" sql = "select * from `picture` where `경로`='"+found_path+"' order by id desc" #sql = "insert into `id_with_photo_path`(`id`,`경로`) values (22,'test2/');" no_tag = 0 if tag_searched_from_file == "민수" : no_tag = 2 if tag_searched_from_file == "채윤" : no_tag = 6 if tag_searched_from_file == "미애" : no_tag = 9 if tag_searched_from_file == "대원" : no_tag = 10 print(type(no_tag)) print(found_path) sql = "insert into `id_with_photo_path`(`id`,`경로`,`updated`) values ('"+str(no_tag)+"','"+found_path+"','"+str(present.year)+"-"+str(present.month)+"-"+str(present.day)+"');" #sql = """ insert into `id_with_photo_path`(`id`,`경로`) values (%s, %s)""" print(sql) #print("sql is", sql) curs.execute(sql) #rows = curs.fetchall() #rows는 전체 행을 표시. #column을 뽑기 위해 루프로 돌림. #for row in rows: #print(type(rows)) #print(len(rows)) # tag=row["태그"] #태그 확인부분. #print(tag) #rows.strip(",").split(",") #conn.commit() conn.close()
마지막에 conn.commit()을 하지 않으면 sql로 insert를 할 수 없다. 두번 실행을 방지하기 위해 #conn.commit()로 주석처리 했다.
다음으로 홈페이지에서 파일을 관리하기 위해, 기존에 작성한 사진 조회 페이지를 수정했다. php를 잘 알면 더 고급지게 할 수 있겠으나, 딱 이정도 수준이면 충분하다.
<?php /* Template Name: myPhotoQuery*/ ?> <?php get_header(); ?> <div id="main-content" class="main-content"> <?php if ( is_front_page() && twentyfourteen_has_featured_posts() ) { // Include the featured content template. get_template_part( 'featured-content' ); } ?> <div id="primary" class="content-area"> <div id="content" class="site-content" role="main"> <?php // Start the Loop. while ( have_posts() ) : the_post(); // Include the page content template. get_template_part( 'content', 'page' ); // If comments are open or we have at least one comment, load up the comment template. if ( comments_open() || get_comments_number() ) { comments_template(); } endwhile; ?> <?php //로그인 되었는지 확인하는 부분.. if( is_user_logged_in()) { echo '환영합니다.'; ?> <html> <body> <?php $config = parse_ini_file("****"); $link = mysqli_connect('localhost',$config['user'],$config['passwd']); if(!$link) { echo 'Could not connect'; echo '<br />'; exit; } else { echo '<br />'; echo 'Connected<br />'; echo '<br />'; } mysqli_set_charset($link,"utf8"); if(!mysqli_select_db($link,'myHome')){ echo "could not select DB<br />";} else{ echo "data base selected<br />";} //한국 시각 기준으로 설정.. date_default_timezone_set("Asia/Seoul"); $today = date("Y-m-d"); $time = time(); #echo $time; $minus30Day = date("Y-m-d", strtotime("-30 day", $time)); #echo $today; #echo $targetDay; //처음 접속시 사용자가 입력하는 부분.. ?> <form method="POST" action="" name="날자 입력"> <br><hr> 시작 날자 <input value=" <?php echo $minus30Day ?>" name="InputStartDay" type="datetime-local" > 끝 날자 <input value=" <?php echo $today?>" name="InputEndDay" type="datetime-local"> <br><br> 사람 이름<br> <input value="" name="InputTag"><br> <br><br> 표식 개수<br> <input value="10" name="InputLimit" type="number" min="1" max="200"> <br><br> 선택 완료 <input name="submitSelect" type="submit"> </form> <?php //선택후 쿼리로 전송.. if(isset($_POST["submitSelect"])){ #InputTag가 누구를 가리키는지 확인. $numbered_person = 0; if ($_POST[InputTag] == '대원') $numbered_person = 10; if ($_POST[InputTag] == '미애') $numbered_person = 9; if ($_POST[InputTag] == '민수') $numbered_person = 2; if ($_POST[InputTag] == '채윤') $numbered_person = 6; $file_limiter = 50; if( $_POST[InputLimit] > 0 and $_POST[InputLimit] < 1000) $file_limiter = $_POST[InputLimit]; #$query="select * from picture where (`종류`='이미지' and (`날자` >= '$_POST[InputStartDay]' and `날자` <='$_POST[InputEndDay]')) limit $_POST[InputLimit]"; # # #InputTag가 있을 경우 query. if($numbered_person != 0){ $queryCount= "select count(*) from `picture` inner join `id_with_photo_path` on `id_with_photo_path`.`경로`= `picture`.`경로` where (`종류`='이미지' and `picture`.`날자` >= '$_POST[InputStartDay]' and `날자` <='$_POST[InputEndDay]' and `id_with_photo_path`.`ID` = $numbered_person) limit $file_limiter"; $query ="select * from `picture` inner join `id_with_photo_path` on `id_with_photo_path`.`경로`= `picture`.`경로` where (`종류`='이미지' and `picture`.`날자` >= '$_POST[InputStartDay]' and `날자` <='$_POST[InputEndDay]' and `id_with_photo_path`.`ID` = $numbered_person) limit $file_limiter"; } else { $queryCount="select count(*) from picture where (`종류`='이미지' and (`날자` >= '$_POST[InputStartDay]' and `날자` <='$_POST[InputEndDay]'))"; $query = "select * from picture where (`종류`='이미지' and (`날자` >= '$_POST[InputStartDay]' and `날자` <='$_POST[InputEndDay]')) limit $file_limiter"; } $resultCount=mysqli_query($link,$queryCount) or die("error"); while($rowCount=mysqli_fetch_array($resultCount)){ echo "총 갯수는 ".$rowCount[0]; } echo $query; $result=mysqli_query($link,$query) or die("error"); $index=0; while($row=mysqli_fetch_array($result)){ $index=$index+1; ?> <div class="layout"> <div class="text" align="middle"> <br> <?php echo $row['설명']."<br>"; echo $row['날자']."<br>"; echo $row['태그']."<br>"; echo $row['경로']."<br>"; ?> </div> <div class="polaroid" align="right"> <img width="100%" alt="" src=<?=$row['경로']?> > </div> </div> <?php } } ?> </body> </html> <?php } else { echo '로그인하지 않고는 볼수 없습니다.'; } ?> </div><!-- #content --> </div><!-- #primary --> <?php get_sidebar( 'content' ); ?> </div><!-- #main-content --> <?php get_sidebar(); get_footer();
아래 동영상에 나오듯이 조회하는데, 공개 프로그램인지 정확도가 좀 떨어진다. 또한 2018년 이후 찍은 사진을 인식할 수 없다. 해상도를 높여 다시 학습시켜야 할 듯 하다. 이 짓을 완료하기까지 약 일주일정도 걸렸다.