[OpenCV] 아보카도 필통 data Augmentation !
오늘은 영상처리에서 중요한... 이미지 증강을 해보았다 !
어이가 없지만 아무튼 그렇다
데이터셋이 별로 없기도 하고
나에게는 아보카도 정면 사진 2장만 있다면 ??
그렇다면 이미지 증강을 통해 데이터셋을 늘리고 학습시켜보자 ~
새싹에서 미니 과제로 주어졌다
그래서 오늘 하루 재밌게 코딩했던 것 같다
챗지피티로 해결이 안 됐던 문제를 해결해서 기분이 좋다
(사실 엥 이게 왜 안 댐...? 이게..왜 댐? 이런 느낌으로 해결하긴 했지만)
# 1. 배경: 흰색 책상, 우드 테이블
# 2. 데이터 증식 조건
# 2-0 스마트폰으로 사진 촬영 후 이미지 크기를 줄여주자(224x224)
# 2-1 rotate: 회전(15~20도)범위 안에서 어느 정도 각도를 넣어야 인식이 잘 되는가?
# 2-2 hfilp, vfilp: 도움이 되는가?
# 2-3 resize, crop: 가능하면 적용해 보자
# 2-4 파일명을 다르게 저장하자 cf) jelly_wood_rotate_30.jpg
# 2-5 클래스 별로 폴더를 생성
# 2-6 데이터를 어떻게 넣느냐에 따라 어떻게 동작되는지 1~2줄로 요약
# 구성 순서
# 1. 촬영
# 2. 이미지를 컴퓨터로 복사, resize
# 3. 육안으로 확인, 이렇게 사용해도 되는가?
# 4. 함수들을 만든다. resize, crop, hfilp, vfilp, 원본 파일명을 읽어서 파일명을 생성하는 기능은 모든 함수에 있어야 함
# 5. 단일 함수를 검증
# 6. 함수를 활용해서 기능 구현
# 7. 테스트(경우의 수)
# 8. 데이터셋을 teachable machine 사이트에 올려서 테스트
# 9. 인식이 잘 안 되는 케이스를 분석하고 케이스 추가 1~8에서 구현된 기능을 이용
이런 느낌의 과제였고... 나는
귀여운 아보카도 필통을 가지고 있어서 너로 정했다
^_^
이미지 불러오기
우선 이미지를 불러와야 한다.
import cv2, sys 뭐 이런건 국룰이라 패스
# 이미지 불러오기
whiteorg = cv2.imread('org/org_white.jpg')
woodorg = cv2.imread('org/org_wood.jpg')
if whiteorg is None:
sys.exit(f"{whiteorg} 이미지를 불러오지 못했습니다.")
if woodorg is None:
sys.exit(f"{whiteorg} 이미지를 불러오지 못했습니다.")
# 이미지 Resize
org_white = cv2.resize(whiteorg, (224, 224), interpolation=cv2.INTER_AREA)
org_wood = cv2.resize(woodorg, (224, 224), interpolation=cv2.INTER_AREA)
흰 배경과 나무 배경 아보카도 정면 필통을 가져왔다.
이걸 이제 이미지 증강(data augmentation)을 통해 데이터를 늘려보도록 하겠다.
사진 저장하는 함수
우선 여러 방식으로 이미지를 늘리는 함수를 만들기 전, 사진을 저장하는 공통적인 부분을 함수로 빼주겠다.
def createFile(folderName, filePrefix, images):
"""
여러 이미지 데이터를 주어진 폴더에 저장하는 함수.
:param folderName: 저장할 폴더 이름 (문자열)
:param filePrefix: 파일 이름에 붙일 접두사 (문자열)
:param images: 저장할 이미지 데이터 (리스트)
"""
# 폴더가 없으면 생성
if not os.path.exists(folderName):
os.makedirs(folderName)
# 각 이미지에 대해 파일 이름 생성 및 저장
for i, image in enumerate(images):
# 파일 이름 생성
output_path = os.path.join(folderName, f"{filePrefix}_{i}.jpg")
# 이미지 저장
if cv2.imwrite(output_path, image):
print(f"파일이 성공적으로 저장되었습니다: {output_path}")
else:
print(f"파일 저장에 실패했습니다: {output_path}")
이런식으로 폴더를 만들고 이름을 지정해서 이미지를 저장할 수 있다.
이렇게 빡세게 분류하는 이유는...
나중에 필요없어진 이미지를 삭제하기 쉽게 하기 위함이다.
이미지 Crop
사진을 랜덤으로 10장 정도 잘라보겠다.
너무 작게 잘리면 의미가 없기 때문에 80퍼 크기로 자르게 설정했다.
def randomCrop(img, sizing, filePrefix, num_crops=10):
height, width, _ = img.shape
crops = []
for _ in range(num_crops):
# 이미지 크기의 비율로 자를 크기 설정
crop_height = random.randint(int(height * sizing), height)
crop_width = random.randint(int(width * sizing), width)
if height < crop_height or width < crop_width:
raise ValueError("자르려는 사이즈가 이미지보다 큽니다.")
# 랜덤으로 crop할 좌표 선택
x = random.randint(0, width - crop_width)
y = random.randint(0, height - crop_height)
# 이미지 자르기
cropped_img = img[y:y+crop_height, x:x+crop_width]
crops.append(cropped_img)
# 크롭된 이미지 저장
createFile('randomCrop', filePrefix, crops)
return crops
sizing을 0.8로 지정해서 호출하면 된다. (sizing=0.8 기본값 설정하는거 까먹었네.ㅋ. 귀찬으니까 패스)
그럼 뭐 이런식으로 잘려져 저장된다.
반전(flip)
좌우반전, 상하반전, 좌우 + 상하반전 세 가지를 만들었다. ㅋㅋ
def hFlip(img, filePrefix):
flipped_img = cv2.flip(img, 1)
# 플립된 이미지 저장
createFile('hFlip', filePrefix, [flipped_img])
return flipped_img
def vFlip(img, filePrefix):
flipped_img = cv2.flip(img, 0)
# 플립된 이미지 저장
createFile('vFlip', filePrefix, [flipped_img])
return flipped_img
def vhFlip(img, filePrefix):
flipped_img = cv2.flip(img, -1)
# 플립된 이미지 저장
createFile('vhFlip', filePrefix, [flipped_img])
return flipped_img
차이가 보이십니까? ?
네..
회전(rotate)
이번엔 0도부터 360도까지 돌릴 수 있는 rotate를 사용해보겠다.
0도와 360도는 의미가 없어서 10~350도를 저장하도록 설정했다.
def rotate(img, filePrefix):
rotated_images = []
height, width = img.shape[:2]
center = (width // 2, height // 2)
for angle in range(10, 360, 10):
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_img = cv2.warpAffine(img, rotation_matrix, (width, height), borderMode=cv2.BORDER_REPLICATE)
rotated_images.append(rotated_img)
# 회전된 이미지 저장
createFile('rotate', filePrefix, rotated_images)
return rotated_images
제법 귀엽군 ㅋ..
데이터가많이 생겨서 기분이 좋아요.
대비(contrast)
어두운 거랑 밝은 거 구분을 위해...
이것도 써봤다.
def contrast(img, filePrefix):
contrasts = []
for i in range(5, 15, 1):
adjusted = cv2.convertScaleAbs(img, alpha=(i/10), beta=0)
contrasts.append(adjusted)
# 대비 조정된 이미지 저장
createFile('contrast', filePrefix, contrasts)
return contrasts
간단한 코드.
0.5 ~ 1.5가 적합한 것 같아서 alpha값을 0.1 단위로 조정했음
for문에 소수점 넣긴 좀 그러니까 i/10 해봤는데 옹 이게 되네
색 변환(ColorShifting)
색 변화를 좀 줘봤다.
이것도 필요할 것 같아서..?
ef colorShifting(img, filePrefix):
shifted_images = []
for value in range(10, 60, 10):
shifted_img = img.astype(np.int32)
for i in range(3): # 0: Blue, 1: Green, 2: Red
shift = random.randint(-value, value)
shifted_img[:, :, i] = np.clip(shifted_img[:, :, i] + shift, 0, 255)
shifted_img = shifted_img.astype(np.uint8)
shifted_images.append(shifted_img)
createFile('colorShifting', filePrefix, shifted_images)
return shifted_images
컬러 채널을 랜덤으로 조정해서 뭐.. 이렇게 되었습니당.
10부터 50까지 조정하게 해놨고 아주 알록달록 클럽 아보카도 완성
마무리는 이런 코드로 했음
여기서 1시간 넘게 썼는데....
ef CreateImages(images, filePrefix):
randomCrop(images, 0.8, filePrefix)
hFlip(images, filePrefix)
vFlip(images, filePrefix)
vhFlip(images, filePrefix)
rotate(images, filePrefix)
contrast(images, filePrefix)
colorShifting(images, filePrefix)
print("이미지 생성이 완료되었습니다.")
def CreateAll():
CreateImages(org_white, "org_white")
CreateImages(org_wood, "org_wood")
CreateAll()
사실 wood랑 white를 같이 쓰려고 배열을 만들어서 for문을 돌려봤는데 계속 생성이 안 되길래..
그냥 삽질하다가 여러 시도 끝에 성공해서 오 굳. 했다.
아무튼 재미가 있었다 ~
아래는 깃허브
기능 하나마다 git add . git commit -m"Feat: ...." git push origin main 했다 ㅎㅁㅎ
잔디 많이 심음. .
이슈랑 브랜치 파서 pr 날릴까 했는데 너무 귀찮아서 그냥 했다.
https://github.com/seacrab808/avocadoDataAugmentation
GitHub - seacrab808/avocadoDataAugmentation: openCV로 데이터 증강 - 아보카도 필통 학습 데이터 만들기
openCV로 데이터 증강 - 아보카도 필통 학습 데이터 만들기 . Contribute to seacrab808/avocadoDataAugmentation development by creating an account on GitHub.
github.com
뭐 이런식으로 결합해서 사용하는거니까 해보려 했는데 그냥 더하면 되는거라 냅뒀다 (걍 노가다임)
결과
어.. 네 이렇게 아보카도 필통의 압승이 있었구요
아보카도 이미지가 너무 적어서 의미는 없지만 네 기분은 좋네요 ㅎㅁㅎ
그래도 90퍼 이상의 정확도를 보여주는군녀.