1 한국 R 컨퍼런스

한국 R 컨퍼런스에 다양한 발표자가 프로필 사진을 보내주셨기 때문에 어떤 형태로든 이를 표준화시킬 필요가 있다. 가장 일반적인 방식은 사진속 얼굴을 중심으로 작은 원을 통해 졸업앨범과 같은 느낌을 구현하는 것이 아닌가 싶다.

2 얼굴 인식

가장 먼저 사진속 얼굴을 인식하는 것이다. 코를 중심으로 가로세로 위치를 통해 얼굴이 특정된 이미지를 찾아내고 그 다음 단계로 작은 원을 그려 졸업앨범 발표자 사진으로 표준화시키는 것이다.

2.1 opencv 환경 설정

ropensci opencv 팩키지를 기본으로 하여 외부 opencv 기능을 활용하여 얼굴을 잡아내도록 한다. opencv가 운영체제에 설치되어 있어야 하기 때문에 먼저 다음 명령어로 맥의 경우 설치한다.

brew install opencv

opencv 팩키지를 R에서 사용할 수 있도록 opencv R 팩키지도 설치한다.

install.packages("opencv")

2.2 헬로 월드

opencv R 팩키지를 사용해서 발표자 얼굴을 인식해서 특정 부분을 뽑아내는 사례를 만들어보자.

library(tidyverse)
library(opencv)
library(magick)

julia_raw <- ocv_read('fig/rconf_speakers/julia_silge.jpg')
julia_face <- ocv_face(julia_raw)

julia_face
<pointer: 0x000000001485f530>
attr(,"class")
[1] "opencv-image"

opencv 팩키지 ocv_facemask() 함수를 사용해서 사진속 얼굴 마스크 정보를 추출하자.

julia_face_info <- ocv_facemask(julia_face)

attributes(julia_face_info)
$class
[1] "opencv-image"

$faces
  radius   x   y
1     70 158 110

이미지에서 얼굴과 관련된 중요정보를 데이터프레임으로 추출한다.

julia_tbl <- attr(julia_face_info, "faces") %>% 
  as_tibble()

julia_tbl
# A tibble: 1 x 3
  radius     x     y
   <int> <int> <int>
1     70   158   110

2.3 얼굴인식 영역

인식한 얼굴의 영역을 원이 아니라 영역상자(bouding box)를 통해 원본 이미지 위에 표현해보자. 인식한 얼굴의 좌표를 바탕으로 얼굴이 위치한 곳에 녹색 영역상자를 그려보자. 먼저 원으로 된 좌표를 정사각형으로 변화시키는데 필요한 로직을 정리하면 다음과 같다. rect() 함수의 인자값에 맞춰야 하기 때문에 다음 수식을 사용한다.

julia_img <- image_read("fig/rconf_speakers/julia_silge.jpg")

## 영역 표시 -------------------------
julia_draw <- image_draw(julia_img)

rect(xleft   = julia_tbl$x - julia_tbl$radius, 
     xright  = julia_tbl$x + julia_tbl$radius,
     ybottom = julia_tbl$y - julia_tbl$radius,
     ytop    = julia_tbl$y + julia_tbl$radius, border = "green", lwd = 5)

dev.off()
png 
  2 
fs::dir_create("fig/rconf_speakers_processed")

julia_draw %>% 
  image_write("fig/rconf_speakers_processed/julia_rect.png")

image_read() 함수로 저장한 이미지를 불러와서 확인해보자.

julia_bbox_img <-  
  image_read("fig/rconf_speakers_processed/julia_rect.png")

julia_bbox_img

2.4 얼굴 추출 함수

앞선 알고리즘을 바탕으로 사진을 입력값으로 넣었을 때 얼굴을 추출하여 이미지로 저장하는 함수를 작성해보자. 원본 이미지 경로를 받아 이미지에서 얼굴 좌표를 얻어내고 이미지 영역 표시를 거쳐 얼굴 영역만 짤라낸다. 그리고 추출된 이미지를 적절한 확장자를 붙여 로컬 파일에 저장시킨다.

