언덕에 누워 생각하기

(개선)2025 설악그란폰도 사진 찾기 위한 여정(파이썬,AI,ChatGPT) 본문

프로그래밍

(개선)2025 설악그란폰도 사진 찾기 위한 여정(파이썬,AI,ChatGPT)

생각하기 2025. 8. 5. 16:49
반응형

순서

사진(썸네일) 다운로드 - 헬멧 학습 시키기 - 헬멧 찾은 뒤 파란색인지 확인 - 파란색 헬멧 중 파란 옷 입었는지 확인 - 사람이 확인 후 원본 사진 다운로드 - 배번호 지정

 

 

설악그란폰도는 참가 인원도 엄청나서 사진을 모두 찾기란 많이 힘들다.

거의 포기 상태였다가 명색이 그래도 컴퓨터로 먹고 사는 사람인지라 컴퓨터의 힘을 빌리기로 했다.

 

프로그래밍을 안한지 엄청 오래되었고, 그마저도 C++이 주특기였기에 파이썬은 익숙하지가 않다.

그렇다면? ChatGPT에게 물어보면... ㅎㅎ

 

1. 사진 다운로드 받기

- 공식 사이트? - 기록 보는 사이트

우선 사진을 다운로드 받아야 한다. 그래야 더 빠르다. 당연. 다른 곳은 그냥 다운로드 받을 수 있는데, 공식 홈페이지는 좀 다르다. 엄청나게 많은 파일을 페이지별로 넘어가며 찾아서 다운로드 해야 한다. 미쳤어... 총 61,281장의 사진이 있다. 페이지 몇 개와 사진 몇 개를 보니 주소가 이렇다.

 

https://photo2.spct.kr/2025/2025051702

페이지의 썸네일은 https://photo2.spct.kr/2025/2025051702Thumb 이다.

 

https://photo2.spct.kr/2025/2025051702 에서 사진을 모두 다운로드 받으면 된다. --> Thumb에서 다운로드 받는 것으로 수정. 그래야 훨씬 더 빠르다.

ChatGPT에게 물어봤다.

BeautifulSoup으로 파싱해서 이미지 링크를 알아내고 다운로드 받는 코드를 알려줬다. 당연하지.

download-fast.py
0.00MB

 

파이썬 소스가 실행되는 디렉토리에서 spct 폴더를 만들고 거기에 다운로드 받는다.

무지 막지 느리다. 하루 이상 걸림.Thumb에서 받으니 많이 빨라짐. 그나마 멀티쓰레드이고 다운로드 실패하면 재시도 한다.

 

- 공식 사이트2(flickr)

설악그란폰도 홈페이지에 있는 공식 플리커. 플리커는 동적으로 생성되기 때문에 까다롭다. 스크롤을 내려야 그 때 사진 보이고 링크가 동적으로 생성된다. 각 페이지당 100장씩 사진이 있다. 많네...

결국 해결. 단, 크롬드라이버 있어야 함.

download_flickr.py
0.00MB

 

flickr_originals 폴더에 사진 저장. 그런데 재미있는거 발견. 동적으로 생성된 html 원본을 보니 이런 주석이 있다.

나도 채용 신청을???

