Web app para processar fotos
Uma busca rápida na internet deixa claro que existem muitos serviços online de processamento e manipulação de imagens e fotos. Essa é uma atividade com alta demanda. Embora esse ramo possa ser competitivo, com um pouco de criatividade e conhecimento de IA, certamente podemos oferecer serviços novos e conquistar uma fatia do mercado. Neste post, faremos exatamente isso. Criaremos um web app em Python usando Streamlit.
Streamlit é o framework ideal para apps de manipulação de fotos e imagens. A razão para isso é simples. Streamlit é totalmente integrado com as principais bibliotecas Python de processamento de imagens como Pillow, OpenCV, e as bibliotecas tradicionais de IA.
Nosso app com Streamlit oferecerá serviços de processamento de fotos e imagens usando filtros tradicionais e super resolução com deep learning.
Streamlit para criar aplicativos com múltiplas páginas
Para oferecer esses serviços, desenvolveremos um aplicativo com múltiplas páginas usando Streamlit. Portanto, ele terá uma estrutura mais complexa do que exemplos anteriores mostrados aqui, aqui e aqui. Essa estrutura, apesar de complexa, ilustra melhor o potencial do Streamlit para criar apps profissionais.
Pré-requisitos
Usaremos Streamlit, NumPy, OpenCV e Pillow.
pip install numpy opencv-python Pillow streamlit
Para os modelos de deep learning, utilizaremos sistemas pré-treinados disponíveis através do módulo dnn do OpenCV. É preciso baixar os pesos dos modelos que utilizaremos. Visite esse link e baixe dois modelos: LapSRN_x2.pb e LapSRN_x4.pb.
Elementos do App
Como explicado acima, faremos um app com múltiplas páginas. Para manter tudo organizado, crie uma pasta para armazenar o projeto. A pasta pode se chamar app. A estrutura do projeto é mostrada abaixo. Crie as mesmas pastas e arquivos no seu diretório app:
Como mostrado na imagem, o app contém um arquivo app.py. Ele é a página principal que funciona como a Home do aplicativo.
O arquivo ui.py será usado para definir os elementos da interface do usuário.
O arquivo utils.py conterá funções do web app para seus serviços.
Já o arquivo style.css armazenará alguns elementos de estilos.
Além dos arquivos, temos ainda duas pastas. A pasta pages armazena as páginas com serviços e uma página de contato. Dentro dela, existem três arquivos: filtros.py, super_resolution.py e contact.py.
A pasta resources armazena os modelos pré-treinados que utilizaremos. Mova ambos os arquivos de pesos baixados (LapSRN_x2.pb e LapSRN_x4.pb) para ela como mostrado na imagem acima.
Arquivo style.css
O primeiro estilo que teremos no arquivo style.css é mostrado abaixo. Ele remove a configuração padrão da barra lateral do Streamlit para aplicativos web com múltiplas páginas:
// arquivo style.css
[data-testid="stSidebar"] [data-testid="stSidebarNav"] {
display: none;
}
O segundo estilo é esse:
// arquivo style.css
.footer {
position: fixed;
left: 1rem;
bottom: 1rem;
}
Este trecho estiliza um pequeno footer na barra lateral do aplicativo. O trecho de código acima determina que ele fique posicionado no final do contêiner.
// arquivo style.css
h1 {
text-align: center;
}
.text-p {
font-size: 1.5rem !important;
text-align: center;
padding: 2rem;
}
As últimas definições de estilo são para o formulário de contato do app. Se você não tem familiaridade com css, clique nesse link para entender melhor os elementos usados.
// arquivo style.css
/* Inputs and textarea */
input[type="text"],
input[type="email"],
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
margin-top: 6px;
margin-bottom: 16px;
resize: vertical;
}
/* submit button*/
button[type="submit"] {
background-color: #04aa6d;
color: white;
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
border: 2px #04aa6d solid;
color: #04aa6d;
background-color: #fff;
}
}
Arquivo ui.py
O arquivo ui.py define elementos comuns da interface de usuário presentes nas páginas do web app. Usaremos apenas um elemento que será uma barra lateral (sidebar). Ela é implementada usando o elemento sidebar do Streamlit. O código inteiro do arquivo ui.py é mostrado abaixo:
# arquivo ui.py
import streamlit as st
def make_sidebar():
'''
Sidebar para navegar entre as paginas do app
'''
with st.sidebar:
st.title('🐍 PyIAmagem')
st.title('Processamento de Fotos e Imagens')
st.page_link("app.py", label="Home", icon='🏠')
st.page_link("pages/filtros.py", label="Filtros", icon='🛠️')
st.page_link("pages/super_resolution.py", label="Super Resolução", icon='🛠️')
st.page_link("pages/contact.py", label="Contato", icon='✉️')
footer = """
Copyright © 2024 PyIAmagem
"""
st.markdown(footer, unsafe_allow_html=True)
Como mostrado no código acima, a barra lateral tem o nome do app (PyIAmagem), os links para as páginas e um pequeno footer com copyright. Tudo isso é definido no elemento st.sidebar. Os links das páginas são definidos com seus respectivos títulos (labels). Também incluímos um ícone (icon) para cada página, mas eles não aparecem no código. Se você quiser inseri-los, copie e cole diretamente no seu código os seguintes ícones: 🏠 (home), 🛠️ (para as duas páginas de serviços) e ✉️ (para contato). No elemento st.title, antes do nome do app, adicionamos esse ícone: 🐍.
Arquivo utils.py: configurações gerais
O arquivo utils.py possui algumas funções. Antes de definí-las, fazemos as importações que usaremos:
# arquivo utils.py
import base64
import cv2
import io
import streamlit as st
from cv2 import dnn_superres
Em seguida, definimos nossas funções. A função load_css() é encarregada de carregar o arquivo css e usar o estilo definido por ele no app.
# arquivo utils.py
def load_css():
with open('style.css') as f:
st.markdown(f"", unsafe_allow_html=True)
A função set_config() determina configurações gerais do layout do web app incluindo seu ícone.
# arquivo utils.py
def set_config():
st.set_page_config(
page_title="PyIAmagem",
layout="wide",
initial_sidebar_state="expanded")
Arquivo utils.py: serviços
Depois, no mesmo arquivo utils.py, definimos três funções com filtros para editar fotos e imagens. Elas usam métodos básicos do OpenCV e foram retiradas daqui. Todas recebem uma imagem como argumento. Duas delas também recebem um valor numérico como argumento (amount) que será definido pelo usuário do app. Elas são parâmetros dos filtros.
# arquivo utils.py
def brighten_image(img, amount):
img_bright = cv2.convertScaleAbs(img, beta=amount)
return img_bright
def blur_image(img, amount):
blur_img = cv2.GaussianBlur(img, (11, 11), amount)
return blur_img
def enhance_details(img):
hdr = cv2.detailEnhance(img, sigma_s=12, sigma_r=0.15)
return hdr
A próxima função do web app é para a super resolução de imagens. Uma explicação sobre esse processo pode ser obtida aqui. A função recebe uma imagem como argumento e uma escala (scale) para o aumento da imagem que será definido pelo usuário do aplicativo.
# arquivo utils.py
def super_resolution(img, scale):
sr = dnn_superres.DnnSuperResImpl_create()
# Carrega o modelo selecionado
if scale == '2x':
path = "resources/LapSRN_x2.pb"
sr.readModel(path)
sr.setModel("lapsrn", 2)
if scale == '4x':
path = "resources/LapSRN_x4.pb"
sr.readModel(path)
sr.setModel("lapsrn", 4)
processed_image = sr.upsample(img)
return processed_image
A última função do arquivo utils.py cria um link para download para cada imagem processada. Ela foi adaptada daqui.
# arquivo utils.py
def get_image_download_link(img, filename, text):
buffered = io.BytesIO()
img.save(buffered, format="JPEG")
img_str = base64.b64encode(buffered.getvalue()).decode()
href = f'{text}'
return href
Arquivo app.py
O arquivo app.py é a página inicial do aplicativo. Ele precisa das funções de configurações de estilo e da barra lateral definidas anteriormente. Ele também possui um contêiner com uma mensagem introdutória devidamente gerada com a ajuda do Gemini! O código inteiro desse arquivo é mostrado a seguir:
# arquivo app.py
import streamlit as st
from ui import make_sidebar
from utils import set_config, load_css
def main():
set_config()
load_css()
make_sidebar()
with st.container(border=False):
st.title('Bem-vindo ao PyIAmagem')
text = """
Traga fotos borradas de volta à vida! PyIAmagem usa tecnologia de ponta para magicamente aprimorar suas fotos e apagar ruídos indesejados. Veja todos os detalhes,
desde rostos distantes até texturas esquecidas. Carregue suas imagens agora e veja a mágica.
"""
st.markdown(text, unsafe_allow_html=True)
if __name__ == "__main__":
main()
Com o arquivo app.py devidamente definido, podemos testá-lo com o comando streamlit run app.py. A página inicial do web app no navegador é mostrada abaixo:
Arquivo filtros.py
Nesta etapa, definiremos o código da primeira página de serviços chamada filtros.py (localizado na pasta pages). Seu código inteiro é mostrado a seguir:
# arquivo pages/filtros.py
import cv2
import streamlit as st
import numpy as np
from PIL import Image
from ui import make_sidebar
from utils import set_config, load_css, brighten_image, blur_image, enhance_details, get_image_download_link
def main():
set_config()
load_css()
make_sidebar()
with st.container(border=False):
st.title('PyIAmagem Filtros')
text = """
Explore nossos filtros
"""
st.markdown(text, unsafe_allow_html=True)
blur_rate = st.slider("Blurring", min_value=0.5, max_value=3.5)
brightness_amount = st.slider("Brightness", min_value=-50, max_value=50, value=0)
apply_enhancement_filter = st.checkbox('Aumento de Detalhes')
image_file = st.file_uploader("Carregue sua imagem", type=['jpg', 'png', 'jpeg'])
if not image_file:
return None
original_image = Image.open(image_file)
original_image = np.array(original_image)
processed_image = blur_image(original_image, blur_rate)
processed_image = brighten_image(processed_image, brightness_amount)
if apply_enhancement_filter:
processed_image = enhance_details(processed_image)
st.text("Imagem Original e Imagem Processada")
cols = st.columns(2)
with cols[0]:
st.image(original_image, width=400)
with cols[1]:
st.image(processed_image, width=400)
img = Image.fromarray(processed_image)
st.markdown(get_image_download_link(img, image_file.name, "imagem_processada"), unsafe_allow_html=True)
if __name__ == '__main__':
main()
Neste código, primeiro carregamos configurações gerais do web app e iniciamos a barra lateral (linhas 11-13). Depois, criamos um contêiner onde os usuários do app selecionam os filtros e os parâmetros que serão usados (linhas 24-26). Também definimos um elemento para upload de imagens (linha 27).
Depois, processamos cada imagem recebida com base nos filtros selecionados. A imagem original e a versão processada são mostradas no app (linhas 40-45). Por fim, criamos um link para download da imagem modificada.
Executando o app novamente, podemos testar o uso de filtros diretamente no navegador. Abaixo, veja um exemplo do layout obtido.
Arquivo super_resolution.py
A página para Super Resolução (arquivo super_resolution.py na pasta pages) de imagens é bem parecida com a página do arquivo filtros.py. A diferença essencial é na função empregada. As mudanças principais ocorrem na linha 24 (definição da escala de super resolução) e linha 33 abaixo (função com super resolução).
# arquivo pages/super_resolution.py
import cv2
import streamlit as st
import numpy as np
from PIL import Image
from ui import make_sidebar
from utils import set_config, load_css, super_resolution, get_image_download_link
def main():
set_config()
load_css()
make_sidebar()
with st.container(border=False):
st.title('PyIAmagem Super Resolução')
text = """
Aumente sua imagem com Super Resolução
"""
st.markdown(text, unsafe_allow_html=True)
scale = st.radio("Escolha a resolução para aumento da imagem", ['2x', '4x'], index=None, horizontal=True, label_visibility="visible")
image_file = st.file_uploader("Carregue sua imagem", type=['jpg', 'png', 'jpeg'])
if not image_file:
return None
original_image = Image.open(image_file)
original_image = np.array(original_image)
processed_image = super_resolution(original_image, scale)
st.text("Imagem Original e Imagem Processada")
cols = st.columns(2)
with cols[0]:
st.image(original_image, width=400)
with cols[1]:
st.image(processed_image, width=400)
img = Image.fromarray(processed_image)
st.markdown(get_image_download_link(img, image_file.name, "imagem_processada"), unsafe_allow_html=True)
if __name__ == '__main__':
main()
Abaixo mostramos o layout dessa página já com um exemplo de imagem.
Arquivo contact.py
O último arquivo que faremos é o contact.py (localizado na pasta pages) com um formulário de contatos. Ele foi adaptado deste post. Copie todo o trecho mostrado abaixo no seu arquivo contact.py.
# arquivo pages/contact.py
import streamlit as st
from ui import make_sidebar
from utils import set_config, load_css
def main():
img = set_config()
load_css()
make_sidebar()
with st.container(height=600, border=False):
st.header(":mailbox: Entre em Contato")
contact_form = """
"""
st.markdown(contact_form, unsafe_allow_html=True)
if __name__ == "__main__":
main()
Na linha 16, insira seu e-mail corretamente no lugar de your@emal.com. Para saber mais detalhes sobre esse formulário, clique aqui. O formulário é mostrado abaixo: