오늘의 한 일
- 최종 프로젝트 - yolov5 사물인식 모델 학습 관련
- 소셜 로그인
YOLOv5 모델 커스텀 트레이닝
1번 로보플로우를 이용하여 수작업 라벨링과
2번 방법에서 고민하던 도중에 아무리 생각해도 이미 라벨 박스 데이터가 있는데 이것을 이용하지 않으면 안될 것 같아 2번 방법을 더욱 고민해보았다.
json형태의 라벨을 yolov5에 맞춰 커스텀 하는 방법에 대해 고민중, 상호 튜터님께 질문드려 답을 얻을 수 있었다.
여기서 우리가 원하는 것은 x, y 좌표와 width height를 가로 세로 비율로 나눠서 0~1의 값으로 나열하는 형태의 txt파일로 저장하는것.
import glob
import json
json_list = glob.glob('*.json')
for json_file in json_list:
with open(json_file, 'r', encoding='UTF-8') as f:
data = json.load(f)
width = 1920
height = 1080
# x, y, 너비, 높이 구하기
label_x = data['labelingInfo'][1]['box']['location'][0]['x']/width
label_y = data['labelingInfo'][1]['box']['location'][0]['y']/height
label_width = data['labelingInfo'][1]['box']['location'][0]['width']/width
label_height = data['labelingInfo'][1]['box']['location'][0]['height']/height
# 소수점 6번째까지
# label_x = round(data['labelingInfo'][1]['box']['location'][0]['x']/width, 6)
# label_y = round(data['labelingInfo'][1]['box']['location'][0]['y']/height, 6)
# label_width = round(data['labelingInfo'][1]['box']['location'][0]['width']/width, 6)
# label_height = round(data['labelingInfo'][1]['box']['location'][0]['height']/height, 6)
# 확인용
# print(f'0, {label_x}, {label_y}, {label_width}, {label_height}')
# .txt파일 저장
file_name = json_file.split('.')[0] + '.txt'
with open(file_name, 'a', encoding='UTF-8') as f1:
f1.write(f'0 {label_x} {label_y} {label_width} {label_height}')
이상의 함수로 class(0), x, y, width, height 의 형태를 가진 txt파일로 저장할 수 있었다.
사실 생각해볼 수 있는 방법이였던것 같은데 glob라던지 json 모듈, write 함수 등 직접 써보질 않아서 감이 안잡혔던 것 같다 ㅠㅠ
어쨋든 라벨링을 txt파일로 교체 후 train - valid - test 폴더에 70 20 10의 비율로 이미지와 라벨링 데이터를 넣고 data.yaml파일을 만들어주면 학습준비 완료
data.yaml
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
train: sample/train/images
val: sample/valid/images
# Classes
names:
0: erosion_ulcer
데이터 학습은 petdic 프로젝트때 한 것과 같이 코랩 환경에서 진행할 예정.
이미지 크기가 커서 epoch 조절을 얼마나 해야할지, 클래스는 몇 개를 넣어야 할지 고민이다..
일단 실험용으로 이미지 100장을 추려서 학습을 진행시켜봤는데 pt모델로 검증을 해보니 다 no detection만 뜨던데 이것도 문제다..
트러블슈팅
yolov5에서 요구하는 x, y의 값이 꼭지점이 아니라 센터값이였던거임~~~
label x label y값에 width, height를 1/2한 값을 더해주는것으로 해결
소셜 로그인 관련
일단 저번 프로젝트때 왜 발급까지만 성공했다고 하는지 알겠다. 발급과 계정 생성까지는 가능했는데
이걸 어떻게 토큰을 유지한 채로 프론트엔드(아마도 index.html)페이지로 보내지? views.py에서 해결해야 할 것 같은데?
자잘한건 참조했던 페이지와 동일하니 views.py만
참조 : https://velog.io/@kjyeon1101/%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%EA%B8%80
user/views.py
# 소셜 로그인 연습중
from django.conf import settings
from allauth.socialaccount.models import SocialAccount
from dj_rest_auth.registration.views import SocialLoginView
from allauth.socialaccount.providers.google import views as google_view
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from django.http import JsonResponse
import requests
from json.decoder import JSONDecodeError
from django.shortcuts import redirect
state = getattr(settings, 'STATE')
BASE_URL = 'http://localhost:8000/'
GOOGLE_CALLBACK_URI = BASE_URL + 'user/google/callback/'
def google_login(request):
"""
Code Request
"""
scope = "https://www.googleapis.com/auth/userinfo.email"
client_id = getattr(settings, "SOCIAL_AUTH_GOOGLE_CLIENT_ID")
return redirect(f"https://accounts.google.com/o/oauth2/v2/auth?client_id={client_id}&response_type=code&redirect_uri={GOOGLE_CALLBACK_URI}&scope={scope}")
def google_callback(request):
client_id = getattr(settings, "SOCIAL_AUTH_GOOGLE_CLIENT_ID")
client_secret = getattr(settings, "SOCIAL_AUTH_GOOGLE_SECRET")
code = request.GET.get('code')
"""
Access Token Request
"""
token_req = requests.post(
f"https://oauth2.googleapis.com/token?client_id={client_id}&client_secret={client_secret}&code={code}&grant_type=authorization_code&redirect_uri={GOOGLE_CALLBACK_URI}&state={state}")
token_req_json = token_req.json()
error = token_req_json.get("error")
if error is not None:
raise JSONDecodeError(error)
access_token = token_req_json.get('access_token')
"""
Email Request
"""
email_req = requests.get(
f"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={access_token}")
email_req_status = email_req.status_code
if email_req_status != 200:
return JsonResponse({'err_msg': 'failed to get email'}, status=status.HTTP_400_BAD_REQUEST)
email_req_json = email_req.json()
email = email_req_json.get('email')
"""
Signup or Signin Request
"""
try:
user = User.objects.get(email=email)
# 기존에 가입된 유저의 Provider가 google이 아니면 에러 발생, 맞으면 로그인
# 다른 SNS로 가입된 유저
social_user = SocialAccount.objects.get(user=user)
if social_user is None:
return JsonResponse({'err_msg': 'email exists but not social user'}, status=status.HTTP_400_BAD_REQUEST)
if social_user.provider != 'google':
return JsonResponse({'err_msg': 'no matching social type'}, status=status.HTTP_400_BAD_REQUEST)
# 기존에 Google로 가입된 유저
data = {'access_token': access_token, 'code': code}
accept = requests.post(
f"{BASE_URL}user/google/login/finish/", data=data)
accept_status = accept.status_code
if accept_status != 200:
return JsonResponse({'err_msg': 'failed to signin'}, status=accept_status)
accept_json = accept.json()
accept_json.pop('user', None)
return JsonResponse(accept_json)
except User.DoesNotExist:
# 기존에 가입된 유저가 없으면 새로 가입
data = {'access_token': access_token, 'code': code}
accept = requests.post(
f"{BASE_URL}user/google/login/finish/", data=data)
accept_status = accept.status_code
if accept_status != 200:
return JsonResponse({'err_msg': 'failed to signup'}, status=accept_status)
accept_json = accept.json()
accept_json.pop('user', None)
return JsonResponse(accept_json)
class GoogleLogin(SocialLoginView):
adapter_class = google_view.GoogleOAuth2Adapter
callback_url = GOOGLE_CALLBACK_URI
client_class = OAuth2Client
계정이 없다면 회원가입 후, 있다면 바로 토큰 발급
JsonResponse로 return하므로 마지막 페이지가 각 토큰을 표시한 페이지인것 같다.
이제 이 발급받은 토큰들을 로컬스토리지에 저장한 채로 프론트엔드의 index.html로 보내야 하는데.. 어떻게?