블로그 이미지
devtang
Instagram : @taebr0 devtaehyeong@gmail.com

calendar

1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

Notice

2020. 3. 27. 17:40 AI/Keras

앞 게시물에 사용한 Dense는 fully-connected(FC) layer로써 1차원 배열 데이터로 한정됩니다. 

그러나 컬러 사진 1장은 3차원 배열 데이터이기 때문에 FC신경망을 이용하여 사진을 학습시키기 위해서는

3차원 데이터를 1차원 데이터로 평면화 시켜서 학습 시켜줘야합니다.

 

따라서 신경망이 추출 및 학습시에 굉장히 비효율적이고 정확도를 높이는 데 한계가 있을 수 밖에 없습니다.

이미지 공간 정보를 유지한 상태에서 학습을 시킬 수 있는 모델이 바로 CNN(Convolutional Neural Network)입니다.

 

CNN의 구조

Convolution 신경망은 이미지가 가지는 특성이 고려되어 설계된 신경망이므로 영상 처리에 주로 사용됩니다.

CNN의 구조는 위의 그림과 같이 그림에 한 필터가 순회적으로 돌며 합성곱을 계산하여 그 결과로 하나의

피쳐맵을 형성합니다. 

Convolution 과정

합성곱을 함으로써 얻게되는 효과는 아래 사진을 보면 쉽게 이해할 수 있습니다.

왼쪽에 숫자 30으로 이루어진 부분을 따라 그려보면 오른쪽의 그림과 거의 비슷한 모양입니다.

사진이 픽셀 데이터로 구성되어있음을 알 수 있습니다.

 

 

Polling 은 컨볼루션 레이어의 출력 이미지에서 합성곱을 계산하여 주요값을 뽑아 크기가 작게 출력하여 만듭니다.

아래의 그림을 보시면 4X4의 map에서 2X2 네칸으로 나눌수 있는데 각 칸의 최고 크기의 숫자를 뽑아서 2X2로 resize한 모습입니다.

 

본래 신경망은 인간의 신경계를 모사한 것인데 뉴런이 큰 신호에 반응하는 것과 유사합니다.

주요값을 뽑아서 resize시키는 max pooling을 거치면 노이즈 감소, 속도 증가, 영상의 분별력 강화등이 이점이 있습니다.

 

CNN은 overfitting(너무 학습시켜서 오히려 역효과가 발생하는 현상)을  방지할 수 있습니다.

 

keras에서 CNN을 이용하여 손글씨를 학습해보도록 하겠습니다.

from keras import layers
from keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

컨볼루션 레이어중 Conv2D 레이어를 사용했습니다. 영상인식에 주로 사용되는 레이어입니다.

 

Conv2D(32, (3,3) , activation ='relu', input_shape=(28,28,1))

32 : 컨볼루션 필터의 수 입니다.

(3,3) : 컨볼루션 커널의 행열입니다.

활성화 함수로는 'relu'를 이용하였습니다. 

input_shape=(28,28,1) : 입력값을 28X28 로 주었고, 흑백 사진인 채널1을 사용하였습니다.

 

MaxPooling2D(2,2) : 축소 비율을 2,2로 지정하였습니다.

 

model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

Convolution과 MaxPooling 과정을 거쳤으니 주요 특징들만 추출 되었을 것입니다. 

추출된 주요 특징은 전결합층(Fully-connected)에 전달되어 학습됩니다. 위의 컨볼루션과 풀링의 과정은 2차원 자료를 다루지만 전결합층에 전달하기 위해선 1차원 자료로 바꿔줘야 합니다. Flatten의 원리는 아래의 사진과 같은 느낌입니다.

model.summary()

model.summary()를 이용하여 출력타입을 확인할 수있는데

 

conv2d_9 (Conv2D) (None,3,3,64) 에서

flatten_3 (Flatten) (None,576) 으로 바뀌었습니다.

 

즉 flatten 과정을 거쳐 3X3X64 = 576576으로 평탄화 되어 두개의 Dense층으로 들어갔습니다.

 

from keras.datasets import mnist
from keras.utils import to_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255

train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

 

학습할 데이터와 테스트할 데이터 셋을 설정했습니다. 

model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=5, batch_size=64)
test_loss, test_acc = model.evaluate(test_images, test_labels)

모델을 컴파일 설정 후 테스트를 진행하는 과정입니다.

CNN을 이용한 손글씨셋 인식의 정확도는 0.9929정도 나왔습니다.

 

참고사이트

----------------------------------------------------------------------------------------------------------------------------------

https://bskyvision.com/412

 

딥러닝 알고리즘의 대세, 컨볼루션 신경망(convolutional neural network, CNN)

인공지능이 핫하다. 핫한지 벌써 오래다. 인공지능이 발전하게 된 계기는 크게 세가지로 볼 수 있다. 딥러닝 알고리즘의 발전. 데이터량의 폭발적인 증가. GPU의 발전. 딥러닝 알고리즘은 가장 간단한 피드포워드..

bskyvision.com

https://sonofgodcom.wordpress.com/2018/12/31/cnn%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90-fully-connected-layer%EB%8A%94-%EB%AD%94%EA%B0%80/

 

CNN을 이해해보자. fully connected layer는 뭔가

