Opencv 이용

더보기
def to_padding(image):
    height = image.shape[0]
    width = image.shape[1]
    top, bottom, left, right = 0, 0, 0, 0

    if height > width:
        left, right = (height - width) // 2, (height - width) // 2
    else:
        top, bottom = (width - height) // 2, (width - height) // 2

    new_image = cv.copyMakeBorder(image, top, bottom, left, right, cv.BORDER_CONSTANT, value=(0, 0, 0))

    # cv.imshow('test', new_image)
    # cv.waitKey(0)

    return new_image

 

Opencv 없이 붙이기

더보기
def resize_aspect_ratio(img, final_h, final_w):
    height, width, channel = img.shape
    ratio = 0
    # height = 8914
    # width = 6457
    target_w_size = final_w
    target_h_size = final_h

    h_ratio = target_h_size / height
    target_h, target_w = int(height * h_ratio), int(width * h_ratio)
    diff = target_w_size - target_w
    if diff > 0:
        resized = cv2.resize(img, (target_w, target_h), interpolation=interpolation)
        pad_array = np.ones(shape=(target_h_size, diff, 3), dtype=np.uint8)
        # pad_array = np.full(shape=(target_h_size, diff, 3), 255)
        resized = np.append(resized, pad_array, axis=1)
        ratio = h_ratio
    else:
        w_ratio = target_w_size / width
        target_h, target_w = int(height * w_ratio), int(width * w_ratio)
        diff = target_h_size - target_h
        resized = cv2.resize(img, (target_w, target_h), interpolation=interpolation)
        pad_array = np.ones(shape=(diff, target_w_size, 3), dtype=np.uint8)
        resized = np.append(resized, pad_array, axis=0)
        ratio = w_ratio

    cv2.imshow('test', resized)
    cv2.waitKey(0)

여기서

target_w_size, target_h_size가 원하는 이미지의 크기이고,

이미지가 비율을 일정하게 유지하면서 padding을 붙여서 원하는 크기의 이미지로 resize하는 코드

'기록해둘 코드 > python' 카테고리의 다른 글

Opencv를 이용하여 Fafce Align 하기!  (1) 2022.09.29

감정 인식을 위해 데이터 전처리 과정에서 쓰일 수도 있기 때문에 기록해둡니다.

 

Opencv 에는 여러가지 기능을 가지고 있는데 그 중에서 Haarcascade를 이용하여 진행하겠습니다.

 

이런식으로 detector를 선언해줄 수 있습니다.

 

저는 face align을 위해 눈만 detection 하겠습니다.

 

원래는 얼굴 검출 후 눈을 검출하는 단계로 진행해야하지만 제가 가지고 있는 Data는 crop된 얼굴 이미지 형태이기 때문에 face detection은 제외하겠습니다.

 

 먼저 eye_detect.detectMultiScale(img_gray, scaleFactor, minNeighbors) 를 통해 나오는 결과값을 살펴보면

 

눈의 (x좌표, y좌표, w, h) 순서대로 되어있습니다.

 

위 코드에서는 x좌표를 이용하여 왼쪽, 오른쪽 눈을 지정해줍니다.

 

그 이유는 눈을 기준으로 alignment를 진행해주기 위해서 입니다.

 

위 코드에서는 왼쪽, 오른쪽 눈의 중심점을 찾기 위해 위와 같은 연산을 진행해줍니다.

 

세번째 포인트를 계산해 줍니다.

 

세번째 포인트를 지정해주는 이유는 왼쪽과 오른쪽 눈의 회전되는 각도를 알기 위해서 입니다.

 

 

여기서 a, b, c의 길이는 좌표값을 알고 있기 때문에 피타고라스 정리를 통하여 유클리드 거리를 구할 수 있습니다.

 

 

그 후, 각도를 구하기 위해서 코사인 제 2 법칙을 사용 후 역삼각함수를 이용합니다.

 

 

만약 시계방향으로 이미지를 회전하려면 90도에서 찾은 각도를 뺀 각도로 이미지를 회전시킵니다.

 

 

위 그림으로 왜 그런지 이해할 수 있을 것입니다.

 

이제 이미지를 회전시켜주면 끝입니다!

 

Full Code

