달러💰 이렇게 비싸져도 되냐?! AWS를 탈출하고 비용절약하자!
AWS를 사용해오다 자체 서버로 전환한 과정과 이유를 공유합니다. 비용 절감, 보안 강화, 물리 서버 운영의 장점, 그리고 다단계 인증(MFA)을 적용한 방법을 소개합니다. B2B 서비스의 특성을 고려한 성공적인 서버 운영 전략을 확인해보세요.
FastAPI, rembg, extcolors 라이브러리를 활용하여 이미지 처리 API를 구축하는 방법을 단계별로 소개합니다. 사진에서 배경을 제거하고 주요 색상을 추출하는 API를 만들어 다양한 애플리케이션에서 활용해보세요.
pip install fastapi pip install uvicorn
main.py
에 아래 코드를 작성합니다:# 패키지 import from fastapi import FastAPI import uvicorn # # Helper 함수 작성 # app = FastAPI() # # Endpoint 함수 작성 # # 루트 페이지("/") 접속 시 "Hello": "world" return @app.get("/") def read_root(): return { "Hello": "world" } if __name__ == "__main__": uvicorn.run(app, host='0.0.0.0', port=5000)
'0.0.0.0'
, port는 5000
으로 설정하겠습니다.uvicorn main:app --reload
INFO: Will watch for changes in these directories: ['PROJECT_DIRECTORY_PATH'] INFO: Uvicorn running on <http://127.0.0.1:8000> (Press CTRL+C to quit) INFO: Started reloader process [9982] using StatReload
http://127.0.0.1:8000
에서 uvicorn 서버가 실행되고 있네요!pip install image pip install rembg pip install extcolors
import os import request from PIL import Image from extcolors import extract_from_image from rembg import remove
<img>
태그에는 src
속성이 있는데요, 아래 함수는 해당 속성에 접근하여 이미지 파일을 가져오게 됩니다.def save_image(image_url): response = requests.get(image_url) # 이미지 저장 디렉토리 download_dir_name = 'downloaded_images' if response.status_code == 200: # 파일 이름 얻기 image_name = os.path.basename(image_url) # 파일 이름에 확장자 없다면 추가 exts = [".jpg", ".jpeg", ".png", ".gif"] ext_included = False file_ext = None for ext in exts: if ext in image_name: ext_included = True file_ext = ext break if ext_included: image_name = image_name.split(file_ext)[0] + file_ext else: image_name += ".jpeg" file_ext = ".jpeg" # 저장 경로 없다면 생성 if not os.path.exists(download_dir_name): os.makedirs(download_dir_name) # 파일 경로 얻기 image_path = os.path.join(download_dir_name, image_name) # 파일 저장 with open(image_path, 'wb') as f: f.write(response.content) result = { "message": "Image saved successfully!", "status_code": response.status_code, "image_path": image_path, "image_name": image_name, "file_ext": file_ext } return result else: return { "message": "Failed to save image", "status_code": response.status_code }
def rgb_to_hex(rgb_tuple): r = int(rgb_tuple[0]) g = int(rgb_tuple[1]) b = int(rgb_tuple[2]) return '#' + hex(r)[2:].zfill(2) + hex(g)[2:].zfill(2) + hex(b)[2:].zfill(2)
http://127.0.0.1:8000/rembg/{IMAGE_URL}
위와 같은 URL에 접속하면 이미지를 저장하여 배경을 제거한 후, 이미지 경로와 이미지 이름을 반환하는 Endpoint 함수를 만들어보겠습니다.@app.get('/rembg/{url:path}') def remove_image_background(url: str): image_info = save_image(url) # 이미지 저장 image_path = image_info["image_path"] image_name = image_info["image_name"] file_ext = image_info["file_ext"] image_name_without_ext = image_name.rsplit(file_ext, 1)[0] image = Image.open(image_path).convert('RGBA') output = remove(image) convert_dir_name = 'converted_images' # 저장 경로 없다면 생성 if not os.path.exists(convert_dir_name): os.makedirs(convert_dir_name) out_image_path = os.path.join(convert_dir_name, f'{image_name_without_ext}_rembg.png') output.save(out_image_path, 'PNG') result = { "message": "The background of the image is successfully removed", "image_path": out_image_path, "image_name": image_name } return result
converted_images/
를 확인해보면 하얀색 배경이 제거된 png가 저장되어 있는 것을 확인할 수 있습니다.http://127.0.0.1:8000/extcolors/{IMAGE_URL}
위와 같은 URL에 접속하면 이미지를 저장한 뒤 색상 정보, 이미지 경로, 이미지 이름을 반환하는 Endpoint 함수를 만들어보겠습니다.http://127.0.0.1:8000/extcolors/{IMAGE_URL}?rembg=True
추가적으로, rembg
옵션을 추가하여 배경을 제거한 후 색상을 추출할 것인지 선택할 수 있도록 하겠습니다.⚠️주의사항 이미지 URL에 이미 ?가 포함되어 있는 경우 ?rembg=True는 적용되지 않습니다. 이러한 경우에는 ?를 &로 바꾸어주세요.
@app.get('/extcolors/{url:path}') def get_image_color(url: str, rembg: bool = False): if rembg: result = remove_image_background(url) image_path = result["image_path"] image_name = result["image_name"] else: result = save_image(url) image_path = result["image_path"] image_name = result["image_name"] converted_image = Image.open(image_path) colors, pixel_count = extract_from_image(converted_image, tolerance=30, limit=3) most_colors = {} pixel_output = 0 for idx, color in enumerate(colors): pixel_output += color[1] color_rgb = list(color[0]) color_ratio = round((color[1] / pixel_count) * 100, 2) color_pixels = color[1] most_colors[idx] = {} most_colors[idx]["RGB"] = color_rgb most_colors[idx]["HEX"] = rgb_to_hex(color_rgb) most_colors[idx]["RATIO"] = f'{color_ratio}%' most_colors[idx]["PIXELS"] = color_pixels result = { "rembg": rembg, "message": "Image saved successfully.", "image_name": image_name, "image_path": image_path, "most_colors": most_colors } return result
☝️상의색은 RGB(255, 255, 255)가 아니므로 1순위 색상의 퍼센티지에 영향을 주지 않았습니다.