<!--                 _
		           . -  ` : `   '.' ``  .            - '` ` .
		         ' ,gi$@$q  pggq   pggq .            ' pggq
		        + j@@@P*\7  @@@@   @@@@         _    : @@@@ !  ._  , .  _  - .
		     . .  @@@K      @@@@        ;  -` `_,_ ` . @@@@ ;/           ` _,,_ `
		     ; pgg@@@@gggq  @@@@   @@@@ .' ,iS@@@@@Si  @@@@  .6@@@P' !!!! j!!!!7 ;
		       @@@@@@@@@@@  @@@@   @@@@ ` j@@@P*"*+Y7  @@@@ .6@@@P   !!!!47*"*+;
		     `_   @@@@      @@@@   @@@@  .@@@7  .   `  @@@@.6@@@P  ` !!!!;  .    '
		       .  @@@@   '  @@@@   @@@@  :@@@!  !:     @@@@7@@@K  `; !!!!  '  ` '
		          @@@@   .  @@@@   @@@@  `%@@@.     .  @@@@`7@@@b  . !!!!  :
		       !  @@@@      @@@@   @@@@   \@@@$+,,+4b  @@@@ `7@@@b   !!!!
		          @@@@   :  @@@@   @@@@    `7%S@@hX!P' @@@@  `7@@@b  !!!!  .
		       :  """"      """"   """"  :.   `^"^`    """"   `""""" ''''
		        ` -  .   .       _._    `                 _._        _  . -
		                , ` ,glllllllllg,    `-: '    .~ . . . ~.  `
		                 ,jlllllllllllllllp,  .!'  .+. . . . . . .+. `.
		              ` jllllllllllllllllllll  `  +. . . . . . . . .+  .
		            .  jllllllllllllllllllllll   . . . . . . . . . . .
		              .l@@@@@@@lllllllllllllll. j. . . . . . . :::::::l `
		            ; ;@@@@@@@@@@@@@@@@@@@lllll :. . :::::::::::::::::: ;
		              :l@@@@@@@@@@@@@@@@@@@@@l; ::::::::::::::::::::::;
		            `  Y@@@@@@@@@@@@@@@@@@@@@P   :::::::::::::::::::::  '
		             -  Y@@@@@@@@@@@@@@@@@@@P  .  :::::::::::::::::::  .
		                 `*@@@@@@@@@@@@@@@*` `  `  `:::::::::::::::`
		                `.  `*%@@@@@@@%*`  .      `  `+:::::::::+`  '
		                    .    ```   _ '          - .   ```     -
		                       `  '                     `  '  `
		
			You're reading. We're hiring.
			https://flickr.com/jobs/
		
		-->

 

 

2. AI님에게 헬멧 찾기 학습

이제 그 많은 사진에서 내 사진을 찾아야 한다. 나는 헬멧을 파란색으로 썼다. 많이 쓰지 않는 색이다. ㅇㅋ. 이걸 찾아야 한다. AI에게 헬멧을 찾도록 한 뒤, 그 헬멧이 파란색인지 확인해야 한다. AI 모델을 찾았는데, 없네. roboflow에서 보니 한국사람 같은데, 헬멧 사진을 무려 5001장이나 올리고 어노테이션 해주셨다. 만세!

검색어: bicycle helmet

Author: Sunjune Kim

1년 전에 해주셔서 덕분에 잘 썼어요!

데이터셋을 다운로드 받아서 학습시켜야 한다. 내가 쓰는 컴퓨터는 GPU도 없다. CPU로 하니 Epoch을 50으로 했을 때, 도저히 답이 안나온다. 이럴 땐??? 구글 Colab에서 GPU로 학습시켜보니 세상에... 엄청 빠르다.

 

학습시키는 코드

# 설치
!pip install ultralytics roboflow

# roboflow 데이터 다운로드
from roboflow import Roboflow
rf = Roboflow(api_key="-------------")  # ✅ 너의 API 키
project = rf.workspace("sunjune-kim").project("bicycle-helmet-2azcz")
dataset = project.version(1).download("yolov8")  # YOLOv8 포맷 다운로드

# 다운로드된 경로 확인
print("DATA PATH:", dataset.location)

# YOLOv8 모델 학습
from ultralytics import YOLO

model = YOLO("yolov8n.pt")  # 사전학습된 작은 모델 사용
# 정확한 경로 사용
model.train(data=f"{dataset.location}/data.yaml", epochs=50, imgsz=640)

# 학습된 모델 다운로드
from google.colab import files
files.download('runs/detect/train/weights/best.pt')

 

이제 best.pt라는 파일로 모델을 다운로드 했다. 헷갈리니 

bicycle-helmet.pt 라는 이름으로 바꿔줬다. 넌 춘식이여... 아니 넌 자전거 헬멧 찾기 모델이여.

bicycle-helmet.pt
5.96MB

 

3. 이제 파란 헬멧을 찾아줘!

파일이 너무 많아서 모두 다운 받으면서 찾으려면 쉬운 방법이... 대충 10,000개 쯤 폴더에 넣고 찾은 것만 따로 빼 놓는게 좋겠다. 폴더 구조는 아래와 같다. 현재 폴더에는 파란헬멧 찾을 파이썬 소스와 모델(bicycle-helmet.pt)을 넣는다.

현재폴더 --spct---- Verifying

                          +--Verified

                          +--BlueHelmet

파란 헬멧을 찾을 사진들만 Verifying에 넣는다. 모든 사진을 넣어도 되겠지만, 사진 다운로드 받는데 엄청난 시간이 들어서 그럴 수 없다. 우선 다운로드 받은 것만 찾으려면 Verifying에 넣어야만 한다. 그리고, 검사가 끝난 파일은 찾았든 못 찾았든 모두 Verified로 옮겨진다. 파란 헬멧을 찾은 사진만 BlueHelmet에 복사한다.

파란 헬멧을 찾는 파이썬 소스

blue-helmet.py
0.00MB

 

우와... 파란 헬멧이 무척 많다. 아... 많다. 그래서, 나는 파란색 옷을 입었으니 파란헬멧 사진 중에서 파란 옷을 찾는 것을 추가했다.

blue.py
0.00MB

 

결국.... 찾았다.... 내 사진....

 

이제 썸네일 사진을 찾았으니 원본 파일을 다운로드 받아야 한다.

GUI로 되어 있다. 썸네일 사진을 드래그&드랍하면 원본을 다운로드 받을 수 있다.

OriginalDownload.exe
10.98MB

 

소스코드

OriginalDownload.py
0.01MB

 

 

한 가지 문제가 더 있는데... 나처럼 배번호가 일부 가려지면 사진기록증에 사진이 안들어간다. 또한, 배번호(BibNo)로 사진을 찾을 수 없다. 찾은 사진들에게 배번호를 등록하는 파이썬. GUI이다. 배번호를 입력하고 사진들을 드래그&드랍한 뒤 "배번호 전송" 버튼을 누르면 된다.

register_bibNo.exe
11.83MB

 

소스코드

register_bibNo.py
0.01MB

 

CUI 버전... 기록 때문에 남김... 코드 수정해 가면서 실행시켜야 함.

import requests

# 요청 보낼 URL
url = "https://time.spct.kr/PhotoBibUpdate.php"

# 입력할 값
bib_number = "0000"	# 배번호 입력... 숫자로...
filename = "----.JPG"	# 사진파일 이름 입력...

# 폼 데이터 구성
form_data = {
    "EVENT_NO": "2025051702",
    "TargetYear": "2025",
    "photosection": "8",
    "currentPage": "1",
    "PhotoCurrentPage": "1",
    "photofilename": filename,
    "NewBIB_NO": bib_number
}

# 기본 헤더 (크롤링 방지 우회 목적)
headers = {
    "User-Agent": "Mozilla/5.0",
    "Referer": "https://time.spct.kr/photos.php",
}

# POST 요청
response = requests.post(url, data=form_data, headers=headers)

# 결과 확인
if response.status_code == 200:
    html = response.text

    # 응답에 BIB 번호 또는 성공 관련 키워드가 포함되었는지 확인
    if bib_number in html or "성공" in html or "등록" in html:
        print(f"✅ 배번 {bib_number} 등록 성공!")
    elif "배번을 넣어 주세요" in html or "실패" in html:
        print(f"❌ 배번 등록 실패 (입력 오류 또는 서버 거부)")
    else:
        print("⚠️ 등록 결과가 명확하지 않음 (응답 HTML 일부 미리보기):")
        print(html[:500])
else:
    print(f"❌ 요청 실패: HTTP 상태 코드 {response.status_code}")

 

끝.

반응형