음식 사진만 찍으면 칼로리와 영상소를 분석하고 기록해주는 서비스가 있으면 좋겠다. 사람들이 식단 관리, 건강 관리에 관심이 많다. 그런데 내가 먹은 음식의 칼로리를 계산하기도 어렵고, 하나 하나 기록하기도 어렵다. 정확한 계산 보다는 편리하게 적당히 계산해주는 도구가 있으면 좋겠다.
막상 시작하려고 하면 어디부터 손대야 할지 감이 안 잡히는데, 직접 이것저것 찾아보면서 삽질하는 과정을 기록해보고자 한다.
복잡해 보이지만 전체 흐름을 단순화하면 이렇다.
이 흐름만 이해하면 어디서부터 공부해야 하는지 훨씬 명확해진다.
사진 분석에서 제일 먼저 필요한 건 음식 이름 분류 모델이다.
이미 커뮤니티에 완성된 사전학습 모델들이 많이 올라와 있다.
그냥 가져다 쓰면 된다. 음식 사진 한 장 넣으면 “파스타”, “라멘”, “치킨 윙” 같은 식으로 음식 이름이 나온다.
참고로 Food-101은 모델이 아니라 ‘사진 모음 데이터셋’이다.
하지만 워낙 유명해서 이걸로 학습된 모델들도 “Food-101 모델”이라고 부르곤 한다.
모델이 음식 이름을 뱉어주면, 이제 그걸 바탕으로 영양 정보를 찾으면 된다.
사진만으로 “얼마나 담겨 있는지”는 사실 정확히 알기 어려워서, 초반에는
1인분 기준 칼로리 또는 사용자가 직접 gram 입력 방식으로 가는 게 현실적이다.
나중에는 동전이나 손가락, 젓가락, 신용카드 등 그 크기가 일정한 것과 같이 사진을 찍고, 이것들과 크기를 비교해서 추측하는 방법이 있다.
누가 한식에 대해 미리 해둔게 있다. → 링크
일단 이미 학습된 오픈 소스 모델 이용해서 음식 사진 넣고, 음식 이름 받아오는 걸 해보자.
Python 환경에서 transformers (Hugging Face)와 torch (PyTorch) 라이브러리를 사용한다.
라면이 먹고 싶어서, 위키백과 라면의 사진을 넣어보았다.

결과는
1. ramen 41.17%
2. miso_soup 15.13%
3. hot_and_sour_soup 11.00%
로 잘 나왔다.!
python -m venv foodenv
foodenv\Scripts\activate
pip install transformers torch pillow requests
import torch
from transformers import ViTImageProcessor, ViTForImageClassification
from PIL import Image
import requests
from io import BytesIO
class FoodClassifier:
def __init__(self):
# Food-101 데이터셋으로 사전 학습된 ViT 모델 로드
# model_name을 변경하여 다른 아키텍처(ResNet, EfficientNet 등)로 교체 가능
self.model_name = 'nateraw/food'
print(f"Loading model: {self.model_name}...")
# 이미지 전처리기 (Resize, Normalize 등을 담당)
self.processor = ViTImageProcessor.from_pretrained(self.model_name)
# 분류 모델 로드
self.model = ViTForImageClassification.from_pretrained(self.model_name)
# 평가 모드로 전환 (Dropout 등을 비활성화)
self.model.eval()
def predict(self, image_source, top_k=3):
"""
이미지를 입력받아 상위 k개의 예측 결과를 반환합니다.
image_source: 이미지 파일 경로(str) 또는 URL(str)
"""
# 1. 이미지 로드
image = self._load_image(image_source)
if image is None:
return
# 2. 전처리 (Preprocessing)
# PyTorch 텐서로 변환 및 배치 차원 추가 (C, H, W -> 1, C, H, W)
inputs = self.processor(images=image, return_tensors="pt")
# 3. 추론 (Inference)
with torch.no_grad(): # 그래디언트 계산 비활성화 (메모리 절약)
outputs = self.model(**inputs)
# 4. 결과 후처리 (Post-processing)
logits = outputs.logits
probabilities = torch.nn.functional.softmax(logits, dim=-1)[0]
# 상위 k개 확률과 인덱스 추출
top_probs, top_indices = torch.topk(probabilities, top_k)
results = []
for prob, idx in zip(top_probs, top_indices):
label = self.model.config.id2label[idx.item()]
confidence = prob.item()
results.append((label, confidence))
return results
def _load_image(self, source):
try:
if source.startswith('http'):
response = requests.get(source)
img = Image.open(BytesIO(response.content))
else:
img = Image.open(source)
return img.convert("RGB") # PNG 등의 투명 채널 제거
except Exception as e:
print(f"Error loading image: {e}")
return None
# --- 실행 예시 ---
if __name__ == "__main__":
# 분류기 인스턴스 생성
classifier = FoodClassifier()
# 테스트할 이미지 URL (피자 이미지)
test_img_url = "test.jpg"
print("\nAnalyzing image...")
predictions = classifier.predict(test_img_url)
print("\n[Analysis Result]")
for i, (food, conf) in enumerate(predictions):
print(f"{i+1}. {food}: {conf*100:.2f}%")
https://data.vision.ee.ethz.ch/cvl/datasets_extra/food-101/
https://aihub.or.kr/aihubdata/data/view.do?currMenu=115&topMenu=100&aihubDataSe=data&dataSetSn=74
https://github.com/Cheng-K/FoodNet/tree/main
https://github.com/Nikhilchakravarthy1303/FoodCalorieEstimation