extract_face <- function(raw_image) {
  # 1. 얼굴인식 좌표 추출 --------------
  figure_face_info <- ocv_read(raw_image) %>% 
    ocv_facemask()
  
  ## 데이터프레임 자료구조 변환
  face_tbl <- attr(figure_face_info, "faces") %>% 
  as_tibble()
  
  # 2. 이미지 영역 표시 -------------
  figure_img <- image_read(raw_image)

  ## 영역 표시 -------------------------
  figure_draw <- image_draw(figure_img)
  
  rect(xleft   = face_tbl$x - face_tbl$radius, 
       xright  = face_tbl$x + face_tbl$radius,
       ybottom = face_tbl$y - face_tbl$radius,
       ytop    = face_tbl$y + face_tbl$radius, border = "green", lwd = 5)
  
  dev.off()

  # 3. 이미지 잘라내기 -------------
  figure_crop <- figure_draw %>% 
    image_crop(geometry_area(x_off = face_tbl$x - face_tbl$radius * 1.5, 
                             y_off = face_tbl$y - face_tbl$radius * 1.5,
                             width = face_tbl$radius  * 2 * 1.5, 
                             height = face_tbl$radius * 2 * 1.5))
  
  # 4. 이미지 저장하기 -------------
  processed_filename <- fs::path_file(raw_image) %>% fs::path_ext_remove(.)
  
  figure_crop %>% 
    image_write(path =  glue::glue("fig/rconf_processed/{processed_filename}_face.png"))
  
  # return(figure_crop)
}

extract_face('fig/rconf_speakers/julia_silge.jpg')

julia_face_img <- image_read(path = "fig/rconf_processed/julia_silge_face.png")
julia_face_img

2.5 발표자 얼굴 추출

발표자 사진 모두에서 발표자를 추출하여 얼굴만 담긴 프로필 사진을 저장시킨다.

speakers_list <- fs::dir_ls("fig/rconf_speakers/")

safely_extract_face <- safely(extract_face, otherwise = NA_real_)

walk(speakers_list, safely_extract_face)

2.6 추출 결과확인

추출한 발표자 얼굴을 확인한다.

speakers_face_list <- fs::dir_ls("fig/rconf_processed/")

speakers_face_img <- map(speakers_face_list, image_read)
speakers_face_img <- map(speakers_face_img, image_resize, "100")

image_join(speakers_face_img) %>% 
  image_append()

3 졸업 얼굴 추출

ocv_facemask() 함수를 사용해서 바로 마스크를 사용해서 발표자 얼굴을 추출해보자.

julia_img <- image_read("fig/rconf_speakers/julia_silge.jpg")

# 1. 얼굴인식 좌표 추출 --------------
figure_face_info <- ocv_read('fig/rconf_speakers/julia_silge.jpg') %>% 
  ocv_facemask() 

# 2. 마스크 저장 --------------
# fs::dir_create("fig/rconf_circle")

figure_face_info %>% 
  ocv_write(path = "fig/rconf_circle/julia_silge_mask.png")
[1] "C:\\docs\\deep-learning\\fig\\rconf_circle\\julia_silge_mask.png"
julia_silge_mask <- 
  image_read("fig/rconf_circle/julia_silge_mask.png") %>% 
  image_transparent(color = "white")

# 3. 마스크 이미지에 적용 --------------

julia_silge_circle <- 
  image_composite(julia_img, julia_silge_mask, operator='atop') %>% 
  image_fill(
    color = "white", 
    refcolor = "black", 
    fuzz = 3,
    point = "+1+1" # start at top left 1 pixel in
  ) %>% 
  image_trim

julia_silge_circle %>% 
  image_resize("200%")

4 한국 R 컨퍼런스 발표자

한국 R 컨퍼런스 발표자가 확정되어 졸업앨범 사진 형태로 각 발표자가 보내준 프로필 사진에서 얼굴을 인식하여 우선 직사각형 형태로 특정하고 졸업앨범 형태로 발표자 사진 이미지를 추출한 결과를 확인해보자.

library(magick)
library(slickR)

rconf_mask_filename <- fs::dir_ls("fig/rconf_speakers_mask/")

speaker_name <- fs::path_file(rconf_mask_filename) %>% 
  str_remove("_face_mask\\.(png|gif)") %>% 
  str_replace("_", " ") %>% 
  str_to_title()

rconf_mask_tbl <- tibble(file_path    = rconf_mask_filename,
                         speaker_name = speaker_name)

opts <- settings(
  dots = TRUE,
  initialSlide = 0,
  slidesToShow = 3, 
  slidesToScroll = 3, 
  focusOnSelect = TRUE)

slickR(obj = rconf_mask_tbl$file_path)  %synch%
( slickR(rconf_mask_tbl$speaker_name, slideType = 'p') + settings(arrows = FALSE) ) +
  opts
 

데이터 과학자 이광춘 저작

kwangchun.lee.7@gmail.com