본문 바로가기

Programmer Jinyo/Machine Learning

Yolo V3 + Pytorch로 자동차 번호판 라벨링 & object detection 해보기


투명한 기부를 하고싶다면 이 링크로 와보세요! 🥰 (클릭!)

바이낸스(₿) 수수료 평생 20% 할인받는 링크로 가입하기! 🔥 (클릭!)


COCO 데이터 셋 등이 아닌 직접 모은 데이터셋으로 object detection을 진행해보자!


자동차 번호판의 숫자들을 한번 맞춰보도록 하자.


yolo / pytorch 환경으로 진행한다. 


(이 글에서는 Yolo의 내용은 다루고 있지 않다. 궁금하다면

2019/01/31 - [Programmer Jinyo/Machine Learning] - Yolo 논문 정리 및 Pytorch 코드 구현, 분석 01 ( You Only Look Once: Unified, Real-Time Object Detection )

포스트를 참고하자. 코드 구현은 완성하지 못했지만 이론적 설명은 마친 글이다)





이미지 데이터셋 만들기


https://github.com/tzutalin/labelImg


이미지 라벨링을 도와주는 툴이다.


들어가서 본인의 환경에 맞는 세팅을 하자.


나 같은 경우에는 python + anaconda 이기 때문에 클론 받은 후 해당 폴더에서


conda install pyqt=5 pyrcc5 -o resources.py resources.qrc python labelImg.py


위의 명령어를 실행했다.


그리고 폴더를 하나 만들어서


위와 같이 파일들을 저장 해 주자.


(나 같은 경우 data\img폴더에 저장했다)

data 폴더에 predefined_classes 부분을 눌러 class를 적어준다.


번호를 할 꺼니까...


이렇게 class를 저장 해 주었고


python labelImg.py를 실행하면 창이 하나 뜨는데, 



위 버튼을 클릭해서 이미지 폴더를 선택해주면 차례로 사진이 뜬다.


저장 폴더를 정하고 싶으면 그 아래의 Change Save Dir을 눌러서 바꿔주자.




꼭 필요한 단축키는 w, a, d인데, 새로 영역 지정(w), 다음(d) 이전(a) 사진으로 넘어가기 단축키이다.





위와같이 w를 누르고 영역 지정을 한 후에 라벨링을 해 주면 된다.


매 사진마다 라벨링이 끝나면 Ctrl + s 후 d키로 다음 사진 라벨링을 해 주면 된다.


중요한건 Yolo 포멧으로 저장 해 줘야 한다는거.




그러면 이렇게 나온다!


(13.txt를 연 모습)




YOLO 받기



이제 트레이닝 할 YOLO 본체를 받아보자.



https://github.com/eriklindernoren/PyTorch-YOLOv3



여기의 깃허브에서 받았다.



자 이제, 우리가 정성스레 라벨링 한 친구들을 YOLO 소스 파일에서  쓸 수 있도록 세팅을 해 주자.


프로젝트 폴더안의 data 폴더에 plate 라는 폴더를 만들어 주자.


/data/plate 안쪽에



images

labels


폴더를 만들어 주고


plate.names

traindir.txt

validdir.txt


파일을 만들어 주자.


이제 아까 만든 파일들을 넣을 차례이다.


images 폴더에는 자동차 번호판에 대한 이미지들을 넣으면 되고,

labels 폴더에는 아까 만들어놓은 ~~.txt 파일들을 넣자.


그리고 plate.names 파일에는 아까 라벨링 결과의 classes.txt안의 내용을 복붙해주자.


우리는 학습 -> 결과 확인만을 진행할 것이기 때문에 validdir은 사용하지 않을 것이고

traindir.txt에 우리의 이미지 파일 경로들을 복사해주자.



(이 글은 고작 20개의 파일들만 가지고 진행하고 있다)



모델, 트레이닝 코드 수정하기


기존 coco 데이터셋은 80개의 class인 반면, 우리는 10개의 class만 가지고 진행하므로 조금의 수정이 필요하다.


기존의 파일을 건드리지 말고 (혹시나 잘못 건드리면 다시 clone 받아야 할 수도 있으니까) 새로운 파일을 만들면서 진행하겠다.




1.  /config/yolov3.cfg


위 파일을 복사해서


/config/yolov3plate.cfg 파일로 저장하자.

모델의 파라미터 정의를 내려놓은 파일이다.


우리는 class 개수가 달라졌으므로 



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