*본 글은 개인 공부를 위해 복사 및 인용한 글들의 출처를 명시하고 적는 글입니다. 무단 도용 및 인용으로 인한 책임은 지지 않습니다. ​ 요즘 귀차니즘에 빠졌어요. 펜으로 적기 귀찮아서 제가 찾아본 내용들은 시간 순으로 쭉 정리해서 블로깅 하려고 합니다. 머리에서 필요하다고 생각하는 거를 바로바로 찾는 과정이라 많이 두서가 없을 수 있습니다 . 존댓…

sonofgodcom.wordpress.com

https://github.com/AhnSungJoo

 

AhnSungJoo - Overview

Machine Learning & Blockchain & Quant . AhnSungJoo has 34 repositories available. Follow their code on GitHub.

github.com

 

posted by devtang
2020. 3. 26. 17:47 AI/Keras

이번 게시물에서는 Keras를 이용하여 손글씨  데이터셋을 불러와서 모델 구성 및 학습하여 Keras의 간단한 기초과정을 이해하는 게시물입니다.

 

MNIST란 이미지 데이터 셋으로 사람이 직접 쓴 글씨체 0~9까지의 이미지들로 구성되어있습니다.

케라스 데이터셋 라이브러리를 이용하여 쉽게 훈련용 이미지를 load할 수 있습니다.

 

Windows10 환경에서 진행하였으며 기본 세팅 과정은 아래의 블로그에서 참조해주세요.

https://like-edp.tistory.com/3

 

Anaconda(아나콘다) 에서 Keras(케라스) 설치하기

반갑습니다. 이번 글에서는 아나콘다를 사용하여 케라스까지 설치해보는 작업을 진행하도록 하겠습니다. 아나콘다는 일반적으로 정말 다양한 라이브러리 패키지들을 모아놓은 소프트웨어인데, Python을 포함해 연..

like-edp.tistory.com

우선 keras 모듈을 import 하여 훈련에 필요한 손글씨 데이터 셋을 불러옵니다.

import keras
from keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

딥러닝 모델을 학습하기 위해 train 과 test 2가지로 나눕니다.

train 은 훈련시킬 데이터이고 test는 훈련시킨 모델의 테스트용 데이터 입니다.

_images는 숫자 이미지의 정보이며 _label은 숫자 이미지의 이름입니다.

 

print(train_images[0])

import matplotlib.pyplot as plt
plt.imshow(train_images[0])
plt.show()

 

 

이미지데이터를 출력시켜 보았습니다. 출력시켜보면 배열안에 0~255 숫자 픽셀 데이터로 구성 되어있는 것을 알 수 있습니다. 추가적으로 matplotlib를 import 하여 train_images[0] 의 사진을 불러왔습니다.

 

print(train_images.shape)
print(len(train_labels))
print(train_labels)

print(test_images.shape)
print(len(test_labels))
print(test_labels)

train_image와 test_image 를 shape 명령어를 사용하여 각각의 배열 데이터의 크기를 확인할 수 있습니다.

print(train_images.shape) 

(60000,28,28)

60000 : 갯수가 6만개라는 뜻입니다.

28,28 : 하나의 배열은 28X28 로 이루어져 있습니다.

 

print(len(train_labels))

60000

train_labels 의 데이터의 길이가 60000이라는 뜻입니다.

 

이제 데이터 셋 성분을 보았으니 이 데이터들을 Normalize해줘야합니다.

train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255

reshape 로 출력 데이터를 같은 양식으로 통일하고 astype를 이용하여 픽셀값이 0~255사이 값이므로 255로 나누어 데이터를 0~1까지의 숫자로 변환합니다.

 

이미지 데이터들을 전처리 해주었으니  라벨 데이터 역시 One-hot encoding으로 변환 해줍니다.

 

One-hot encoding 이란?

표현하고 싶은 단어의 index에는 1의 값을 부여하고 , 다른 인덱스에는 0을 부여하는 표현 방식입니다.

print (train_labels[:10])
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
print (train_labels[:10])

train_labels 을 인코딩전에 출력하면

[ 5 0 4 1 9 2 1 3 1 4 ]

이렇게 표현이됩니다.

5를 encoding 한 결과값입니다.

[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]

배열 index (0~9)중 여섯번째가 5입니다. 정수 5 위치의 값이 1이며, 나머지값은 0으로 표현하게됩니다.

 

이제 데이터셋의 생성이 모두 완료되었습니다. 다음 단계는 모델을 구성하여 학습준비를 해야합니다.

from keras import models
from keras import layers

network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
network.add(layers.Dense(10, activation='softmax'))

models과 layers 를 import하여 Sequential()함수를 이용합니다.

이때 Dense 레이어를 이용하는데

Dense() 함수에 들어가는 인자는 다음과 같습니다.

 

Dense(512, activation='relu', input_shape=(28*28))

*512 는 출력 뉴런의 수를 설정합니다.

*input_shape 는 입력 뉴런의 수를 설정합니다. (행,열,채널 수) 로 구성됩니다.

*activation='relu' 

*relu : (ReLU, Rectified Linear Unit), 은닉층(hidden layer) 에서 주로 사용하는 활성화 함수입니다.

