728x90
반응형

저번에 얼굴 인식을 하는 프로젝트를 진행했었는데

이 기술을 활용해서 닮은 연예인을 찾아주는 프로그램을 만들어보았다. 

얼굴 인식을 어떻게 하는 건지 궁금하다면 아래 글을 보도록 하자.

실시간 얼굴 인식과 모자이크 처리: https://jinho-study.tistory.com/249

 

실시간 얼굴 인식과 모자이크 처리

꽤 전에 완성을 했었는데 블로그에 올린다는 것을 완전히 까먹고 있었다. 전에 얼굴 탐지 및 전처리, 데이터 크롤링 등등의 글들을 올렸었는데, 그 이후 조금의 삽질 과정이 있었고 결국 완성했�

jinho-study.tistory.com

이번 닮은 연예인 찾기에서는 데이터베이스에 한국 연예인, 유명인 등을 100명 정도 저장해놓았다.

이런 식으로 이미지가 저장되어있다.

 

탐지된 사람의 랜드마크와 데이터베이스의 랜드마크 중 제일 짧은 거리에 속하는 사람을 알려주는 간단한 프로그램이다. 

거리가 너무 먼데도 닮은 사람이라고 하면 안 되기 때문에 0.5를 기준으로 정해주었다.

우선 아래의 모델들이 필요하니 주소를 통해 다운로드하여야 한다.

github.com/davisking/dlib-models/raw/master/shape_predictor_68_face_landmarks.dat.bz2

github.com/kairess/simple_face_recognition/raw/master/models/dlib_face_recognition_resnet_model_v1.dat

github.com/kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning/raw/master/models/dlib_face_recognition_resnet_model_v1.dat

코드는 이전과 많이 다르지 않다.

라이브러리)

import dlib, cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import ImageFont, ImageDraw, Image
import tensorflow.keras 
from tensorflow.keras import backend as K
import glob

detector = dlib.get_frontal_face_detector()
sp = dlib.shape_predictor('models/shape_predictor_68_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('models/dlib_face_recognition_resnet_model_v1.dat')

 

사용한 함수들)

def find_faces(img):
    dets = detector(img, 1)

    if len(dets) == 0:
        return np.empty(0), np.empty(0), np.empty(0)
    
    rects, shapes = [], []
    shapes_np = np.zeros((len(dets), 68, 2), dtype=np.int)
    for k, d in enumerate(dets):
        rect = ((d.left(), d.top()), (d.right(), d.bottom()))
        rects.append(rect)

        shape = sp(img, d)
        
        # convert dlib shape to numpy array
        for i in range(0, 68):
            shapes_np[k][i] = (shape.part(i).x, shape.part(i).y)

        shapes.append(shape)
        
    return rects, shapes, shapes_np


def encode_faces(img, shapes):
    face_descriptors = []
    for shape in shapes:
        face_descriptor = facerec.compute_face_descriptor(img, shape)
        face_descriptors.append(np.array(face_descriptor))

    return np.array(face_descriptors)
    
    
def draw(img, comment, x, y, h, size):
    img = Image.fromarray(img)
    draw = ImageDraw.Draw(img)
    draw.text((x+5,y+h), comment, font=ImageFont.truetype("./batang.ttc", size), fill=(40,180,120))
    
    return np.array(img)  

 

데이터 베이스 생성)

label_name = []
label_class = {}
img_paths = glob.glob("kpop_img/*")

for path in img_paths:
    name = path.split(".")[0][9:]
    label_name.append(name)
    label_class[name] = path

#print(label_name)
print(len(label_class))
descs = []

for name, label_path in label_class.items():
    img = cv2.imread(label_path)
#    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR -> RGB
    _, img_shapes, _ = find_faces(img)
    descs.append([name, encode_faces(img, img_shapes)[0]])

np.save('descs.npy', descs)
#print(descs)

 

닮은 연예인 찾기(저장된 사진으로 찾기)

img = cv2.imread('test_img/taeyeon.jpg')
#img = cv2.imread('test_img/chen.jpg')
#img = cv2.imread('test_img/jinho.jpg')

img = cv2.flip(img, 1) # 좌우 대칭
rects, shapes, _ = find_faces(img) # 얼굴 찾기
descriptors = encode_faces(img, shapes) # 인코딩

if(len(descriptors) == 0):
    print("얼굴 인식이 안되었네요ㅜㅜ \n다시 찍어주세요!")
elif(len(descriptors) > 1):
    print("여러 명이 인식이 되었네요ㅜㅜ \n혼자 다시 찍어주세요!")
else:
    desc = descriptors[0]
    x = rects[0][0][0] # 얼굴 X 좌표
    y = rects[0][0][1] # 얼굴 Y 좌표
    w = rects[0][1][1]-rects[0][0][1] # 얼굴 너비 
    h = rects[0][1][0]-rects[0][0][0] # 얼굴 높이        

    descs1 = sorted(descs, key=lambda x: np.linalg.norm([desc] - x[1]))
    dist = np.linalg.norm([desc] - descs1[0][1], axis=1)
    if dist < 0.5:
        name = descs1[0][0]
        comment = "{0}을 닮으셨네요. 올~~".format(name) 
        img = cv2.imread(label_class[name])
#             img = cv2.resize(img, dsize=(780, 520))
        result = draw(img, comment, x-10, y, h, 36)
    else:
        comment = "닮은 연예인이 없네요\nㅜㅜ 성형하고 오세요!\n"
        img = cv2.imread("test_img/ojingeo.jpg")
        result = draw(img, comment, 50, 10, h, 24)

    print("거리: %.3f" % dist[0])
    print(comment)
#    cv2.imshow(name, result)
    result = cv2.cvtColor(result, cv2.COLOR_BGR2RGB) # BGR -> RGB
    plt.imshow(result)

cv2.waitKey(0)
cv2.destroyAllWindows()

 

닮은 연예인 찾기(실시간으로 찍어서 찾기)

버전 1)

user_name = input("너의 이름은? ") # 사용자 이름 입력

cap = cv2.VideoCapture(0) # 노트북 웹캠을 카메라로 사용
cap.set(3,640) # 너비
cap.set(4,480) # 높이