더보기
def face_alignment(img, face_detect, eye_detect):
    first_show = True
    show_img = False
    last_show = False
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    eyes_list = eye_detect.detectMultiScale(img_gray)

    eyes = eye_detect.detectMultiScale(img_gray, scaleFactor=1.2, minNeighbors=2)
    # if len(eyes_list) >= 3:
    #     max_y = []
    #     for idx in range(len(eyes_list)):
    #         max_y.append(eyes_list[idx][1])
    #
    #     idx_list = []
    #     for i in range(2):
    #         idx_list.append(max_y.index(min(max_y)))
    #         max_y[max_y.index(min(max_y))] = 1000
    #         eyes.append(eyes_list[idx_list[i]])

    index = 0
    for (eye_x, eye_y, eye_w, eye_h) in eyes:
        if index == 0:
            eye_1 = (eye_x, eye_y, eye_w, eye_h)
        elif index == 1:
            eye_2 = (eye_x, eye_y, eye_w, eye_h)
        index = index + 1
        if first_show:
            cv.rectangle(img, (eye_x, eye_y), (eye_x + eye_w, eye_y + eye_h), (255, 255, 255), 2)

    if first_show:
        cv.imshow('test', img)
        cv.waitKey(0)

    if eye_1[0] < eye_2[0]:
        left_eye = eye_1
        right_eye = eye_2
    else:
        left_eye = eye_2
        right_eye = eye_1

    left_eye_center = (int(left_eye[0] + (left_eye[2] / 2)), int(left_eye[1] + (left_eye[3] / 2)))
    left_eye_x = left_eye_center[0]
    left_eye_y = left_eye_center[1]

    right_eye_center = (int(right_eye[0] + (right_eye[2] / 2)), int(right_eye[1] + (right_eye[3] / 2)))
    right_eye_x = right_eye_center[0]
    right_eye_y = right_eye_center[1]

    if show_img:
        cv.circle(img, left_eye_center, 2, (255, 0, 0), 2)
        cv.circle(img, right_eye_center, 2, (255, 0, 0), 2)
        cv.line(img, right_eye_center, left_eye_center, (67, 67, 67), 2)
        cv.imshow('test', img)
        cv.waitKey(0)

    if left_eye_y < right_eye_y:
        point_3rd = (right_eye_x, left_eye_y)
        direction = -1  # rotate same direction to clock
        print("rotate to clock direction")
    else:
        point_3rd = (left_eye_x, right_eye_y)
        direction = 1  # rotate inverse direction of clock
        print("rotate to inverse clock direction")

    if show_img:
        cv.circle(img, point_3rd, 2, (0, 255, 0), 2)

        cv.line(img, right_eye_center, left_eye_center, (67, 67, 67), 1)
        cv.line(img, left_eye_center, point_3rd, (67, 67, 67), 1)
        cv.line(img, right_eye_center, point_3rd, (67, 67, 67), 1)
        cv.imshow('test', img)
        cv.waitKey(0)

    a = euclidean_distance(left_eye_center, point_3rd)
    b = euclidean_distance(right_eye_center, left_eye_center)
    c = euclidean_distance(right_eye_center, point_3rd)

    cos_a = (b * b + c * c - a * a) / (2 * b * c)
    angle = np.arccos(cos_a)
    angle = (angle * 180) / math.pi

    print("cos(a) = ", cos_a)
    print("angle: ", angle, " in radian")
    print("angle: ", angle, " in degree")

    if direction == -1:
        angle = 90 - angle
    if point_3rd[0] == left_eye_x and point_3rd[1] < left_eye_y:
        angle = -angle
    elif point_3rd[0] == right_eye_x and point_3rd[1] < right_eye_y:
        angle = -angle

    new_img = Image.fromarray(img)
    new_img = np.array(new_img.rotate(direction * angle))

    if last_show:
        cv.imshow('test', new_img)
        cv.waitKey(0)

    return new_img

 

 

출처 : https://a292run.tistory.com/entry/Face-Alignment-for-Face-Recognition-in-Python-within-OpenCV-1

'기록해둘 코드 > python' 카테고리의 다른 글

Padding 붙이기  (0) 2022.09.29

 학습 방법

학습 방법

Bagging 방법은 각각의 모델을 독립적으로 학습 시킵니다.

하지만 Boosting은 이전 모델의 학습 결과를 통해 더욱 개선된 모델을 만듭니다.

이전 모델의 학습 결과를 통해 영향을 주는 방법은

분류가 어려운 사례를 강조하기 위해 잘못 분류한 데이터에 가중치를 주어 더 잘 학습하도록 만듭니다.

즉, 잘못 분류한 데이터일수록 샘플링 될 확률이 늘어나게 됩니다.

 


 