*softmax : 입력받은 값을 출력으로 0~1사이의 값으로 모두 정규화하며 출력 값들의 총합은 항상 1이 되는 특성을 가진 함수 입니다. 

 

이 게시물에서 구하고자 하는 문제는 입력 이미지가 주어졌을 때 0~9 까지 각 숫자와 얼마나 비슷한지에 대한 확률을 구하는 것입니다. 숫자 9를 인식하려는데 예를들어 9일확률 80%, 8일확률 5% 로 다른 숫자에 대해서 낮은 확률로 인식할 수 있습니다. 

 

network.add를 이용하여 두 개의 Dense 계층을 연속적으로 추가하며, 합계가 1이 되는 10개의 확률 점수의 배열을 반환합니다. 각 점수는 현재의 숫자 이미지가 10개 숫자 분류 각각에 속할 확률입니다.

 

 

network.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

이제 .compile을 이용하여 학습하기전 설정을 합니다.

optimizer = 학습속도를 빠르고 안정적으로 하는 원리를 말합니다.

'Adam' , 'SGD', 등 다양한 알고리즘이 있지만 여기서 수행할 'rmsprop'은 각각의 가중치에 맞춰 학습률을 제어하는 기법입니다. 이 알고리즘은 학습률을 최근에 갱신된 가중치 기울기의 평균으로 나누는 방법입니다.

metrics 는 'accuracy' 를 이용하였으며 이는 훈련 및 시험 중에 모니터링할 측정값입니다.

 

이제 컴파일도 완료 되었으니 fit()함수를 이용하여 학습을 진행해보도록 합시다.

network.fit(train_images, train_labels, epochs=5, batch_size=128)

epochs 는 학습횟수를 의미합니다. 5를 입력하였으니 총 5번 학습을 반복합니다.

batch_size 는 몇번 학습해보고 해답을 맞출 건지 의미합니다. 즉 , 한번에 128개씩 훈련하는 것입니다.

 

test_loss, test_acc = network.evaluate(test_images, test_labels)
print('test_acc:', test_acc)

학습할때의 훈련셋 검증셋의 손실 추이를 보기위한 코드입니다.

위의 사진을 보시면 총 5번 학습 진행하였으며 70000장의 사진중 60000장이 훈련에 사용됬으며 10000장이 검증셋으로 사용되어 테스트 정확도가 0.977로 나타났습니다.

 

참고사이트

https://keras.io/ko/optimizers/#_1

 

Optimizers - Keras Documentation