while(True):
    ret, frame = cap.read()
    frame = cv2.flip(frame, 1) # 좌우 대칭
    
    cv2.imshow('frame', frame)
    
    k = cv2.waitKey(30) & 0xff 
    if k == 49: # 1 키를 누르면 사진 찍음.
        cap.release()
        cv2.destroyAllWindows()
        
        rects, shapes, _ = find_faces(frame) # 얼굴 찾기
        descriptors = encode_faces(frame, shapes) # 인코딩

        if(len(descriptors) == 0):
            print("얼굴 인식이 안되었네요ㅜㅜ \n다시 찍어주세요!")
        elif(len(descriptors) > 1):
            print("여러 명이 인식이 되었네요ㅜㅜ \n혼자 다시 찍어주세요!")
        else:
            desc = descriptors[0]
            x = rects[0][0][0] # 얼굴 X 좌표
            y = rects[0][0][1] # 얼굴 Y 좌표
            w = rects[0][1][1]-rects[0][0][1] # 얼굴 너비 
            h = rects[0][1][0]-rects[0][0][0] # 얼굴 높이        

            descs1 = sorted(descs, key=lambda x: np.linalg.norm([desc] - x[1]))
            dist = np.linalg.norm([desc] - descs1[0][1], axis=1)
            if dist < 0.5:
                name = descs1[0][0]
                comment = "{0}을 닮으셨네요. 올~~".format(name) 
                img = cv2.imread(label_class[name])
#                 img = cv2.resize(img, dsize=(780, 520))
                result = draw(img, comment, x-10, y, h, 36)
            else:
                comment = "닮은 연예인이 없네요\nㅜㅜ 성형하고 오세요!\n"
                img = cv2.imread("test_img/ojingeo.jpg")
                result = draw(img, comment, 50, 10, h, 24)
            
            print("거리: %.3f" % dist[0])
            print(comment)
            cv2.imshow(name, result)
#             result = cv2.cvtColor(result, cv2.COLOR_BGR2RGB) # BGR -> RGB
#             plt.imshow(result)

        break
    
cv2.waitKey(0)
cv2.destroyAllWindows()

 

버전 2)

user_name = input("너의 이름은? ") # 사용자 이름 입력

cap = cv2.VideoCapture(0) # 노트북 웹캠을 카메라로 사용
cap.set(3,640) # 너비
cap.set(4,480) # 높이

while(True):
    ret, frame = cap.read()
    frame = cv2.flip(frame, 1) # 좌우 대칭
    
    cv2.imshow('frame', frame)
    
    k = cv2.waitKey(30) & 0xff 
    if k == 49: # 1 키를 누르면 사진 찍음.
        cap.release()
        cv2.destroyAllWindows()
        
        rects, shapes, _ = find_faces(frame) # 얼굴 찾기
        descriptors = encode_faces(frame, shapes) # 인코딩

        if(len(descriptors) == 0):
            print("얼굴 인식이 안되었네요ㅜㅜ \n다시 찍어주세요!")
        elif(len(descriptors) > 1):
            print("여러 명이 인식이 되었네요ㅜㅜ \n혼자 다시 찍어주세요!")
        else:
            desc = descriptors[0]
            x = rects[0][0][0] # 얼굴 X 좌표
            y = rects[0][0][1] # 얼굴 Y 좌표
            w = rects[0][1][1]-rects[0][0][1] # 얼굴 너비 
            h = rects[0][1][0]-rects[0][0][0] # 얼굴 높이        

            descs1 = sorted(descs, key=lambda x: np.linalg.norm([desc] - x[1]))
            dist = np.linalg.norm([desc] - descs1[0][1], axis=1)
            if dist < 0.45:
                name = descs1[0][0]
                comment = "{0}을 닮으셨네요. 올~~".format(name) 
                img = cv2.imread(label_class[name])
#                 img = cv2.resize(img, dsize=(780, 520))
                result = draw(img, comment, x-10, y, h, 36)
            else:
                print(dist)
                comment = "{0}님은 닮은 연예인이 없네요\nㅜㅜ 성형하고 오세요!\n".format(user_name)
                img = cv2.imread("test_img/ojingeo1.png")
                img = cv2.resize(img, dsize=(w, h+30)) 
                frame[y-30:y+h, x:x+w] = img 
                result = draw(frame, comment, 50, 10, h, 24)

            print(comment)
#             cv2.imshow(name, result)
            result = cv2.cvtColor(result, cv2.COLOR_BGR2RGB) # BGR -> RGB
            plt.imshow(result)

        break
    
cv2.waitKey(0)
cv2.destroyAllWindows()

 

결과는 아래와 같은 식으로 나온다. 여러 버전이 있으니, 맘에 드는 식으로 바꿔서 사용하면 좋을 듯.

닮은 사람이 있을 때
닮은 사람이 없을 때

 

github: https://github.com/kimjinho1/find_similar_celebrity/blob/master/Find_Similar_Celebrities.ipynb

 

kimjinho1/find_similar_celebrity

닮은 연예인 찾기. Contribute to kimjinho1/find_similar_celebrity development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

꽤 전에 완성을 했었는데 블로그에 올린다는 것을 완전히 까먹고 있었다.

전에 얼굴 탐지 및 전처리, 데이터 크롤링 등등의 글들을 올렸었는데, 

그 이후 조금의 삽질 과정이 있었고 결국 완성했다. 

얼굴 인식도 잘 되고 모자이크 처리도 잘 된다. 하지만...

컴퓨터 성능이 안 좋아서 그런지, 랜드마크를 추출하는데 시간이 오래 걸려서 그런지

프레임이 매우 매우 낮다. 이 문제가 나중에 해결된다면 업데이트하겠다.

 

얼굴 인식은 아래와 같이 진행된다.  

1. 데이터베이스에 인식하고 싶은 사람의 얼굴 랜드마크를 저장.

2. 실시간으로 사람 얼굴을 탐지하고 그 얼굴의 랜드마크를 추출.

3. 데이터베이스에 있는 랜드마크와 새로 추출된 랜드마크의 거리를 측정.

4. 제일 짧은 거리가 0.45 이하라면 그 사람으로 인식, 아니라면 모르는 사람이라고 인식.

5. 인식된 사람이라면 그대로 내버려 두고, 모르는 사람이라면 모자이크 처리를 함.

아래는 그 코드이다.

사용한 라이브러리)

# 라이브러리
import dlib, cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import ImageFont, ImageDraw, Image
import tensorflow.keras 
from tensorflow.keras import backend as K

detector = dlib.get_frontal_face_detector()
sp = dlib.shape_predictor('models/shape_predictor_68_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('models/dlib_face_recognition_resnet_model_v1.dat')

 

함수들)

# 얼굴 탐지
def find_faces(img):
    dets = detector(img, 1)
    
    if len(dets) == 0:
        return np.empty(0), np.empty(0), np.empty(0)
    
    rects, shapes = [], []
    shapes_np = np.zeros((len(dets), 68, 2), dtype=np.int)
    for k, d in enumerate(dets):
        rect = ((d.left(), d.top()), (d.right(), d.bottom()))
        rects.append(rect)

        shape = sp(img, d)
        
        # convert dlib shape to numpy array
        for i in range(0, 68):
            shapes_np[k][i] = (shape.part(i).x, shape.part(i).y)

        shapes.append(shape)
        
    return rects, shapes, shapes_np

# 랜드마크 추출
def encode_faces(img, shapes):
    face_descriptors = []
    for shape in shapes:
        face_descriptor = facerec.compute_face_descriptor(img, shape)
        face_descriptors.append(np.array(face_descriptor))

    return np.array(face_descriptors)

 

데이터베이스 생성)

# 인식하고 싶은 사람들 이미지 지정
# 여기는 사용자가 원하는 대로 바꾸면 된다. 
img_paths = {
     '진호': 'user_img/jinho.jpg',
     '태호': 'user_img/taeho.jpg',
     '아이린': 'user_img/irene.jpg',
     '조이': 'user_img/joy.jpeg',
     '창모': 'user_img/changmo.jpg',
     '홍철': 'user_img/hongchul.jpg',
     '명수': 'user_img/myungsoo.jpg'
}

# 인식하고 싶은 사람들의 얼굴 랜드마크 추출후 저장
descs = []

for name, img_path in img_paths.items(): 
    img = cv2.imread(img_path)
    _, img_shapes, _ = find_faces(img)
    descs.append([name, encode_faces(img, img_shapes)[0]])
    
np.save('user_img/descs.npy', descs)
print(descs)

 

실시간 얼굴 인식과 모자이크 처리)

cam = cv2.VideoCapture(0) # 노트북 웹캠 사용
cam.set(3, 640) # 너비
cam.set(4, 480) # 높이
font = cv2.FONT_HERSHEY_SIMPLEX # 폰트

while True:
    
    ret, img = cam.read() 
    img = cv2.flip(img, 1) # 좌우 대칭
#    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    rects, shapes, _ = find_faces(img) # 얼굴 찾기
    descriptors = encode_faces(img, shapes) # 인코딩
    
    for i, desc in enumerate(descriptors):
        x = rects[i][0][0] # 얼굴 X 좌표
        y = rects[i][0][1] # 얼굴 Y 좌표
        w = rects[i][1][1]-rects[i][0][1] # 얼굴 너비 
        h = rects[i][1][0]-rects[i][0][0] # 얼굴 높이
        
        # 추출된 랜드마크와 데이터베이스의 랜드마크들 중 제일 짧은 거리를 찾는 부분
        descs1 = sorted(descs, key=lambda x: np.linalg.norm([desc] - x[1]))
        dist = np.linalg.norm([desc] - descs1[0][1], axis=1)
        
        if dist < 0.45: # 그 거리가 0.45보다 작다면 그 사람으로 판단 
            name = descs1[0][0]
        else:           # 0.45보다 크다면 모르는 사람으로 판단 -> 모자이크 처리
            name = "누구?"
            mosaic_img = cv2.resize(img[y:y+h, x:x+w], dsize=(0, 0), fx=0.04, fy=0.04) # 축소
            mosaic_img = cv2.resize(mosaic_img, (w, h), interpolation=cv2.INTER_AREA)  # 확대
            img[y:y+h, x:x+w] = mosaic_img # 인식된 얼굴 영역 모자이크 처리 

        cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2) # 얼굴 영역 박스 
        cv2.putText(img, str(dist)[1:6], (x+5,y+h-5), font, 2, (0,0,255), 4) # 사진에 거리 출력

#     영어
#     cv2.putText(img, name, (x+5,y-5), font, 2, (255,255,255), 4)

#     한글
    img = Image.fromarray(img)
    draw = ImageDraw.Draw(img)
    draw.text((x+5,y-50), name, font=ImageFont.truetype("./batang.ttc", 60), fill=(255,255,255))
    img = np.array(img)
        
    cv2.imshow('camera', img)
    
    k = cv2.waitKey(10) & 0xff # 'ESC' 키 누르면 종료
    if k == 27:
        break
        
cam.release()
cv2.destroyAllWindows()

 

실시간 X 그냥 얼굴 인식과 모자이크 처리)

img = cv2.imread('test_img/inf.jpg')

rects, shapes, _ = find_faces(img) # 얼굴 찾기
descriptors = encode_faces(img, shapes) # 인코딩

for i, desc in enumerate(descriptors):
    x = rects[i][0][0] # 얼굴 X 좌표
    y = rects[i][0][1] # 얼굴 Y 좌표
    w = rects[i][1][1]-rects[i][0][1] # 얼굴 너비 
    h = rects[i][1][0]-rects[i][0][0] # 얼굴 높이        
        
    # 추출된 랜드마크와 데이터베이스의 랜드마크들 중 제일 짧은 거리를 찾는 부분
    descs1 = sorted(descs, key=lambda x: np.linalg.norm([desc] - x[1]))
    dist = np.linalg.norm([desc] - descs1[0][1], axis=1)

    if dist < 0.45: # 그 거리가 0.45보다 작다면 그 사람으로 판단 
        name = descs1[0][0]
    else:           # 0.45보다 크다면 모르는 사람으로 판단 -> 모자이크 처리
        name = "누구?"
        mosaic_img = cv2.resize(img[y:y+h, x:x+w], dsize=(0, 0), fx=0.04, fy=0.04) # 축소
        mosaic_img = cv2.resize(mosaic_img, (w, h), interpolation=cv2.INTER_AREA) # 확대
        img[y:y+h, x:x+w] = mosaic_img # 인식된 얼굴 영역 모자이크 처리
        
    cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2) # 얼굴 영역 박스 
    cv2.putText(img, str(dist)[1:6], (x+5,y+h+10), font, 1, (0,0,255), 4) # 사진에 거리 출력
    
#     영어
#     cv2.putText(img, name, (x+5,y-5), font, 2, (255,255,255), 4)

#     한글
    img = Image.fromarray(img)
    draw = ImageDraw.Draw(img)
    draw.text((x+5,y-30), name, font=ImageFont.truetype("./batang.ttc", 30), fill=(255,255,255))
    img = np.array(img)

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)        
plt.imshow(img)

 

일단 모델 파일 3개를 아래 주소를 통해 다운로드해야 된다.

github.com/davisking/dlib-models/raw/master/shape_predictor_68_face_landmarks.dat.bz2

github.com/kairess/simple_face_recognition/raw/master/models/dlib_face_recognition_resnet_model_v1.dat

github.com/kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning/raw/master/models/dlib_face_recognition_resnet_model_v1.dat

그리고 데이터베이스 생성 부분에서 본인의 파일 상황에 맞게 img_paths 안에 이름과 파일 경로를 바꿔줄 필요가 있다. 

결과 사진)

무한도전
창모
조이

 