결과 도출 방법

 

 

  1.  Bagging
    • 각각의 Tree를 정직하게 평균을 내서 사용
    • 일반적으로 알고 있는 Soft Voting, Hard Voting 방법 사용
  2. Boosting
    • 각각의 Tree의 학습 결과에 따른 Weight(가중치)를 부여
    • 더 잘 학습한 Tree 일수록 더 많은 Weight(가중치)를 주어서 결과를 도출
    • 더 좋은 성능을 낼 수 있지만 Over Fitting의 위험이 있다.

 

 

 

 

 - 출처 : https://wyatt37.tistory.com/1

from openpyxl import load_workbook
import os

if __name__ == "__main__":
    wb = load_workbook("C:/Users/TEST/Desktop/Data/test.xlsx")  # 작성할 엑셀 파일 불러오기

    ws = wb.active  # 첫번째 Sheet 불러오기
    IDCol = 'A'     # ID 적을 Col
    nameCol = 'B'   # NAME 적을 Col
    emoCol = 'G'    # Col
    angleCol = 'H'  # Col
    ROW = 3         # 시작할 행

    file_list = os.listdir("C:/Users/TEST/Desktop/Image_Folder/") # 엑셀에 적을 Data 경로 적어주기

    for file in file_list:
        if file.count(".") == 1:
            name = file.split('.')[0]
            ID = name.split('_')[0]
            emotion = name.split('_')[3]
            ANGLE = name.split('_')[5]
            print(ID)
            print(name)
            ws[IDCol + str(ROW)] = ID
            ws[nameCol + str(ROW)] = name
            ws[emoCol + str(ROW)] = emotion
            ws[angleCol + str(ROW)] = ANGLE
            ROW += 1

    wb.save("C:/Users/TEST/Desktop/Data/test_result.xlsx")     # 엑셀 저장해주기

객체에 방향키를 누르면 움질일 수 있게 만들 수 있는 방법은 여러가지가 있습니다.

 

그 중 Rigidbody의 AddRelativeForce를 사용하는 방법이 있고,

 

Input.GetAxis("Horizontal"), Input.GetAxis("Vertical") 를 사용하는 방법도 있고,

 

또 하나는 transform을 가져와서 사용하는 방법이 있습니다.

 

이번에는 위 세가지 방법 말고 InputSystem을 사용하는 방법을 알아보겠습니다.

 

 

 

InputSystem

 

먼저 Window의 Package Manager를 열어주고

 

InputSystem을 검색해 준 후에 import 해줍니다.

더보기

그 후에 Edit -> Project Settings로 들어가 주고 Player의 Active Input Handling을 봐줍니다.

 

우선 비행선의 움직임을 위해서 Script를 생성해주고 비행선에 Script를 Add Component 해줍니다.

 

그 후에 Script 내부에 using UnityEngine.InputSystem; 을 선언해 준 후에 

 

[SerializeField] InputAction movement; 를 선언해줍니다.

 

그 후에 Unity Hub로 다시 돌아가서

 

Script를 추가한 비행체를 클릭하여 빨간 박스의 오른쪽 위에 있는 Add Binding을 눌러준 후

 

3번째(2D Vector)를 눌러주고 단축키를 지정해주면 됩니다. (더블클릭 -> Path -> Listen -> 단축키 누르기 -> 설정해주기)

 

Script 에서는 Update의 29, 32 번째 줄처럼 사용하면 방향키가 눌리는 값을 가져올 수 있습니다.

 

 

 

Update Position

 

방향키를 누름으로써 값들을 가져오게 됐다면

 

이제 비행선을 움직이게 해주어야 합니다.

 

Vector3인 이유는 비행선이 3D 공간에서 움직이기 때문입니다.

 

x, y, z 순으로 입력 데이터 값을 가지며 새로운 위치값을 순서에 맞게 넣어주면 됩니다.

 

값을 넣어줄때 역시 프레임을 고려하여 Time.deltaTime 값을 곱해주게 됩니다.

 


Time.detaTime을 사용하는 이유!!

deltaTime

Unity는 위와 같이 deltaTime을 '전 frame이 완료되기까지의 시간'이라고 설명하고 있습니다.

ex) 하나의 frame이 완료되기까지의 시간이 5면 deltaTime = 5

 

하나의 frame당 1m를 가는 총알이 있다고 가정하겠습니다.

 

초당 10프레임을 찍어내는 10FPS 유저는 총알이 3m 가는 동안

 

20FPS 유저는 초당 2배의 프레임을 찍어내므로 6m를 가겠죠. 

 

이렇게 되면 20프레임 유저의 총알이 더 빠르므로 10프레임 유저는 걸레짝이 될 수 밖에 없습니다. 

 