옵티마이저의 사용법 옵티마이저는 Keras 모델을 컴파일하기 위해 필요한 두 개의 매개변수(parameter) 중 하나입니다. from keras import optimizers model = Sequential() model.add(Dense(64, kernel_initializer='uniform', input_shape=(10,))) model.add(Activation('softmax')) sgd = optimizers.SGD(lr=0.01, de

keras.io

https://tensorflow.blog/4-%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%8B%B1%EA%B8%80-%EB%A0%88%EC%9D%B4%EC%96%B4-%EB%89%B4%EB%9F%B4-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-first-contact-with-tensorflow/

 

4. 텐서플로우 단일 레이어 뉴럴 네트워크 – First Contact with TensorFlow

이 글은 스페인 카탈루냐 공과대학의 Jordi Torres 교수가 텐서플로우를 소개하는 책 ‘First Contack with TensorFlow’을 번역한 것입니다. 이 글은 원 도서의 라이센스(CC BY-NC-SA 3.0)와 동일한 라이센스를 따릅니다. 파이썬 3  notebook으로 작성한 이 섹션의 코드는 여기에서 보실 수…

tensorflow.blog

 

posted by devtang
2020. 3. 20. 15:15 AI/YOLO

항상 YOLO 학습때 anchor 값이 정확히 무엇인지 몰라서 찾아봤는데 

https://github.com/pjreddie/darknet/issues/568

 

Can someone clarify the anchor box concept used in Yolo? · Issue #568 · pjreddie/darknet

I know this might be too simple for many of you. But I can not seem to find a good literature illustrating clearly and definitely for the idea and concept of anchor box in Yolo (V1,V2, andV3). Thanks!

github.com

여기서 AlexeyAB , YOLO 버전의 제작자중 한명이 댓글을 남겼습니다.

 

Anchors are initial sizes (width, height) some of which (the closest to the object size) will be resized to the object size - using some outputs from the neural network (final feature map):

 

Anchor는 초기의 크기(너비, 높이) 이며 그 중 일부 (개체 크기에 가장 가까운)는 신경망 (최종 기능 맵)의 일부 출력을 사용하여 개체 크기로 크기가 조정됩니다.

 

yolo_layer.c - get_yolo_box 함수

 

  b.x = (i + x[index + 0*stride]) / lw;
  b.y = (j + x[index + 1*stride]) / lh;
  b.w = exp(x[index + 2*stride]) * biases[2*n]   / w;
  b.h = exp(x[index + 3*stride]) * biases[2*n+1] / h;

darknet 폴더안에 yolo_layer.c 파일중 get_yolo_box 함수 안에 다음과 같은 코드가 있습니다.

대부분의 경계 상자에는 특정 높이 너비 비율이 있는데 YOLO는 경계 상자를 직접 예측하는 대신 특정 높이 너비 비율을 가진 미리 결정된 상자 세트에서 대상을 예측합니다. 이때 미리 정해진 상자 세트가 앵커 입니다.

 

따라서 학습시에 yolov3.cfg 파일을 가져와서 다음과 같이 수정합니다.

 

max_batches 는 자신의 classes * 4000 로 설정하였습니다. (저는 클래스가 6개이므로 24000)

 

그리고 cfg파일에 맨아래쪽으로 내려가면

[convolutional]
size=1
stride=1
pad=1
filters=33
activation=linear


[yolo]
mask = 0,1,2
anchors = 11, 21,  31, 62,  43, 80,  53,103,  65,125,  78,155,  51,287, 106,202,  76,350
classes=6
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1

여기서 [yolo]부분에 classes 를 각 자신이 설정한 classes 개수로 설정하고 [convolutional] 파트에 filters 수는 yoloV3 기준 (classes + 5) * 3 해준 값을 설정해줍니다. (저는 클래스가 6이므로 (6+5) * 3 = 33 해줬습니다.)

 

조금더 올려보면 마찬가지로 [yolo] 가 두개나 더 있습니다.

[yolo]와 바로 그위에 [convolutional] 를 모두 수정해줍니다.

[convolutional]
size=1
stride=1
pad=1
filters=33
activation=linear

[yolo]
mask = 6,7,8
anchors = 11, 21,  31, 62,  43, 80,  53,103,  65,125,  78,155,  51,287, 106,202,  76,350
classes=6
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1

[convolutional]
size=1
stride=1
pad=1
filters=33
activation=linear

[yolo]
mask = 3,4,5
anchors = 11, 21,  31, 62,  43, 80,  53,103,  65,125,  78,155,  51,287, 106,202,  76,350
classes=6
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1

자이제 anchors 값도 설정해줘야하는데 각자 anchors 값이 다르므로 명령어를 통해 가져옵니다.

darknet.exe detector calc_anchors data/obj.data data/yolo-obj.cfg -num_of_clusters 9 -width 416 -height 416

obj.data , cfg 파일은 본인의 경로에 맞게 설정해주시고 width 와 heights 역시 본인의 cfg 파일에 설정한 크기대로 설정해주시면 됩니다. 저는 416 416으로 맞췄습니다.

코드를 실행해주면 평균 IoU % 값이 뜨고 anchors값이 txt로 저장이됩니다.

 

 

이후에 동일하게 설정한 cfg파일을 사용하여 학습 진행해 주면 됩니다.

 

 

posted by devtang
2020. 3. 11. 16:58 개인 프로젝트

*2020년도 10월 교내 캡스톤경진대회 제품제작에 성공하였습니다

youtu.be/OwrC1YFvGH0

 

 

마스크 학습관련내용은 아래 게시물에 있습니다.

https://ultrakid.tistory.com/15

 

[Windows] 마스크 착용 유/무 판단을 위한 YOLO 학습

요즘 코로나 바이러스로 인해 전세계가 혼란입니다. 그래서 마스크를 착용하지 않으면 주변 사람에게 피해가 가니 항상 착용하도록 합시다!! 다름이 아니라 연구실에서 공부겸 간단한 개인 프��

ultrakid.tistory.com

코로나 바이러스로 요즘 전세계적으로 혼란인 가운데,

마스크를 착용하지않으면 상대적으로 타인에게 불안감을 조성할 우려가 있어서

마스크를 착용했을때 문이 열리게 되는 프로젝트를 만들었습니다.

개인적으로 시간이 날때마다 짬짬히 만들어보았습니다.

우선 구현 영상입니다.

https://youtu.be/fcFa_WhXNIo

 

 

라즈베리파이와 컴퓨터간 실시간 소켓 통신을 이용하였으며

컴퓨터에서는 YOLO를 이용한 마스크 착용 인식, 라즈베리에서는 소켓으로 통신받아 GPIO를 제어하였습니다.

 

HC-SR04 초음파센서

우선 사람이 문앞에 있다고 가정하기 위해 초음파센서를 이용하였습니다.

라디오 같이 생긴 이 센서는 송신부인 Trigger와 수신부인 Echo로 나뉘어있는데 송신부에서 초음파를 발사하면 물체에 부딪쳐 나오는 진동을 Echo에서 받은 시간을 이용하여 거리를 측정하는 방식입니다.

이 센서는 최대 4m까지 거리측정이 가능합니다.

 

 

 라즈베리파이와 브레드보드를 이용하여 연결해놓고 잘 작동하는지 테스트해봤습니다.

 LED는 초음파센서와 물체와의 거리가 100cm 이하로 일정 count동안 존재할때 켜지도록 해놨습니다.

 

 서보모터는 SG-90을 이용했으며 서보모터에 나무젓가락 세개를 테이프로 이어붙혀서 만들었습니다.

 출입문 역할로 만들었으며, 마스크를 쓰면 서보모터를 회전시켜 지나갈 수 있도록 만들었습니다.

 이제 남은건 윈도우 환경에서 인식한 결과를 라즈베리에 전송시켜 마스크를 썼을때 서보모터가 움직이게

 두가지를 합쳤습니다.

 

YOLO의 darknet 소스코드찾다 image_opencv.cpp 파일에서 class를 검출하는 소스코드를 찾아냈습니다.

/*********************************************INSERT CODE LINE*******************************************************/
if (strcmp(detectname, names[j]) != 0)
	{
		fp = fopen("***TXT파일위치***", "w");
		fwrite(names[j], strlen(names[j]), 1, fp);
		fclose(fp);
	}
/*********************************************INSERT CODE LINE*******************************************************/

제가 추가한 소스코드입니다. names[j]에 인식된 class의 이름이 저장되있습니다.

TXT파일을 fopen 함수를 이용하여 열어서 fwrite 함수로 TXT파일에 검출된 class를 적게 구현했습니다.

이제 소켓으로 class name을 보내기만 하면 됩니다.

 

윈도우 소켓 코드중 일부입니다.

while (1) 
	{
		fp = fopen("***TXT파일 위치***", "r"); //YOLO에서 검출된name을 적은 TXT파일 열기
		fscanf(fp, "%s", message); // 문장을 복사
		fclose(fp); // 파일닫기
		if (strcmp(message, pre_message) == 0)
			continue;
		else {
			strcpy(pre_message, message);
			printf("SEND : %s\n", pre_message); 
			send(hSocket, message, sizeof(message) - 1, 0); //send를이용하여 라즈베리로 전송
			
		}

	}

 

YOLO와 소켓을 둘다 실행시켜놓고 소켓은 계속 텍스트를 읽어서 보내주기만 합니다.

 

라즈베리파이에서 GPIO를 다루는 코드 중 일부입니다.

if distance <= 100: # *초음파센서와 물체간 거리가 100cm 이하일때
			f = open("라즈베리파이 TXT파일 위치",'r') #윈도우에서 받은 name을 읽기
			read_yolo = f.readline() # read_yolo 변수에 문장을 복사
			f.close()
			right = 'MASK'	
			count += 1 # count가 증가함 
			if count >= 4 and read_yolo == right: # count가 4이상 and 'MASK'라는 name을 받게되면
				# print(2)
				p.ChangeDutyCycle(12) # 서보모터를 올림
				time.sleep(1)
				GPIO.output(LED, GPIO.HIGH) # LED를 켬
				time.sleep(2)	
				p.ChangeDutyCycle(7.5) # 2초후 서보모터를 내림
				time.sleep(1)
				count = 0 # count를 0으로 초기화
				GPIO.output(LED,GPIO.LOW) # LED를 끔
			
			else: # 마스크를 쓰지 않으면 서보모터를 제어하지 않는다.
				continue

일정시간동안 출입문 앞에서 마스크를 끼고 있지 않은 상태로 서있으면 야속하게 문이 열리지 않습니다.

간단하게 GPIO를 제어하여 마스크 착용시에만 문이 열리는 시스템을 만들어 봤습니다.

나중에 추가적으로 소스 코드 수정 및 기능을 개선,추가하여 더 좋은 퀄리티로 만들어 보고 싶습니다.

 

 

posted by devtang
2020. 3. 10. 15:48 AI/YOLO

=========수정 ==========

마스크 착용 여부를 이용하여 교내 경진대회의 출전한 UCC영상입니다. 

youtu.be/OwrC1YFvGH0

========================

요즘 코로나 바이러스로 인해 전세계가 혼란입니다.

그래서 마스크를 착용하지 않으면 주변 사람에게 피해가 가니 항상 착용하도록 합시다!!

다름이 아니라

연구실에서 공부겸 간단한 개인 프로젝트를 진행해보고 있습니다. 시국이 시국인지라 마스크를 착용해야 문이 열리는 그런 시스템을 만들어보고자 하는데 착용 유무를 판단하기 위하여 YOLO를 이용하기로 했습니다.

 

class는 2가지로, 마스크를 쓴사람, 안쓴사람으로 나눠 학습했습니다. YOLO_MARK를 이용하여 라벨링 작업하였습니다.

Windows에서 학습을 진행하였으며, 컴퓨터환경은 인텔 CPU i5-6600 3.30Ghz ,NVIDIA GeForce GTX 1050 Ti 입니다.

 

 마스크를 쓴얼굴 500장, 안쓴얼굴 500장 정도 모아서 수동으로 라벨링 작업을 진행했습니다.

사실 딥러닝 학습시에 class당 500장의 사진은 굉장히 소량입니다. 추후에 더 괜찮은 아이디어가 생기면 

더 기능을 추가할 예정입니다. 연구실에서 간단한 프로젝트로 학습겸 진행해보았습니다.

 

이미지를 라벨링 하게되면 txt파일이 생기는데 파일 안에는 class num와 각 사진의 라벨을 딴 좌표값이 들어있습니다.

참고로 이미지는 크롤링을 이용하여 구글에서 사진을 뽑아서 학습에 이용가능한 사진 500장씩만 분류했습니다.

 

정상적으로 확인이 되고있습니다.

 

123: 0.814543, 1.045863 avg loss, 0.001000 rate, 5.422000 seconds, 7872 images

 

123: 현재 훈련/Batch 를 몇번 반복했는지 나타냅니다.

0.814543 : 총 손실을 나타냅니다.

1.045863 avg : 평균 손실 오차로써 최대한 낮을때까지 학습을 시켜줘야합니다. 보통 저는 0.020000정도 아래로 내려가면 학습을 중지 시킵니다.

0.001000 rate : cfg 파일안에 정의된 학습 속도 비율을 나타냅니다.

5.422000 seconds : 1batch를 학습시키는데 소요된 시간을 나타냅니다.

7872 images : 지금까지 학습에 사용된 이미지의 총갯수를 나타냅니다.

 

위의 사진은 학습한지 얼마 안됬을때 캡쳐한 사진입니다. 20000번 넘게 학습시켜본결과 제 컴퓨터로는 하루좀 넘게 켜뒀던 것 같습니다.

 

위의 사진은 약 2만 2천번 학습시켰을때 나타난 학습곡선으로 이정도로 학습된 weights 파일로 구동시켜봤습니다.

 

아래는 구현 사진과 동영상 입니다.

 

30fps정도 나오며 인식률도 500장의 데이터 치고는 양호합니다.

마스크를 꼈을때, Mask 93% 정도로 인식합니다.

 

마스크를 벗게되면, 마스크를 착용하지 않은 class이름을 NO_MASK로 정의했습니다.

역시 90%이상으로 인식합니다.

 

 

 

추가적으로 라즈베리파이GPIO와 소켓통신을 이용하여 마스크를 끼지 않았을때 출입을 하지 못하도록 막는 시스템을 구현할 예정입니다.

감사합니당. 

'AI > YOLO' 카테고리의 다른 글

[YOLO] 윈도우 버전 YOLOv3 설치 Guideline  (35) 2020.07.22
YOLO 학습시 cfg 파일 설정(anchors)  (3) 2020.03.20
posted by devtang
2020. 3. 10. 14:41 개인 프로젝트

개인 프로젝트를 위해 저에게 필요한 단방향 소켓 통신방식을 만들어 봤습니다.

 

 

기본적인 세팅 구조

 

우선 라즈베리파이에 data.txt라는 텍스트 파일에 Random한 시간과 내용을 실시간으로 적게 되면,

윈도우에서 실시간으로 받은 문장을 server.txt라고 만든 텍스트 파일에 실시간으로 적어주는 방식입니다.

저번 출입알림시스템 때 이용해봤지만 리눅스-리눅스 환경의 소켓 통신이라 이번에는 서버코드를 약간 수정했습니다.

 

data.txt 파일에 ABCD를 적어서 저장하는 순간 윈도우내 지정된 server.txt 파일에 문자가 저장됩니다.

소켓으로 실시간 메모장을 열고 확인해야 하기 때문에 조금 비효율적이지만 

제가 배운 선에서 최대한 저한테 이용하기 편리하게 만들어놨습니다.

 

Windows 10 에서 구동한 소스 코드입니다. (Server)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma comment(lib,"ws2_32.lib")
#include <WinSock2.h>
#include <windows.h>


#define  BUF_SIZE   1024
void ErrorHandling(char *message);
int main(int argc, char *argv[])

{
   FILE *fp;
   WSADATA wsaData;
   SOCKET hServSock, hClntSock;
   SOCKADDR_IN servAddr, clntAddr;
   char message[BUF_SIZE];
   char write_memo[BUF_SIZE] = { 0, };
   int str_len, j;
   int szClntAddr;

   if (argc != 2)
   {
      printf("Usage:%s <port>\n", argv[0]);
      exit(1);
   }



   if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) //소켓 라이브러리 초기화
      ErrorHandling("WSAStartup() error!");

   hServSock = socket(PF_INET, SOCK_STREAM, 0); //소켓생성

   if (hServSock == INVALID_SOCKET)
      ErrorHandling("socket() error");

   memset(&servAddr, 0, sizeof(servAddr));
   servAddr.sin_family = AF_INET;
   servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
   servAddr.sin_port = htons(atoi(argv[1]));

   if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) //IP주소와 PORT 번호 할당
      ErrorHandling("bind() error");

   if (listen(hServSock, 5) == SOCKET_ERROR) //listen 소켓을 서버 소켓으로 완성
      ErrorHandling("listen() error");


   szClntAddr = sizeof(clntAddr);
   hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr); // accept함수 호출
   if (hClntSock == INVALID_SOCKET)
      ErrorHandling("accept() error");

   while (1)
   {
      str_len=recv(hClntSock, message, sizeof(message)-1, 0); //클라이언트 메세지를 받음
      if (strcmp(write_memo, message) == 0) // *참고
         continue;
      else {
         fp = fopen("C:\\**TXT파일의위치**", "w"); //fopen함수 이용하여 메모장 쓰기모드로 열기
         fwrite(message, strlen(message), 1, fp); // 받은 메세지를 메모장에 쓰기
         fclose(fp); // 메모장을 닫는다.
         strcpy(write_memo, message); // *받은 메세지를 write_memo 변수에 복사해놓음(계속 쓰는것을 방지)
         printf("Message from Client : %s", message); //받은 메세지를 표시
      }
      for (j = 0; j < 100; j++) {
         message[j] = 0; //100글자까지 초기화
      }
         
   }
   closesocket(hClntSock);
   closesocket(hServSock);
   WSACleanup();
   return 0;
}