데이터베이스에 박명수, 노홍철, 창모, 조이 등등의 사진이 있었는데 딱 지정된 사람의 얼굴만 인식하고

다른 사람들의 얼굴은 모드 모자이크 처리한 것을 볼 수 있다.

github: https://github.com/kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning/blob/master/real-time_face_recognition_and_mosaic.ipynb

 

kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning

딥러닝을 이용한 실시간 얼굴 인식과 모자이크 처리. Contribute to kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

에어 마우스: https://jinho-study.tistory.com/247

 

아두이노 에어 마우스(자이로센서)

2019년 창의적 종합설계 경진대회 때 만들었던 에어 마우스이다. 하루면 만들 수 있는 작품인데, 알 수 없는 통신에러 때문에 몇 주일을 날렸었다. 알고 보니 핀 문제였는데 각 핀마다 어느 정도 �

jinho-study.tistory.com

에어 마우스에서 필요 없는 기능만(민감도 조절) 빼면 바로 완성이다. 

그렇기 때문에 새로운 코드는 없다.

#include<Wire.h> // 통신? 때문에 필요
#include<Mouse.h> // 마우스 움직여야지!

/*
 SDA 2번 Pin, SCL 3번 Pin
 Int 7번 Pin
 Int 무조건 박아야됨. 
 */
 
#define MPU6050_INT_PIN 7 // 레오나르도에서는 무조건 박아주자

#define MPU6050_INT digitalPinToInterrupt(MPU6050_INT_PIN)

const int MPU_addr=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

void setup()
{
  // 설정인데 안 건드는게 좋음. 잘 되있음.
  Serial.begin(9600);

  Serial.println("start");
  Serial.print(MPU6050_INT); Serial.print(" on pin "); Serial.print(MPU6050_INT_PIN);
  
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  pinMode(10, INPUT); // 좌클릭 버튼
  pinMode(11, INPUT); // 우클릭 버튼
  Mouse.begin();
  pinMode(MPU6050_INT, INPUT);
  //attachInterrupt(MPU6050_INT, dmpDataReady, RISdlfhgk62ING);
}

void loop()
{
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  // 여기 위까지는 코드 건들지 않는 걸 추천. 여기 아래부터 수정 ㄱㄱ

  int lb = digitalRead(10); // 1이면 누른 상태, 0이면 안 누른 상태임.
  int rb = digitalRead(11); // 위와 마찬가지
  
  float gyroY, gyroZ;
  int sens = 1100;
  gyroZ = GyZ / sens;
  gyroY = -GyY / sens;

if(lb == 1) // Left button이 1이라는건 좌클릭을 하고 있다는 상태이므로 
  {                                       // 마우스를 누른 상태로 유지시켜주는 Mouse.press()를 사용함.
    Mouse.press(); // 좌클릭 한 상태 유지
  }
  if (rb == 1) // 우클릭
  {
    Mouse.press(MOUSE_RIGHT);
  } 
  if (lb == 0) 
  {
    Mouse.release(); //클릭 상태 해제
  }
   if (rb == 0) 
  {
    Mouse.release(MOUSE_RIGHT); //클릭 상태 해제
  }

  Sprint1(gyroY, gyroZ, lb, rb);
  Mouse.move(gyroZ, gyroY); // 마우스를 움직이는 함수
}

void Sprint1(int gyroY, int gyroZ, int lb, int rb) // 자이로, 버튼 값 확인용 시리얼 프린트 함수
{
  Serial.print("X축: "); 
  Serial.print(gyroZ);
  Serial.print(" // Y축: "); 
  Serial.print(-gyroY);
  Serial.print(" // 좌 클릭: ");
  Serial.print(lb);
  Serial.print(" // 우 클릭: ");
  Serial.println(rb);
}

필요한 재료는 다이소에서 파는 2000원? 짜리 장난감 총, 인두기, 접착제 정도이다. 

1. 장난감 총을 완전히 분해한다.

2. 방아쇠 부분과 손잡이 옆쪽 부분을 인두기로 뚫고 그 구멍 사이에 각각 버튼이 삐죽 튀어나도록 붙인다.

3. 손잡이 아래부분에 구멍을 뚫고, 점퍼선들을 잘 정리해서 그 구멍에서 빼냄.

4. 다시 조립하면 완성!

탕탕탕
728x90
반응형

'Projects > 에어 마우스' 카테고리의 다른 글

아두이노 에어 마우스(자이로센서)  (0) 2020.06.20
아두이노 조이스틱 마우스  (0) 2020.06.19
728x90
반응형

2019년 창의적 종합설계 경진대회 때 만들었던 에어 마우스이다. 

하루면 만들 수 있는 작품인데, 알 수 없는 통신에러 때문에 몇 주일을 날렸었다. 

알고 보니 핀 문제였는데 각 핀마다 어느 정도 정해진 역할 같은 것이 있는 건 알고 있었지만 

이런 식으로 통신에서 오류가 날줄은 몰랐다. 덕분에 큰 깨달음을 얻었다.. ㅜㅜ

 

시연 영상

시연 영상이다. 

사실 단순하게 자이로 센서와 버튼 센서만 있으면 되는 작품이지만 뭔가 내용이 너무 모자란 것 같아 민감도 설정 기능을 추가했다.

그리고 발표날에 사람들이 우리 작품 많이 보고 갔으면 좋겠다는 생각에 다이소 2000원짜리 장난감 총과

에어 마우스를 합체시킨 에어 건? 을 만들었고 이걸로 발표날 아침부터 부스에서 배틀그라운드를 했다.

학생들에게는 반응은 매우 좋았지만, 교수님들에게는 작품이 좀 애매한 것 같다?라는 평을 많이 들었다. 

몸으로는 그 말이 이해가 됐는데 지금 말로 설명하려니 좀 어려운 것 같다.

그래도 다행히 동상이라도 받았기에, 좋은 경험이었던 것 같다. 

#include<Wire.h> // 통신 때문에 필요
#include<Mouse.h> // 마우스 움직여야지!

/*
 SDA 2번 Pin, SCL 3번 Pin, Int 7번 Pin
 Int 무조건 박아야됨. 
*/
 
#define MPU6050_INT_PIN 7 // 레오나르도에서는 무조건 7번 Pin에 박아주자
#define MPU6050_INT digitalPinToInterrupt(MPU6050_INT_PIN)

const int MPU_addr=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

//민감도 조절 변수
int timer1 = 0;
int sens = 1100; // sens_num = 3
int sens_num = 3; // sens = 1100;

int Led1 = 9;
int Led2 = 10;
int Led3 = 11;
int Led4 = 12;
int Led5 = 13;

void setup()
{
  // 설정인데 안 건드는게 좋음. 잘 되있음.
  Serial.begin(9600);
  Serial.println("start");
  Serial.print(MPU6050_INT); Serial.print(" on pin "); Serial.print(MPU6050_INT_PIN);
  
  Mouse.begin();

  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  
  pinMode(4, INPUT); // 좌클릭 버튼
  pinMode(8, INPUT); // 우클릭 버튼
  pinMode(MPU6050_INT, INPUT);
  
  //attachInterrupt(MPU6050_INT, dmpDataReady, RISdlfhgk62ING);
  pinMode(Led1, OUTPUT);
  pinMode(Led2, OUTPUT);
  pinMode(Led3, OUTPUT);
  pinMode(Led4, OUTPUT);
  pinMode(Led5, OUTPUT);
}

void loop()
{
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  // 여기 위까지는 코드 건들지 않는 걸 추천. 여기 아래부터 수정 ㄱㄱ

  // 버튼의 센서값을 읽음 -> 누르면 1, 안 누르면 0
  int lb = digitalRead(4); // 좌클릭, 1이면 누른 상태, 0이면 안 누른 상태임.
  int rb = digitalRead(8); // 우클릭, 위와 마찬가지
  
  float gyroY, gyroZ;
  
  // 좌우클릭을 동시에 함 
  if (lb == 1 && rb == 1) {
    ++timer1;
    delay(20);
    // 1초 동안 동시에 좌우클릭 -> 민감도 설정 모드
    if (timer1 > 50) {
      Serial.println("민감도 설정 모드\n");
      delay(500); // 이거 없으면 민감도 설정 모드 바로 탈출하게 됨. 꼭 필요함.
      Serial.print("민감도:  ");
      Serial.println(sens_num);
      
      while(1) { 
        lb = digitalRead(4); // 좌클릭 
        rb = digitalRead(8); // 우클릭

        // 민감도 설정 모드 안에서 한번 더 좌우 클릭을 동시에 한 경우 
        if(lb == 1 && rb == 1) { 
          break; // 민감도 설정 모드 탈출 
        }        
        
        else if(lb == 0 && rb == 1) { 
          if(sens_num > 1) { 
            --sens_num; //분모가 커지니까 gyro값 작아짐 >> 민감도 작아짐  
            Serial.print("민감도:  ");
            Serial.println(sens_num);
            delay(500);
          }
        }
        
        else if(lb == 1 && rb == 0) {
          if(sens_num < 5) { 
            ++sens_num; //분모가 작아지니까 gyro값 커짐 >> 민감도 커짐
            Serial.print("민감도:  ");
            Serial.println(sens_num);
            delay(500);
          }    
        }
        
        switch(sens_num) {
           case 5: sens = 820; Sens_Led_On(sens_num); break;
           case 4: sens = 850; Sens_Led_On(sens_num); break;
           case 3: sens = 1100; Sens_Led_On(sens_num); break;
           case 2: sens = 1220; Sens_Led_On(sens_num); break;
           case 1: sens = 1380; Sens_Led_On(sens_num); break;
           default: break;
        }
        //Sprint2(timer1, sens, sens_num);
      }
      timer1 = 0;
      Serial.println("\n민감도 설정 완료!");
      Serial.print("현재 민감도: ");
      Serial.println(sens_num);     
      delay(500);
    }
  }
  // 좌우클릭을 1초 동안 하지 않은 경우
  else { 
    timer1 = 0; // timer1 변수 값 0으로 초기화
  }
  
  gyroZ = GyZ / sens;  // X축
  gyroY = -GyY / sens; // Y축
  
  // lb 가 1 -> 좌클릭을 한 경우
  if(lb == 1)  {
    // 마우스를 누른 상태로 유지시켜주는 Mouse.press()를 사용함.           
    Mouse.press(); // 좌클릭 한 상태 유지
  }

  // rb 가 1 -> 우클릭을 한 경우
  else if (rb == 1) {
    Mouse.click(MOUSE_RIGHT); // 우클릭
    delay(200);
  } 

  // 좌우클릭 둘 다 안했으므로 클릭 상태 해제
  else if (lb == 0 && rb == 0) {
    Mouse.release(); //클릭 상태 해제
  }

  Sprint1(gyroY, gyroZ, lb, rb); // 센서 값을 출력해줌
  //digitalWrite(8 + sens_num, HIGH); // 민감도에 대응되는 LED를 킴
  Mouse.move(gyroZ, gyroY); // 마우스를 움직여주는 함수
}

// 민감도에 따른 LED On/Off 함수
void Sens_Led_On(int sens_num) 
{
  // 우선 다 끄고 시작함
  digitalWrite(Led1, LOW);
  digitalWrite(Led2, LOW);
  digitalWrite(Led3, LOW);
  digitalWrite(Led4, LOW);
  digitalWrite(Led5, LOW);
  
  /*
  민감도에 대응되는 LED 하나만 켜야 됨.  
  8 + sens_num --> Led1,2,3,4,5 에 대응되는 9, 10, 11, 12, 13번 핀을 의미
  Example) Sens_num이 3일 때 8 + sens_num은 11 즉 Led3을 키게 된다. 
  다른 4가지 경우도 똑같이 작동함. 
  */
  digitalWrite(8 + sens_num, HIGH); // Led On
}

// 센서 값을 출력해줌는 함수(자이로, 버튼)
void Sprint1(int gyroY, int gyroZ, int lb, int rb)
{
  Serial.print("X축: "); 
  Serial.print(gyroZ);
  Serial.print(" // Y축: "); 
  Serial.print(-gyroY);
  Serial.print(" // 좌 클릭: ");
  Serial.print(rb);
  Serial.print(" // 우 클릭: ");
  Serial.println(lb);
}

 

대략적인 설명은 코드 안에 주석을 달아놓은 것 같다. 

조이스틱 마우스와 마찬가지로 에어 마우스도 아두이노 우노에서는 동작하지 않는다. 

무조건 레오나르도여야 된다. 

Loop 시작부터 GyZ=Wire.read()<<8 부분까지는 건들지 않는 게 좋을 것 같고

그 아래부터는 원하는 본인이 원하는 대로 바꿔서 사용하면 될 것 같다.  

github: https://github.com/kimjinho1/Air_Mouse/blob/master/Air_Mouse_V2_%2B.ino

 

kimjinho1/Air_Mouse

2019 Incheon University(INU) Capstone Design. Contribute to kimjinho1/Air_Mouse development by creating an account on GitHub.

github.com

조이스틱 마우스: https://jinho-study.tistory.com/246

 

아두이노 조이스틱 마우스