그렇다면 여기서 deltaTime은 1초에 10프레임을 찍으니 한 프레임당 1/10초 이것이 바로 deltaTime의 개념이 되는 것 입니다. 20프레임이면 1/20초 가 되는 것이죠.

 

이 값을 총알 이동거리에 곱해주면 10프레임 컴퓨터에서는 한 프레임당 1m 날라가던 총알은 0.1m, 20프레임 컴퓨터는 0.05m를 날라가므로 실제적으로 결과 값은 동일하게 나오는 것입니다.

 

비록 10프레임 컴퓨터는 20프레임 컴퓨터보다 한 프레임에 더 먼 거리를 날아가므로 뚝뚝 끊길 수는 있지만, 두 유저의 총알 속도는 같아진 것입니다.

 

※ 출처 : https://itmining.tistory.com/46


 

A, D 방향키로 움직일 수 있게 된 비행선을 볼 수 있습니다.

 

controlSpeed는 [SerializeField]로 선언해주어서 적절한 값을 정해주었습니다.

 

Timeline 만들기

지난 게시물에서 만든 맵에 비행체를 대신해서 사용할 Cube를 하나 만들어주고

 

Main Camera도 Cube에 맞게 위치를 조정해줍니다.

 

그 후 Player라는 Empty Object를 만들어서 Cube와 Main Camera를 같이 넣어주었습니다.

 

그리고 발사대를 만들때 Terrain의 Paint Terrain -> Set Height 옵션을 이용하는데

 

Shift를 누른 상태로 원하는 높이를 왼쪽 클릭 해준 후에 다른 곳을 누르면 그 높이로 맞춰집니다!

 

그 다음 비행체의 동선을 만들어 주기 위해 Timeline과 Animation 창을 킨 후에 아래에 추가해주었습니다.

 

Master Timeline을 클릭한 후에 Timeline에서 Create를 눌러준 후 Assets 폴더 내부에 Timeline 폴더에 저장해주겠습니다.

 

Timeline을 보면 위와 같은 형태가 나타나게 됩니다.

 

그 후 + 버튼을 누른 후에 Animaition Track을 눌러줍니다.

 

이제 Player Rig을 Drag해서 왼쪽 사진과 같이 None (Animator) 에 넣어줍니다.

 

그러면 Player Rig에 Animator라는 Component가 생성됩니다.

 

이제 비행체가 날아가는 방향을 지정해보겠습니다.

 

녹화 버튼을 눌러줍니다.

 

오늘쪽의 하얀색 박스시간 or Frame (설정 가능) 에 따른 객체의 행동을 지정해주는 기준이라고 보시면 됩니다.

 

녹화 버튼을 눌러주면 버튼이 깜빡깜빡 거리게 되고 Player를 살짝 왔다 갔다 해줍니다.

 

그러면 Key가 생성이 됩니다.

 

즉, 기준(하얀색 박스) 위치를 조절한 후객체의 위치를 변경해주면 Key가 생성됩니다.

 

이러한 작업을 해주게 되면 시간에 따른 객체의 움직임을 만들어줄 수 있습니다.

 

원하는 움직임을 만들어 준 후에 Play를 눌러보겠습니다.

 

 

저는 Sphere를 만들어서 부딪힐랑 말랑하게 만들었습니다!

 

이렇게 Timeline 만드는 게시글은 마무리 하겠습니다.

Tree 만들어 주기

 

이번에는 지난 게시물에 이어서 Terrain에 나무를 만들어 보겠습니다!

 

지난 게시물과 같이 Assets Store에서 무료로 제공하는 Assets를 다운받아 주시기 바랍니다.

 

저는 Standard Assets를 다운받아 사용하겠습니다.

 

Package Manager에서 다운받은 Assets을 Import 하시는거 잊지 마세요!

 

이후 Terrain을 클릭해주시고

 

Paint Trees를 클랙해 줍니다.

 

그 후에 Edit Trees...을 클릭해주시고 Add Trees...을 클릭해주세요!

 

그러면 아래와 같은 창이 뜨게 됩니다.

 

 

이 창이 떴다면 빨간 박스의 ◎ 모양을 클릭해 줍시다.

 

그러면 저희가 다운받은 여러가지의 형태들을 볼 수 있는데 여기서 나무 모형을 더블 클릭Add 해주시면 됩니다.

 

 

다음은 Terrain에 Texture 입힌 것처럼 똑같이 사용해 주시면 됩니다!

 

나무까지 추가한 Terrain의 모습

 

 

Project 만들기

새로운 프로젝트를 만들어 줍니다.

 

Unity Hub를 실행시키면 나오는 화면입니다.

 