void ErrorHandling(char *message)
{
   fputs(message, stderr);
   fputc('\n', stderr);
   exit(1);
}

받은 message를 write_memo 라는 변수를 만들어서 복사해준 이유는 

라즈베리파이에서 예를들어 'ABCD'를 입력하면 한번만 보내는것이 아니라 무한적으로 ABCD를 계속 보내게 됩니다.

따라서 write_memo에 전에받았던 'ABCD'를 저장해놓으면 다음 while 루프때 문자가 바뀌지않으면

다음 문장을 입력받기 전까지 대기하게 할 수 있어서 만들어놨습니다.

 

아래는 RaspberryPi에서 구동한 소스 코드입니다.(Client)

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define  BUF_SIZE   1024
void error_handling(char *message);

int main( int argc, char *argv[])
{
   char send_socket[BUF_SIZE]= {0,};
   int sock;
   char message[BUF_SIZE];
   struct sockaddr_in serv_adr;
   FILE *fp;
   if(argc != 3) {
      printf("Usage : %s <IP> <PORT>\n", argv[0]);
      exit(1);
   }
   
   sock= socket( PF_INET, SOCK_STREAM, 0);
   if( -1 ==sock)
      error_handling("socket() error");

   memset( &serv_adr, 0, sizeof( serv_adr));
   serv_adr.sin_family     = AF_INET;
   serv_adr.sin_addr.s_addr= inet_addr(argv[1]);
   serv_adr.sin_port       = htons(atoi(argv[2]));
  

   if( -1 == connect(sock, (struct sockaddr*)&serv_adr, sizeof( serv_adr) ) )
   {
      printf( "접속 실패\n");
      exit( 1);
   }
   
   
   while(1){
   
      fp = fopen("**TXT파일 위치**","r"); // 읽기 모드로 TXT파일을 열기
      fscanf(fp, "%s",message); // TXT파일내 문장을 읽기
      //printf("MES : %s",message);
      fclose(fp);
      if(strcmp(message,send_socket)==0)
      {
    	continue;
      }
      else{
    strcpy(send_socket,message);
    printf("SEND : %s\n", send_socket);
    write(sock,message,sizeof(message)-1); // 서버로 message 전송
    }
      
      
   }
   close(sock);
   return 0;
}