에어 마우스를 만들기 전에 만들었던 조이스틱 마우스이다.  /* 버튼 코드 참조: https://kikiai.tistory.com/29 마우스 코드 참조: https://www.arduino.cc/reference/ko/language/functions/usb/mouse/mousemov..

jinho-study.tistory.com

728x90
반응형

'Projects > 에어 마우스' 카테고리의 다른 글

아두이노 에어 마우스로 총게임 하기  (6) 2020.06.20
아두이노 조이스틱 마우스  (0) 2020.06.19
728x90
반응형

에어 마우스를 만들기 전에 만들었던 조이스틱 마우스이다.  

/* 버튼 코드 참조: https://kikiai.tistory.com/29
   마우스 코드 참조: https://www.arduino.cc/reference/ko/language/functions/usb/mouse/mousemove/
   위의 링크에 마우스 관련 함수 다 설명되있음. 
   조이스틱 코드 참조: http://blog.naver.com/PostView.nhn?blogId=boilmint7&logNo=220927175653
   조이스틱에도 버튼이 있지만 사용하기 힘들어서 안썼음.

   버튼: 핀 4개 있는데 2개 버리고 2개중 하나에 +5V, 하나는 그라운드랑 Pin2에 박으면 됨.
   
   조이스틱: 그라운드랑 +5V는 말 그대로 그라운드랑 +5V에 연결 
   VRX, VRY 는 각각 A0, A1에 연결함. 나는 조이스틱핀이 아래를 보는 방향으로 구현해서 x, y축이 바뀜.
   그래서 아래 코드 보면 A0, A1 순서가 다름. 
*/


#include <Mouse.h> // 마우스 헤더(레오나르도가 아니면 라이브러리 깔아도 오류 뜸!, 무조건 레오나르도로 ㄱㄱ)

void setup() {
  pinMode(2, INPUT); // 좌클릭 버튼
  pinMode(4, INPUT); // 우클릭 버튼
  Mouse.begin(); // 마우스 제어 시작
  Serial.begin(9600); //시리얼 통신을 시작
}

void loop() {/* 다 다르겠지만 나는 조이스틱 초기 값이 x: 517, y: 506 정도였음. 이게 의미하는건 
                중심 좌표가 (512, 512) 쯤이라는건데, 사용하기 불편하므로 중심을 (0, 0)로 만들었다. 
                200을 나눈건 마우스가 너무 큰 범위로 움직이게 되기 때문에 크기를 줄여줬다.*/
  int x = (analogRead(A1) - 517) / 260; /* 나누는 값은 맘대로 바꿔도 된다. 다만 500이 넘어가면 마우스가 움직이지 않게 된다.
                                         왜냐하면 (analogRead(A1) - 517)의 최대값이 500정도 되는데, 거기에 500을 나눠버리면 0이 되기 때문*/
  int y = - (analogRead(A0) - 506) / 260; /* 조이스틱을 위로 올리면 같이 +가 되야 되는데,
                                             -가 되길래 그냥 -곱해줬다.*/
  int Left_button = digitalRead(2); // 1이면 누른 상태, 0이면 안 누른 상태임.
  int Right_button = digitalRead(4); // 위와 마찬가지
  Sprint(x, y, Left_button, Right_button); // 시리얼 프린트
  
  if (Left_button == 1) // Left button이 1이라는건 좌클릭을 하고 있다는 상태이므로 마우스를 누른 상태로 유지시켜주는 Mouse.press()를 사용함
  {
    Mouse.press(MOUSE_LEFT); // 좌클릭 한 상태 유지
  }
  else if (Left_button == 0 && Right_button == 0) 
  {
    Mouse.release(); //클릭 상태 해제
  }
  else if (Right_button == 1) // 우클릭
  {
    Mouse.click(MOUSE_RIGHT);
  } 
  Mouse.move(x, y); // 마우스를 움직이는 함수
}

// 루프안에 여러줄 있는거 꼴보기 싫어서 함수로 만듬. 별 의미 없음.
void Sprint(int x, int y, int lb, int rb) 
{
  Serial.print("X: ");
  Serial.print(x);
  Serial.print(' ');
  Serial.print("Y: ");
  Serial.print(-y); // 시리얼프린트에 -로 출력되길래 부호 바꿔줌.
  Serial.print(' ');
  Serial.print("Left_button: ");
  Serial.print(lb);
  Serial.print(' ');
  Serial.print("Rigft_button: ");
  Serial.println(rb);
}

 

대략적인 설명은 코드 안에 주석을 달아놓은 것 같다. 

아두이노 우노로는 마우스를 제어할 수가 없다. 무조건 레오나르도여야 한다.

github: https://github.com/kimjinho1/Air_Mouse/blob/master/Joystick_Mouse.ino

 

kimjinho1/Air_Mouse

2019 Incheon University(INU) Capstone Design. Contribute to kimjinho1/Air_Mouse development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

이걸 맨 처음에 올렸어야 했는데 나중에 생각나서 뒤늦게 올리게 되었다.

import numpy as np
import cv2

cap = cv2.VideoCapture(0) # 노트북 웹캠을 카메라로 사용
cap.set(3,640) # 너비
cap.set(4,480) # 높이

ret, frame = cap.read() # 사진 촬영
frame = cv2.flip(frame, 1) # 좌우 대칭

cv2.imwrite('self camera test.jpg', frame) # 사진 저장
    
cap.release()
cv2.destroyAllWindows()

위의 코드를 실행하면 self camera test.jpg라는 파일이 저장된다.

파일을 열어보면 아래와 같이 정상적으로 셀카가 찍히는 것을 확인할 수 있다. 

내 얼굴 데이터를 모으려면 핸드폰으로 직접 사진을 찍어야 되는데 그게 너무 귀찮을 것 같아서 만들게 되었다.

OpenCV를 이용한 셀카

github: https://github.com/kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning/blob/master/0.%20opencv_self_camera.ipynb

 

kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning

딥 러닝을 활용한 실시간 얼굴 인식과 모자이크 처리. Contribute to kimjinho1/Real-time-face-recognition-and-mosaic-using-deep-learning development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

저번에는 실시간으로 얼굴 탐지를 하고 탐지된 얼굴들을 모두 모자이크 처리했었다.

이번에는 럭키짱의 강건마 이미지를 사용해 모자이크 처리를 해볼 것이다.

전에 했던 내용은 설명하지 않을 것이니 이해가 안 되는 사람은 이전 글을 참고하도록 하자!

3.1 OpenCV를 이용한 실시간 얼굴 모자이크 처리: https://jinho-study.tistory.com/231

 

3.1 OpenCV를 이용한 실시간 얼굴 모자이크 처리

저번에는 실시간 얼굴 탐지를 했었다. 이번에는 실시간으로 얼굴 탐지를 하고 탐지된 모든 얼굴 영역을 각각 탐지된 얼굴 이미지를 축소-확대한 이미지로 대체하는 방식으로 모자이크 처리