새로생성 및 오른쪽 ↓ 화살표 모양을 눌러서 원하는 Unity 버전으로 프로젝트를 생성해줄 수 있습니다.

 

 


Terrain 만들기

 

Left - Terrain 생성, Right - Terrain 생성된 모습

 

왼쪽 Hierarchy 창에서 빈 공간에 우클릭을 해준 후에 Terrain을 만들어 줍니다.

 

여기서 잠깜 Terrain과 Plane의 차이점을 말씀드리겠습니다.

  • Plane : 평면
  • Terrain : 지형

 

 


Terrain 다루기

 

크기 변경하기

Terrain을 클릭한 후 오른쪽의 Inspector를 보면 Transform 밑에 Terrain이 있을겁니다.

Terrain에는 총 5가지의 카테고리가 있습니다.

  • Create Neighbor
  • Paint Terrain
  • Paint Trees
  • Paint Details
  • Terrain Settings

Terrain Settings를 눌러준 후 밑으로 내리다 보면

Mesh Resolution (On Terrain Data) 를 보시면 크기를 변경할 수 있습니다.

 

 


Terrain 지형 변경하기

이제 Terrain의 Inspector 메뉴 중 Paint Terrain을 눌러준 후 Raise or Lower Terrain을 클릭해줍니다.

Paint Terrain

그 상태로 Terrain 위에

  • 좌클릭 : 지형 상승
  • Shift + 좌클릭 : 지형 하강

 

물론 Brush의 크기 및 지형의 상승, 하강 속도를 변경할 수 있습니다.

  • Brush Size : Brush 크기
    • ] : Brush 커짐
    • [ : Brush 작아짐
  • Opacity : 지형 상승 및 하강 속도

 

일정 크기의 높이 지정도 해줄 수 있습니다.

Set Height를 클릭해준 후 Height 값을 지정해주고 좌클릭을 하게 되면

 

지정한 높이까지 지형이 변경이 됩니다.

 

Flatten All을 눌러주게 되면 Terrain 전체의 높이가 지정한 높이로 변경됩니다.

 

그 후, 다시 Raise or Lower Terrain 클릭 후, 지형 하강을 하게 되면 아래의 이미지같은 골짜기가 생성됩니다.

 

 

Map

위의 기능들을 활용해서 산으로 둘러싸인 필드를 만들어 주었습니다.

 

 


Texture 입히기

 

Asset Store

먼저 Window -> Asset Store -> Search online 으로 들어가겠습니다.

 

https://assetstore.unity.com/packages/3d/environments/landscapes/terrain-sample-asset-pack-145808

 

여러가지 Tool들을 사용할 수 있지만 Unity에서 무료로 제공하는 Tool을 사용하겠습니다.

 

Asset을 다운로드 후 내 에셋에 추가하기를 눌러줍니다.

 

이때 Unity 아이디가 필요하므로 생성해주시길 바랍니다.

 

Package Manager

Window -> Package Manager 들어가신 후에 My Assets으로 들어가 로그인을 해줍니다.

 

로그인을 한 후 Download 를 눌러줍니다.

 

Download가 완료되면 Import 해주면 됩니다.

 

Import가 완료되면 왼쪽 아래의 폴더 목록 중 Assets 폴더에서 파일들을 확인할 수 있습니다.

 

또한 자신이 사용하고 싶은 Texture가 있으면 그 Texture 파일이 들어있는 폴더를 클릭해주셔야 합니다.

 

Paint Texture

이제 Inspector에서 Terrain의 Paint Texture로 들어가 준 후에 Edit Terrain Layers...를 눌러줍니다.

 

Add Layer... or Create Layer... 를 클릭해주신 후에 원하시는 Texture를 선택해주시면 됩니다.

 

Texture 적용

Texture가 적용된 모습입니다.

 

멀리서 보면 작은 타일들이 반복되는 것을 볼 수 있습니다.

 

이러한 문제를 완화시켜 주기 위해 Scale을 조절하겠습니다.

 

X, Y는 기본적으로 2, 2로 셋팅 되어있을 겁니다.

 

이 사이즈를 키워 준다면 오른쪽과 같이 조금더 자연스러운 Texture의 모습을 가질 수 있습니다.

 

Texture를 Brush 형태로도 사용할 수 있습니다.

 

Metallic, Smoothness 로 장난 쳐봐도 재밌습니다!

 

다음은 나무를 만들어보겠습니다!

 

 

 

 

 

 

 

 

※ 참조 자료

Unity Docs - https://docs.unity3d.com/ScriptReference/index.html

+ Recent posts