void error_handling(char *message)
{
   fputs(message,stderr);
   fputc('\n',stderr);
   exit(1);
}

 

여기도 마찬가지로 제가 쓰던 코드라 조금 난잡할수도 있습니다. 위와 같은 방식으로 send_socket이라는 변수에 message를 저장시켜 문장이 변하지 않으면 계속 while 루프를 돌도록 구현했습니다.

 

아래는 프로그램 구현 동영상입니다.

 

 

 

참고도서 : 윤성우의 열혈 TCP/IP 소켓 프로그래밍

 

====== 오렌지 미디어 ======

             목록 서버1   서버2 강의시간 강의교안  PART 01 네트워크 프로그래밍의 시작    Chapter 01 네트워크 프로그래밍과 소켓의 이해        01-1. 네트워크 프로그래밍과 소켓의 이해① 27:43        01-1. 네트워크 프로그래밍과 소켓의 이해② 07:58        01-1. 네트워크 프로그래밍과 소켓의 이해③ 12:52        01-2. 리눅스 기반 파일 조작하기 27:27        01-3.

www.orentec.co.kr

 

posted by devtang
2020. 3. 5. 16:50 Python

몇가지 라이브러리와 파이썬을 이용하여 크롤링하는 예제를 돌려보겠습니다.

 