[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=10
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1



classes = 80으로 되어있는 부분을 10으로 고쳐준다. (총 3군데에 있다)


그리고 그 위의 filters=255로 된 부분도 45로 고쳐준다.


그 이유는 mask를 3개를 쓰고 있기 때문에


yolo로 들어가는 input이 3 * (5 + classnum) 이기 때문이다.



2. /config/plate.data


위 파일을 생성하고


classes= 10

train=data/plate/traindir.txt

valid=data/plate/validdir.txt

names=data/plate/plate.names

backup=backup/

eval=plate



위 내용을 복붙해주자.


체크한 두 파일을 제대로 업데이트 했으면 된다.


(tiny 버전은 왜인지 고쳐봐도 안된다)


3. train.py


를 복붙하여 trainplate.py 를 만들자.


parser.add_argument("--epochs", type=int, default=200, help="number of epochs")
parser.add_argument("--batch_size", type=int, default=5, help="size of each image batch")
parser.add_argument("--model_config_path", type=str, default="config/yolov3plate.cfg", help="path to model config file")
parser.add_argument("--data_config_path", type=str, default="config/plate.data", help="path to data config file")
parser.add_argument("--weights_path", type=str, default="weights/yolov3plate.weights", help="path to weights file")
parser.add_argument("--class_path", type=str, default="data/plate/plate.names", help="path to class label file")


기존의 coco 관련 파라미터들을 다 plate 관련 파라미터로 수정해주었다.


그리고 데이터 로더 부분도 조금..


# Get dataloader
dataloader = torch.utils.data.DataLoader(
ListDataset(train_path), batch_size=opt.batch_size, shuffle=True, num_workers=opt.n_cpu
)

수정했다.



이게 끝났다면, 


python trainplate.py



실행시키면 학습이 쭉 된다.



데이터가 없어서 에포크 한 200 정도 돌렸다.




이제 우리의 결과 파일을 받아와서 볼 차례이다.



detect.py 를 수정 후 실행하면 된다.



detect.py 수정하기


1. 검증하고 싶은 data파일들 저장하기


나같은경우에는 /data/platesamples 폴더를 만들어 그 안에 검증하고싶은 번호판 사진을 저장했다.


그리고 이미지 폴더 파라미터를


parser.add_argument('--image_folder', type=str, default='data/platesamples', help='path to dataset')


이렇게 바꿔준다.


2. config 파일 파라미터 수정하기


parser.add_argument('--config_path', type=str, default='config/yolov3plate.cfg', help='path to model config file')


트레이닝 할 때와 같은 이유이다.



3. weights path 수정하기


우리가 저장해놓은 학습된 모델의 결과를 이제 불러와야 할 것이다.


별다른 수정을 하지 않았다면 /checkpoints/epoch.weights에 매 에포크마다의 결과가 저장될 것이다.


parser.add_argument('--weights_path', type=str, default='checkpoints/199.weights', help='path to weights file')


에포크 200까지 돌려서 난 199.weights 를 불러왔다.


4. class name 경로 수정


parser.add_argument('--class_path', type=str, default='data/plate/plate.names', help='path to class label file')


이것 또한 트레이닝때와 같다


5. 취향.


parser.add_argument('--nms_thres', type=float, default=0.2, help='iou thresshold for non-maximum suppression')


이건 바꾸던지 말던지..


이 숫자가 줄어들수록 같은 class끼리의 겹치는 경향이 줄어든다.


6. 윈도우의 경우 데이터 로더를 수정해야 한다. (나는 에러가 나길래...)


dataloader = DataLoader(ImageFolder(opt.image_folder, img_size=opt.img_size),
batch_size=opt.batch_size, shuffle=False)


여러 쓰레드로 데이터로딩하는게 지원이 안되더라... 그래서 지웠다.


7. 80 class로 되어 있는 부분 수정


detections = non_max_suppression(detections, len(classes), opt.conf_thres, opt.nms_thres)


아무런 것도 없이 80이라고 되어있던데, 좀더 범용적으로 사용할 수 있게 len(classes)로 고치자!


이제 돌려보자


우리는 default 파라미터를 다 수정해줬기 떄문에 python detect.py명령어만으로 실행이 된다. ^_^



/output 폴더에 결과가 들어가있을 것인데, 가서 보자




ㅋㅋㅋㅋㅋㅋ 성능이 쓰레기다


그래도 데이터를 고작 20장 모았고, 20장으로 10개의 class를 판별해야하는 와중에 이정도의 결과이니 괜찮다고.. 생각한다.


데이터 한 1000장 정도 모으면 그래도 확실히 훨씬 나아질것이다.




(기억을 더듬어서 쭉 후기를 적어보았는데 만약에 잘 안되는 부분이 있으면 댓글 달아주세요~)





*


윈도우 사용자분중에 안되는 분 계시면

dataloader = torch.utils.data.DataLoader(
ListDataset(train_path), batch_size=opt.batch_size, shuffle=True, num_workers=opt.n_cpu
)

부분을

dataloader = torch.utils.data.DataLoader(
ListDataset(train_path), batch_size=opt.batch_size, shuffle=True, num_workers=0
)

으로 고쳐보시기 바랍니다.