인선미 강사님
그라디오(Gradio) 란,, 사용자가 손쉽게 머신러닝 모델을 웹 인터페이스로 배포할 수 있는 강력한 Python 라이브러리이다. (출처: ChatGPT)
import gradio as gr
from PIL import Image, ImageDraw, ImageFilter
import requests
import io
from dotenv import load_dotenv
from azure.ai.vision.imageanalysis import ImageAnalysisClient
from azure.ai.vision.imageanalysis.models import VisualFeatures
from azure.core.credentials import AzureKeyCredential
import os
라이브러리를 먼저 살펴보면
1. gradio
: 오늘의 주인공. 머신러닝 애플리케이션을 간단히 웹으로 배포할 수 있게 해준다. 강사님 피셜 프로토타입으로 써볼만한 라이브러리.
2. PIL
(Pillow): 이미지를 처리하고 편집하기 위한 Python 라이브러리.
3. requests
: Beautifulsoup 때도 많이 봤던 HTTP 요청 시 사용하는 라이브러리.
4. dotenv
: .env
파일에서 환경 변수를 쉽게 불러오는데 도움을 준다. API 키 등 민감한 정보를 안전하게 저장하기 위해 사용. (1차 프로젝트 때 원*은 동기님께 배웠다. ㅎㅎ)
5. azure.ai.vision.imageanalysis
: Azure 이미지 분석 서비스랑 상호작용하기 위한 모듈.
- ImageAnalysisClient
: Azure 클라이언트로, 이미지를 분석하는 작업 수행.
- VisualFeatures
: 어떤 시각적 특징을 분석할지 정의.
6. AzureKeyCredential
: Azure API를 사용하기 위해 필요한 인증 객체 제공.
7. os
: 환경 변수나 파일 경로 등 운영체제와 관련된 작업을 처리. (임*건 동기님 감사해요우,,)
.env
로 보안 강화하기지금 우리야 공용 아이디(?)를 사용하고 있어서 큰 상관이야 없겠다만, Azure 같은 서비스들은 사용한 만큼 과금이 되기도 하고 중요한 정보를 코드에 직접 작성하는 것(어제 김*영 동기님께 배운 하드코딩)은 보안상 매우 위험하므로 .env
파일을 사용하여 민감한 데이터를 안전하게 관리한다.
load_dotenv()
endpoint = os.getenv("AI_SERVICE_ENDPOINT")
key = os.getenv("AI_SERVICE_KEY")
load_dotenv()
: 프로젝트 루트 디렉토리에 있는 .env
파일을 불러온다.os.getenv()
: 불러온 환경변수에서 특정 값을 가져온다.
.env
파일 작성 방법AI_SERVICE_ENDPOINT=https://<내 ai service>.cognitiveservices.azure.com/ AI_SERVICE_KEY=<내 key>
이거는 최근에 알게된 사실인데 .env
파일은 git 사용시 항상 .gitignore
에 추가해서 깃 저장소에 업로드되지 않도록 해야 한다고. (뭐 당연한 거긴 한데 새삼 새롭게,,)
client = ImageAnalysisClient(endpoint=endpoint, credential=AzureKeyCredential(key))
ImageAnalysisClient
: Azure의 이미지 분석 클라이언트endpoint
: 우리가 연결할 Azure AI 서비스의 URLAzureKeyCredential(key)
: API 요청 시 인증을 위한 객체. Azure 포털에서 제공하는 API 키로 올바른 키를 제공하지 않으면 Azure 서비스에 접근할 수 없다.client
: 이제 client
객체를 사용해 Azure AI 서비스의 여러 기능을 호출할 수 있다. (client는 내가 지정한 이름이므로 원하는 이름으로 바꿀 수 있다.)쉽게 말하자면 클라이언트 초기화를 통해 AI 서비스와 대화를 시작할 준비를 완료한 것.
def analyse_image(image_file, detect_objects, detect_people):
image_file
: 분석할 이미지 파일detect_objects
: 객체 감지를 수행할지 여부를 결정하는 Boolean 값detect_people
: 사람 감지를 수행할지 여부를 결정하는 Boolean 값with open(image_file, "rb") as f:
image_data = f.read()
VisualFeatures
features = [VisualFeatures.CAPTION]
if detect_objects:
features.append(VisualFeatures.OBJECTS)
if detect_people:
features.append(VisualFeatures.PEOPLE)
VisualFeatures
: Azure 이미지 분석에서 사용할 시각적 기능을 지정하는 옵션.CAPTION
: 이미지에 대한 간단한 설명(캡션)을 생성.OBJECTS
: 이미지 속 객체를 감지.PEOPLE
: 이미지 속 사람을감지. if
문을 사용해서 사용자가 객체 감지를 활성화했는지, 사람감지를 활성화했는지에 따라 기능을 추가하도록 수업 때 작업했었다.result = client._analyze_from_image_data(image_data=image_data, visual_features=features)
_analyse_from_image_data
메서드를 사용해 이미지를 분석.features
(아까 VisualFeatures 기능을 features에 저장했었음) 옵션에 따라 결과를 반환한다.여기는 아직 나도 잘 모르고 공부 중이라서 간단하게만 작성.
image = Image.open(image_file)
draw = ImageDraw.Draw(image)
ImageDraw
객체를 생성한다.if result.caption:
output_text.append(f"Caption: {result.caption.text} (Confidence: {result.caption.confidence:.2f})")
if detect_objects and result.objects:
output_text.append("Objects:")
for obj in result.objects.list:
output_text.append(f" - {obj.tags[0].name} (Confidence: {obj.tags[0].confidence:.2f})")
box = obj.bounding_box
draw.rectangle(
[(box.x, box.y), (box.x + box.width, box.y + box.height)], outline="yellow", width=3
)
if detect_people and result.people:
output_text.append("People:")
for person in result.people.list:
output_text.append(f" - Confidence: {person.confidence:.2f}")
box = person.bounding_box
draw.rectangle(
[(box.x, box.y), (box.x + box.width, box.y + box.height)], outline="magenta", width=3
)
return image, "\n".join(output_text)
image
: 분석 결과(바운딩 박스)가 그려진 이미지.output_text
: 이미지 분석 결과가 포함된 텍스트.def remove_background(image_file, mode):
image_file
: 배경을 제거할 이미지 파일.mode
: 배경 제거 방식.backgroundRemoval
: 배경을 제거한 이미지를 반환.foregroundMatting
: 객체 분리마침 오늘 처음으로 포스트맨에 성공해서 기분이 좋음.
url = f"{endpoint}/computervision/imageanalysis:segment"
headers = {"Ocp-Apim-Subscription-Key": key, "Content-Type": "application/octet-stream"}
params = {"api-version": "2023-02-01-preview", "mode": mode}
url
: Azure 비전 API의 segment url. 이 API로 이미지에서 배경을 제거하거나 객체를 추출할 수 있다.headers
:Ocp-Apim-Subscription-Key
: Azure API 인증에 필요한 키.Content-Type
: 전송할 데이터 형식 지정. 여기서는 이미지를 바이너리 데이터로 보낸다. (application/octet-stream
)params
:api-version
: 사용 중인 API 버전.mode
: 배경 제거 방식(backgroundRemoval vs. foregroundMatting)with open(image_file, "rb") as f:
image_data = f.read()
response = requests.post(url, headers=headers, params=params, data=image_data)
requests.post
:if response.status_code == 200:
return Image.open(io.BytesIO(response.content))
else:
return None
response.status_code == 200
:BytesIO
를 통해 메모리 내 바이너리 데이터를 읽어서 Pillow 객체로 변환한다.얼굴만 블러처리하고 싶었지만,, 그거슨 나의 능력 밖의 일으로, 오늘은 사람 전체를 블러링하고, 다음주에 있을 OpenCV 강의를 기다리기로 했다. (배운 내용은 아니고 그냥 내가 해보고 싶어서 써본 코드)
def blur_faces(image_file):
image_file
: 사람을 흐리게 처리할 이미지 파일.with open(image_file, "rb") as f:
image_data = f.read()
result = client._analyze_from_image_data(image_data=image_data, visual_features=[VisualFeatures.PEOPLE])
VisualFeatures.PEOPLE
: 이미지에서 사람을 감지하는 기능.image = Image.open(image_file)
if result.people:
for person in result.people.list:
box = person.bounding_box
bbox = [int(box.x), int(box.y), int(box.x + box.width), int(box.y + box.height)]
result.people
:region = image.crop(bbox)
blurred_region = region.filter(ImageFilter.GaussianBlur(15))
image.paste(blurred_region, bbox)
image.crop(bbox)
: 사람 영역만 잘라내기ImageFilter.GaussianBlur(15)
:image.paste(blurred_region, bbox)
: 블러 처리된 사람 영역을 원본 이미지에 다시 붙여넣는다.return image
블러처리된 이미지 반환.
여기가 오랜만에 블로그를 쓴 목적이므로 좀 더 자세하게 설명해보려고 노력할 예정.
gr.Blocks()
with gr.Blocks() as demo:
gr.Blocks
: Gradio에서 여러 요소(버튼, 이미지 업로드 등등등)를 체계적으로 배치하는데 사용된다.with gr.Tab("Image Analysis"):
input_image = gr.Image(label="Input Image", type="filepath")
output_image = gr.Image(label="Output Image")
gr.Image
:label
: 요소에 표시될 이름.type="filepath"
: 업로드된 이미지의 파일 경로를 함수에 전달.detect_objects = gr.Checkbox(label="Detect Objects")
detect_people = gr.Checkbox(label="Detect People")
gr.Checkbox
: 특정 옵션을 켜거나 끌 수 있는 체크박스.analyse_button = gr.Button("Analyse")
gr.Button
: 사용자가 작업을 실행할 수 있는 버튼.analyse_button.click(
analyse_image,
inputs=[input_image, detect_objects, detect_people],
outputs=[output_image, gr.Textbox(label="Analysis Results")]
)
click
메서드: 위에서 정의한 analyse_button
클릭시 호출할 함수를 지정.inputs
: analyse_image
함수에 전달될 입력 요소(이미지와 체크박스 값)outputs
: 함수가 반환한 분석 결과를 표시할 출력 요소with gr.Tab("Background Removal"):
input_image_bg = gr.Image(label="Input Image", type="filepath")
output_image_bg = gr.Image(label="Output Image")
첫번째 탭과 동일. 변수명만 다르게 설정.
mode = gr.Radio(
choices=["backgroundremoval", "foregroundmatting"],
label="Mode",
value="backgroundremoval"
)
gr.Radio
: 오늘 배운 기능이라구.choices
: 사용 가능한 옵션 제공backgroundremoval
: 배경 제거foregroundmatting
: 전경 분리value
: 기본 값.bg_button = gr.Button("Process")
첫번째 탭과 동일
bg_button.click(remove_background, inputs=[input_image_bg, mode], outputs=output_image_bg)
remove_background
함수에 업로드된 이미지와 모드 선택 값을 전달하고 결과 이미지를 출력.with gr.Tab("Privacy Protection"):
input_image_blur = gr.Image(label="Input Image", type="filepath")
output_image_blur = gr.Image(label="Output Image")
이제는 뭐 자연스럽쥬.
blur_button = gr.Button("Blur")
blur_button.click(blur_faces, inputs=input_image_blur, outputs=output_image_blur)
demo.launch()
demo.launch(share=True)
launch
: Gradio 앱 실행.share=True
: 외부에서 접근 가능한 공유 링크 생성.gr.Blocks()
로 전체 인터페이스 구성gr.Image
: 이미지 업로드/출력gr.Checkbox
: 옵션 선택 (예: 객체 감지, 사람 감지)gr.Radio
: 모드 선택 (예: 배경 제거 방식)gr.Button
: 작업 실행 버튼click
메서드로 함수와 연결import gradio as gr
from PIL import Image, ImageDraw, ImageFilter
import requests
import io
from dotenv import load_dotenv
from azure.ai.vision.imageanalysis import ImageAnalysisClient
from azure.ai.vision.imageanalysis.models import VisualFeatures
from azure.core.credentials import AzureKeyCredential
import os
load_dotenv()
endpoint = os.getenv("AI_SERVICE_ENDPOINT")
key = os.getenv("AI_SERVICE_KEY")
client = ImageAnalysisClient(endpoint=endpoint, credential=AzureKeyCredential(key))
def analyse_image(image_file, detect_objects, detect_people):
with open(image_file, "rb") as f:
image_data = f.read()
features = [VisualFeatures.CAPTION]
if detect_objects:
features.append(VisualFeatures.OBJECTS)
if detect_people:
features.append(VisualFeatures.PEOPLE)
result = client._analyze_from_image_data(image_data=image_data, visual_features=features)
image = Image.open(image_file)
draw = ImageDraw.Draw(image)
output_text = []
# Caption
if result.caption:
output_text.append(f"Caption: {result.caption.text} (Confidence: {result.caption.confidence:.2f})")
# Objects
if detect_objects and result.objects:
output_text.append("Objects:")
for obj in result.objects.list:
output_text.append(f" - {obj.tags[0].name} (Confidence: {obj.tags[0].confidence:.2f})")
box = obj.bounding_box
draw.rectangle(
[(box.x, box.y), (box.x + box.width, box.y + box.height)], outline="yellow", width=3
)
# People
if detect_people and result.people:
output_text.append("People:")
for person in result.people.list:
output_text.append(f" - Confidence: {person.confidence:.2f}")
box = person.bounding_box
draw.rectangle(
[(box.x, box.y), (box.x + box.width, box.y + box.height)], outline="magenta", width=3
)
return image, "\n".join(output_text)
def remove_background(image_file, mode):
url = f"{endpoint}/computervision/imageanalysis:segment"
headers = {"Ocp-Apim-Subscription-Key": key, "Content-Type": "application/octet-stream"}
params = {"api-version": "2023-02-01-preview", "mode": mode}
with open(image_file, "rb") as f:
image_data = f.read()
response = requests.post(url, headers=headers, params=params, data=image_data)
if response.status_code == 200:
return Image.open(io.BytesIO(response.content))
else:
return None
def blur_faces(image_file):
with open(image_file, "rb") as f:
image_data = f.read()
result = client._analyze_from_image_data(image_data=image_data, visual_features=[VisualFeatures.PEOPLE])
image = Image.open(image_file)
if result.people:
for person in result.people.list:
box = person.bounding_box
bbox = [int(box.x), int(box.y), int(box.x + box.width), int(box.y + box.height)]
region = image.crop(bbox)
blurred_region = region.filter(ImageFilter.GaussianBlur(15))
image.paste(blurred_region, bbox)
return image
with gr.Blocks() as demo:
with gr.Tab("Image Analysis"):
with gr.Row():
input_image = gr.Image(label="Input Image", type="filepath")
output_image = gr.Image(label="Output Image")
with gr.Row():
detect_objects = gr.Checkbox(label="Detect Objects")
detect_people = gr.Checkbox(label="Detect People")
analyse_button = gr.Button("Analyse")
analyse_button.click(
analyse_image,
inputs=[input_image, detect_objects, detect_people],
outputs=[output_image, gr.Textbox(label="Analysis Results")]
)
with gr.Tab("Background Removal"):
with gr.Row():
input_image_bg = gr.Image(label="Input Image", type="filepath")
mode = gr.Radio(
choices=["backgroundremoval", "foregroundmatting"],
label="Mode",
value="backgroundremoval"
)
output_image_bg = gr.Image(label="Output Image")
bg_button = gr.Button("Process")
bg_button.click(remove_background, inputs=[input_image_bg, mode], outputs=output_image_bg)
with gr.Tab("Privacy Protection"):
with gr.Row():
input_image_blur = gr.Image(label="Input Image", type="filepath")
output_image_blur = gr.Image(label="Output Image")
blur_button = gr.Button("Blur")
blur_button.click(blur_faces, inputs=input_image_blur, outputs=output_image_blur)
demo.launch(share=True)
모두모두 파이팅 :D