크롤링(Crawling)이란?

웹 상에 존재하는 갖가지 컨텐츠를 수집하는 작업으로, 다양한 프로그램을 만들 수 있습니다.

예를 들어, 인터넷에 존재하는 이미지 파일을 하나하나 저장하기 번거롭습니다.

크롤링을 이용하면 간단한 코드로 짧은시간에 내가원하는 검색어의 이미지를 가져올 수 있습니다.

 

저는 딥러닝 학습을 위해 많은 양의 이미지가 필요했었는데 이때 자주 이용한 방법이 바로 크롤링입니다.

이것 말고도, 맛집 블로그 링크들을 한번에 모으던가 원하는 사이트목록을 빠른시간에 가져올 수 있는게 큰 장점입니다.

 

마스크 착용 유무를 판단하는 학습 데이터가 필요했습니다.

이렇게 실시간으로 사진이 폴더에 저장됩니다.

 

우선 크롤링에 필요한 import 목록입니다.

from urllib.request import urlopen
from bs4 import BeautifulSoup as bs
from urllib.parse import quote_plus

urlopen은 파이썬을 설치할 때 기본적으로 설치되는 라이브러리입니다.

혹시모르니 필요한 라이브러리를 pip명령어를 이용하여 cmd창에서 설치해주도록 합시다.

 

pip install beautifulsoup4
pip install requests

bequtifulsoup 라이브러리는 HTML 및 XML 파일에서 데이터를 가져 오는 Python 라이브러리입니다. 

이 라이브러리를 이용하여 조금더 편하게 정보를 가져올 수 있습니다.

 

우선 네이버 이미지를 긁어모으기 위해서 네이버 url을 들여다보면

요즘 문제가 심각한 코로나에대해 검색해봤습니다. 또 다른 검색어로 검색해보겠습니다.

 