jinho-study.tistory.com

아래는 전체 코드를 정리한 코드이다.

import numpy as np
import cv2

xml = 'haarcascades/haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(xml)

mosaic_img = cv2.imread('image/mosaic.png') # 럭키짱의 강건마 이미지를 저장

cap = cv2.VideoCapture(0) # 노트북 웹캠을 카메라로 사용
cap.set(3,640) # 너비
cap.set(4,480) # 높이

while(True):
    ret, frame = cap.read() 
    frame = cv2.flip(frame, 1) # 좌우 대칭
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray,1.05,5)
    print("Number of faces detected: " + str(len(faces)))

    if len(faces):
        for (x,y,w,h) in faces:
            """ 탐지된 얼굴 영역을 강건마 이미지로 바꾸는 코드"""
            # 탐지된 얼굴 영역에 맞도록 강건마 이미지의 높이와 너비를 바꿔줌.
            t = cv2.resize(mosaic_img, dsize=(h, w), interpolation=cv2.cv2.INTER_LINEAR)
            frame[y:y+h, x:x+w] = t # 탐지된 얼굴 영역을 강건마 이미지로 모자이크 처리

            """ 사람의 어깨선과 머리선이 강건마 이미지와 딱 맞게 이어지도록 구현한 코드
                문제는 나한테만 딱 맞는다... """
#             wi, hi = int(w*0.08/2), int(h*0.23/2)
#             t = cv2.resize(mosaic_img, dsize=(w+wi*2, h+hi*2), interpolation=cv2.cv2.INTER_LINEAR)
#             frame[y-hi:y+h+hi, x-wi:x+w+wi] = t

    cv2.imshow('result', frame)
        
    k = cv2.waitKey(30) & 0xff
    if k == 27: # Esc 키를 누르면 종료
        break

cap.release()
cv2.destroyAllWindows()

모자이크 처리하는 기능을 조금 다르게 2개를 구현했는데 위는 탐지된 얼굴 영역을 강건마 이미지로 바꿔주고,

아래는 사람의 어깨선과 머리선이 강건마 이미지와 딱 맞게 이어지도록 강건만 이미지의 크기를 바꿔줬다.

근데 문제는 나한테만 딱 맞는다...

위의 코드를 실행하면 아래와 같이 실시간으로 탐지된 얼굴이 강건마로 잘 바뀌고 있는 것을 확인할 수 있다. 

내 얼굴 강건마

내 얼굴 기준으로는 말 그대로 기적의 비율을 보여준다.

너네 얼굴 강건마

여러 명 있어도 아주 잘 된다.

github: https://github.com/kimjinho1/Real-time-specific-object-mosaic-using-deep-learning/blob/master/3.2%20Real-time_face_mosaic_with_lucky-jjang.ipynb

 

kimjinho1/Real-time-specific-object-mosaic-using-deep-learning

딥러닝을 이용한 실시간 특정 객체 모자이크 처리 . Contribute to kimjinho1/Real-time-specific-object-mosaic-using-deep-learning development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

저번에는 실시간 얼굴 탐지를 했었다. 이번에는 실시간으로 얼굴 탐지를 하고 탐지된 모든 얼굴 영역을

각각 탐지된 얼굴 이미지를 축소-확대한 이미지로 대체하는 방식으로 모자이크 처리를 해볼 것이다.  

전에 했던 내용은 설명하지 않을 것이니 이해가 안 되는 사람은 이전 글을 참고하도록 하자!

2. OpenCV를 이용한 실시간 얼굴 탐지: https://jinho-study.tistory.com/230

 

2. OpenCV를 이용한 실시간 얼굴 탐지

저번에는 단순히 이미지로 얼굴을 탐지했었다. 이번에는 노트북 웹캠을 통해 실시간으로 얼굴 탐지를 해볼 것이다. 전에 했던 내용은 설명하지 않을 것이니 이해가 안 되는 사람은 이전 글을 참

jinho-study.tistory.com

아래는 전체 코드를 정리한 코드이다.

import numpy as np
import cv2

xml = 'haarcascades/haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(xml)

cap = cv2.VideoCapture(0) # 노트북 웹캠을 카메라로 사용
cap.set(3,640) # 너비
cap.set(4,480) # 높이

while(True):
    ret, frame = cap.read() 
    frame = cv2.flip(frame, 1) # 좌우 대칭
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray,1.05,5) 
    print("Number of faces detected: " + str(len(faces)))

    if len(faces):
        for (x,y,w,h) in faces:
            face_img = frame[y:y+h, x:x+w] # 탐지된 얼굴 이미지 crop
            face_img = cv2.resize(face_img, dsize=(0, 0), fx=0.04, fy=0.04) # 축소
            face_img = cv2.resize(face_img, (w, h), interpolation=cv2.INTER_AREA) # 확대
            frame[y:y+h, x:x+w] = face_img # 탐지된 얼굴 영역 모자이크 처리

    cv2.imshow('result', frame)
        
    k = cv2.waitKey(30) & 0xff
    if k == 27: # Esc 키를 누르면 종료
        break

cap.release()
cv2.destroyAllWindows()

얼굴 영역에 박스를 치는 코드가 빠졌고, 모자이크 처리를 해주는 코드 4줄이 추가되었다. 

face_img = frame[y:y+h, x:x+w]  -> 탐지된 얼굴 영역의 이미지를 face_img에 저장

face_img = cv2.resize(face_img, dsize=(0, 0), fx=0.04, fy=0.04) -> face_img의 높이와 너비를 0.04배 해줌

face_img = cv2.resize(face_img, (w, h), interpolation=cv2.INTER_AREA) -> face_img를 원래 비율로 되돌려준다. 이 과정에서 이미지가 깨짐

frame[y:y+h, x:x+w] = face_img -> 탐지된 얼굴 영역을 face_img로 바꿔줌

위의 코드를 실행하면 아래와 같이 실시간으로 탐지된 얼굴이 잘 모자이크 되고 있는 것을 확인할 수 있다. 

실시간 얼굴 모자이크 처리

github: https://github.com/kimjinho1/Real-time-specific-object-mosaic-using-deep-learning/blob/master/3.1%20Real-time_face_mosaic.ipynb

 

kimjinho1/Real-time-specific-object-mosaic-using-deep-learning

딥러닝을 이용한 실시간 특정 객체 모자이크 처리 . Contribute to kimjinho1/Real-time-specific-object-mosaic-using-deep-learning development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

저번에는 단순히 이미지로 얼굴을 탐지했었다. 이번에는 노트북 웹캠을 통해 실시간으로 얼굴 탐지를 해볼 것이다. 