치킨이 먹고싶네요. 아무튼 위에 두 검색한 url을 들여다보면 

https://search.naver.com/search.naver?where=image&sm=tab_jum&query= 라는 주소에

치킨, 코로나 만 바뀐것을 알 수 있습니다.

 

baseurl 을 위의 주소로 두고, 우리가 원하는 검색어의 이미지를 크롤링하기 위하여 뒤에 검색어는

파이썬 실행시에 입력하도록 plusurl이라는 변수로 생성해줍니다.

 

baseUrl = 'https://search.naver.com/search.naver?where=image&sm=tab_jum&query='
plusUrl = input('검색어를 입력하세요 : ')
# url = baseurl + 검색어
url = baseUrl + quote_plus(plusUrl)
html = urlopen(url)
soup = bs(html, "html.parser")
img = soup.find_all(class_='_img')

 baseUrl과 검색어의 변수 plusUrl을 더하여 원하는 검색된 페이지로 접근합니다.

이런식으로 html을 열어서 이미지의 경로를 받아옵니다.

 

n = 1
for i in img:
    imgUrl = i['data-source']
    with urlopen(imgUrl) as f:
        with open('./img/' + plusUrl + str(n)+'.jpg','wb') as h: # w - write b - binary
            img = f.read()
            h.write(img)
    n += 1
print('다운로드 완료')

네이버 이미지는 한페이지에 50개의 이미지만 생성되므로 보통 이미지가 다운로드되면 50개까지 크롤링이 가능합니다.

 

아래는 전체 소스 코드입니다.

from urllib.request import urlopen
from bs4 import BeautifulSoup as bs
from urllib.parse import quote_plus

baseUrl = 'https://search.naver.com/search.naver?where=image&sm=tab_jum&query='
plusUrl = input('검색어를 입력하세요 : ')
# 한글 검색 자동 변환
url = baseUrl + quote_plus(plusUrl)
html = urlopen(url)
soup = bs(html, "html.parser")
img = soup.find_all(class_='_img')

n = 1
for i in img:
    imgUrl = i['data-source']
    with urlopen(imgUrl) as f:
        with open('./img/' + plusUrl + str(n)+'.jpg','wb') as h: # w - write b - binary
            img = f.read()
            h.write(img)
    n += 1
print('다운로드 완료')

 

간단한 파이썬 코드로 네이버 이미지를 빠른시간에 많이 저장할 수 있습니다.

마지막으로 구현 영상입니다.

youtu.be/yl36J0dXS2w

 

 

네이버 이미지 가져오는 방법이었습니다. 감사합니다.

posted by devtang
2020. 3. 2. 17:25 Raspberry Pi

 

EAR(Eye Aspect ratio)알고리즘을 이용한 운전자 졸음운전 방지 시스템 입니다.

 

구현 영상

 

EAR 알고리즘

Soukupová와 Čech의 2016 년 논문인 Facial Landmarks를 사용한 Real-Time Eye Blink Detection 의 작업을 기반으로  EAR ( eye aspect ratio) 관계를 반영하는 방정식을 도출 할 수 있습니다.

 

 

 

눈을 감게 되면 눈의 세로 비율이 작아지고, EAR 비율역시 작아집니다. 사람이 깜박이면 눈의 종횡비가 급격히 감소하여 0에 가까워집니다. 사람이 졸리면 눈을 감거나 눈을 조그마하게 뜨는 행동을 하게 되므로 EAR값을 이용하여 주기적으로 낮아지면 알람을 울리게 하는 졸음감지 시스템을 구현할 수 있습니다.

 

def eye_aspect_ratio(eye):
#눈에 랜드마크 좌표를 찍어서 EAR값을 예측합니다.
	A = dist.euclidean(eye[1],eye[5]) 
	B = dist.euclidean(eye[2],eye[4])
	C = dist.euclidean(eye[0],eye[3])
	
	ear = (A+B) / (2.0 * C)
	return ear

 

또한 평소 눈의 THRESH값을 0.3정도로 설정해놓고 눈을 감게되면 EAR비율이 작아지므로 ALARM이 울리게 됩니다. 

# 눈의 EAR의 THRESH 기본값을 0.3으로 설정합니다.
EYE_AR_THRESH = 0.3
EYE_AR_CONSEC_FRAMES = 48
# 프레임 카운터와 부울을 초기화하는 데 사용
# 알람이 울리면 표시
counter = 0
ALARM_ON = false

 

 

OpenCV의 drawContours 함수를 이용하여 눈 주변에 선을 실시간으로 그려줍니다.

   #외곽선으로 인식된 눈을 그려줍니다(drawContours함수이용)
		leftEyeHull = cv2.convexHull(leftEye)
		rightEyeHull = cv2.convexHull(rightEye)
		cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1)
		cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)

 

눈을 인식해야 하므로 68_face_landmark.dat 파일을 이용하여 예제를 실행합니다.

 

정상적으로 구동이 되는것을 확인했습니다. 라즈베리파이에서 실시간으로 돌려봤지만

위에 동영상을 보시면 프레임이 상당히 낮습니다.

 

읽어주셔서 감사합니다.

 

posted by devtang
prev 1 2 3 4 next