전에 했던 내용은 설명하지 않을 것이니 이해가 안 되는 사람은 이전 글을 참고하도록 하자!

1. OpenCV를 이용한 얼굴 탐지: https://jinho-study.tistory.com/229?category=926937

 

1. OpenCV를 이용한 얼굴 탐지

요즘 실시간 특정 객체 모자이크 프로그램을 구현해보고 있는데, 관련 내용들과 코드를 하나씩 천천히 올리게 될 것 같다. 맨 첫 번째 과정은 얼굴 탐지이다. 얼굴 탐지를 하기 위해선 opencv에 있

jinho-study.tistory.com

아래는 전체 코드를 정리한 코드이다.

import numpy as np
import cv2

xml = 'haarcascades/haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(xml)

cap = cv2.VideoCapture(0) # 노트북 웹캠을 카메라로 사용
cap.set(3,640) # 너비
cap.set(4,480) # 높이

while(True):
    ret, frame = cap.read()
    frame = cv2.flip(frame, 1) # 좌우 대칭
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray,1.05, 5)
    print("Number of faces detected: " + str(len(faces)))

    if len(faces):
        for (x,y,w,h) in faces:
            cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
        
    cv2.imshow('result', frame)
    
    k = cv2.waitKey(30) & 0xff
    if k == 27: # Esc 키를 누르면 종료
        break

cap.release()
cv2.destroyAllWindows()

 

얼굴 탐지하는 부분은 전과 똑같고, 추가된 부분은 cv2.VideoCapture, cap.set, cv2.flip, while 문과 break이다.

cv2.VideoCapture는 비디오 캡처 객체를 생성해 주는데, 안의 숫자는 장치 인덱스(어떤 카메라를 사용할 것인가)이다. 1개만 부착되어 있으면 0, 2개 이상이면 첫 번째 웹캠은 0, 두 번째 웹캠은 1로 지정하면 된다. 난 노트북 웹캠을 사용할 것이기 때문에 그냥 0이면 된다.

cap.set은 프레임의 크기를 설정해준다.

cv2.flip은 이미지를 좌우, 상하 대칭을 해준다. 안에 인자가 1이면 좌우, 2이면 상하 대칭이다. cv2.flip은 필수는 아니다.

위의 코드를 실행하면 아래와 같이 실시간으로 얼굴 탐지를 할 수 있다. Esc키를 누르면 종료된다.

실시간 얼굴 탐지

github: https://github.com/kimjinho1/Real-time-specific-object-mosaic-using-deep-learning/blob/master/2.%20Real-time_face_detection.ipynb

 

kimjinho1/Real-time-specific-object-mosaic-using-deep-learning

딥러닝을 이용한 실시간 특정 객체 모자이크 처리 . Contribute to kimjinho1/Real-time-specific-object-mosaic-using-deep-learning development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

요즘 실시간 특정 객체 모자이크 프로그램을 구현해보고 있는데, 관련 내용들과 코드를 하나씩 천천히 올리게 될 것 같다.

맨 첫 번째 과정은 얼굴 탐지이다. 얼굴 탐지를 하기 위해선 opencv에 있는 haarcascades 파일이 필요한데

아래 github에서 다운로드 가능하다.

https://github.com/opencv/opencv/tree/master/data/haarcascades

 

opencv/opencv

Open Source Computer Vision Library. Contribute to opencv/opencv development by creating an account on GitHub.

github.com

필요한 라이브러리: Numpy, OpenCV, matplotlib

우선 라이브러리를 import 한다. matplotlib은 이미지 확인용이라 필수는 아닙니다!

import numpy as np
import cv2
from matplotlib import pyplot as plt
%matplotlib inline

 

그다음은 얼굴 탐지를 할 이미지를 불러온다. 저는 기생충의 기정, 기우의 이미지를 사용했습니다. 

image = cv2.imread('image/bong.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

plt.imshow(gray, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.show()

위의 코드를 실행하면 아래와 같이 이미지를 무사히 불러온 것을 확인할 수 있다.

기정과 기우

그다음은 haarcascades 파일을 사용해서 얼굴 탐지를 해보자.

haarcascades 파일에는 다양한 xml 파일이 들어 있는데 이것들을 사용해서 사람의 얼굴, 눈, 전신 등등을 탐지할 수 있다.

haarcascades

우리의 목표는 얼굴 탐지이므로 haarcascade_frontalface_default.xml 파일을 사용하도록 하자.

xml = 'haarcascades/haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(xml)
faces = face_cascade.detectMultiScale(gray, 1.2, 5)

print("Number of faces detected: " + str(len(faces)))

위의 코드를 실행하면 아래와 같이 사람 얼굴 2개를 탐지했음을 확인할 수 있다.

얼굴 2개 탐지

현재 faces 변수 안에는 아래와 같이 x, y축 좌표값과 얼굴의 높이와 너비인 w, h 값이 리스트 형태로 들어있다. 

마지막으로 이상한 곳을 얼굴로 탐지하지 않고 정확하게 탐지했는지 확인을 해보자. 

cv2.rectangle과 faces에 들어있는 x, y, w, h 값을 통해 이미지에 박스를 치는 방식으로 확인을 한다.

if len(faces):
    for (x,y,w,h) in faces:
        cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,0),2)

plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB), cmap='gray')
plt.xticks([]), plt.yticks([]) 
plt.show()

위의 코드를 실행하면 아래와 같이 기정과 기우의 얼굴을 정확하게 탐지했음을 확인할 수 있다.

기정과 기우의 얼굴 탐지 성공

아래는 전체 코드를 정리한 코드이다. 이 코드를 보면 얼굴 탐지가 정말 짧은 코드로 가능하다는 것을 알 수 있다.

import numpy as np
import cv2
from matplotlib import pyplot as plt
%matplotlib inline

image = cv2.imread('image/bong.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

xml = 'haarcascades/haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(xml)
faces = face_cascade.detectMultiScale(gray, 1.2, 5)

print("Number of faces detected: " + str(len(faces)))

if len(faces):
    for (x,y,w,h) in faces:
        cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,0),2)

plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB), cmap='gray')
plt.xticks([]), plt.yticks([]) 
plt.show()

 

github: https://github.com/kimjinho1/Real-time-specific-object-mosaic-using-deep-learning/blob/master/1.%20Face_detection.ipynb

 

kimjinho1/Real-time-specific-object-mosaic-using-deep-learning

딥러닝을 이용한 실시간 특정 객체 모자이크 처리 . Contribute to kimjinho1/Real-time-specific-object-mosaic-using-deep-learning development by creating an account on GitHub.

github.com

728x90
반응형

+ Recent posts