Bem-vindo ao Blog da DMarkInfo

Conteúdos e novidades sobre Tecnologia da Informação.

Criando um Sistema Modular de Controle de Estacionamento com Leitura de Placas usando Python, Streamlit e OCR (com PostgreSQL)

Postado por Eduardo Marques em 16/11/2025
Criando um Sistema Modular de Controle de Estacionamento com Leitura de Placas usando Python, Streamlit e OCR (com PostgreSQL)

Nos últimos tempos, comecei a experimentar soluções de automação que envolvem visão computacional aplicada ao controle de acesso.
Uma das áreas mais interessantes é o controle de estacionamento com leitura automática de placas (OCR).
E para validar alguns conceitos, eu decidi criar um protótipo completo, modular, organizado e pronto para evoluir para produção.

Neste artigo mostro como eu construí um app em Streamlit, com backend organizado em módulos, usando:

  • Python

  • Streamlit

  • PostgreSQL

  • SQLAlchemy

  • OpenCV

  • EasyOCR

  • PyTorch

  • Arquitetura modular escalável

 

Tecnologias utilizadas

Tecnologia Função
Python 3.10+ Linguagem principal
Streamlit Interface web
EasyOCR Leitura da placa
OpenCV Processamento da imagem
PyTorch Dependência do EasyOCR
PostgreSQL Banco relacional
SQLAlchemy ORM
Pillow Manipulação de imagens
Arquitetura modular Manutenibilidade

 

Instalação dos Pacotes (completa)


A seguir, deixo o passo a passo exato que utilizei para preparar o ambiente.

1. Criando o ambiente virtual (opcional, mas recomendado)

python -m venv venv

Ativar:

Windows:

venv\Scripts\activate

Linux/Mac:

source venv/bin/activate

 

2. Instalando dependências principais

pip install streamlit sqlalchemy psycopg2-binary easyocr opencv-python pillow

 

3. Instalando PyTorch (OBRIGATÓRIO para o EasyOCR funcionar)

Versão CPU (universal):

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu

 

4. Criando um requirements.txt (opcional)

streamlit
sqlalchemy
psycopg2-binary
easyocr
opencv-python
pillow
torch
torchvision
torchaudio

 

E instalo com:

pip install -r requirements.txt

 

Configuração do Banco PostgreSQL

Primeiro, criei o banco:

CREATE DATABASE estacionamento_db;

Depois, criei a tabela necessária:

CREATE TABLE estacionamento (
    id SERIAL PRIMARY KEY,
    placa VARCHAR(20) NOT NULL,
    horario_entrada TIMESTAMP NOT NULL DEFAULT NOW(),
    horario_saida TIMESTAMP
);

 

 

Arquitetura do Projeto (Modular)

Organizei o projeto assim:

controle_estacionamento/

├── app.py                  # Interface Streamlit

├── modules/
│   ├── ocr.py              # OCR da placa
│   ├── database.py         # Conexão PostgreSQL
│   ├── models.py           # Modelos ORM
│   ├── repositories.py     # Acesso ao banco
│   └── services.py         # Regras de negócio

└── README.md

 

Separar assim deixa tudo claro, limpo e escalável.

 

Módulos do Projeto

Agora mostro cada módulo com explicações.
 

modules/database.py — Conexão com Banco

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
DATABASE_URL = "postgresql://usuario:senha@localhost:5432/estacionamento_db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
Base = declarative_base()
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

 

 

modules/models.py — Modelos SQLAlchemy

from sqlalchemy import Column, Integer, String, TIMESTAMP
from sqlalchemy.sql import func
from modules.database import Base
class Movimentacao(Base):
    __tablename__ = "estacionamento"
    id = Column(Integer, primary_key=True)
    placa = Column(String(20), nullable=False)
    horario_entrada = Column(TIMESTAMP, server_default=func.now())
    horario_saida = Column(TIMESTAMP, nullable=True)

 

 

modules/repositories.py — CRUD Completo


from sqlalchemy.orm import Session
from sqlalchemy import select, func
from modules.models import Movimentacao
class EstacionamentoRepository:
    @staticmethod
    def registrar_entrada(db: Session, placa: str):
        mov = Movimentacao(placa=placa)
        db.add(mov)
        db.commit()
    @staticmethod
    def registrar_saida(db: Session, placa: str):
        stmt = (
            select(Movimentacao)
            .where(Movimentacao.placa == placa)
            .where(Movimentacao.horario_saida.is_(None))
        )
        registro = db.execute(stmt).scalar_one_or_none()
        if registro:
            registro.horario_saida = func.now()
            db.commit()
    @staticmethod
    def listar(db: Session):
        stmt = select(Movimentacao).order_by(Movimentacao.id.desc())
        return db.execute(stmt).scalars().all()

 

 

modules/ocr.py — Leitura da Placa


import easyocr
import cv2
import numpy as np
from PIL import Image
reader = easyocr.Reader(["en"], gpu=False)
def ler_placa(imagem: Image.Image) -> str:
    np_img = np.array(imagem)
    np_img = cv2.cvtColor(np_img, cv2.COLOR_RGB2BGR)
    resultados = reader.readtext(np_img)
    textos = [r[1] for r in resultados]
    if not textos:
        return "NÃO IDENTIFICADA"
    placa = "".join([c for c in textos[0] if c.isalnum()])
    return placa.upper()

 

 

modules/services.py — Regras de Negócio


from sqlalchemy.orm import Session
from modules.repositories import EstacionamentoRepository
from modules.ocr import ler_placa
class EstacionamentoService:
    @staticmethod
    def registrar_entrada(db: Session, imagem):
        placa = ler_placa(imagem)
        EstacionamentoRepository.registrar_entrada(db, placa)
        return placa
    @staticmethod
    def registrar_saida(db: Session, imagem):
        placa = ler_placa(imagem)
        EstacionamentoRepository.registrar_saida(db, placa)
        return placa
    @staticmethod
    def listar_movs(db: Session):
        return EstacionamentoRepository.listar(db)

 

 

app.py — Interface Streamlit Completa


import streamlit as st
from PIL import Image
from modules.database import SessionLocal
from modules.services import EstacionamentoService
st.set_page_config(page_title="Controle de Estacionamento", layout="centered")
st.title("🚗 Controle de Estacionamento com OCR")
st.write("Sistema modular utilizando Python, Streamlit, OCR e PostgreSQL")
db = SessionLocal()
uploaded = st.file_uploader("Envie a foto da placa", type=["jpg", "jpeg", "png"])
if uploaded:
    img = Image.open(uploaded)
    st.image(img, caption="Imagem enviada", width=350)
    acao = st.radio("Selecione a ação:", ["Registrar entrada", "Registrar saída"])
    if st.button("Processar"):
        if acao == "Registrar entrada":
            placa = EstacionamentoService.registrar_entrada(db, img)
            st.success(f"Entrada registrada: {placa}")
        else:
            placa = EstacionamentoService.registrar_saida(db, img)
            st.success(f"Saída registrada: {placa}")
st.subheader("📋 Movimentações Registradas")
movs = EstacionamentoService.listar_movs(db)
for m in movs:
    st.write(
        f"ID: {m.id} | Placa: {m.placa} | "
        f"Entrada: {m.horario_entrada} | Saída: {m.horario_saida}"
    )

 


Executando o sistema

Com tudo instalado e configurado:

streamlit run app.py

E pronto: sua aplicação de controle de estacionamento está funcionando.

 

Possíveis evoluções futuras

Como o sistema está modularizado, posso expandi-lo facilmente:

  • Reconhecimento de carro antes do OCR usando YOLOv8

  • Integração com câmera IP para captura automática

  • Painel administrativo completo

  • Cadastro de mensalistas

  • Integração com cancelas e totens

  • API REST consumida por mobile ou desktop

 

Criar um sistema profissional começa por uma boa arquitetura. Ao dividir o projeto em módulos (OCR, banco, repositórios, serviços e interface), o código fica limpo, reaproveitável e pronto para crescer.

Esse tutorial cobre:

• Instalação dos pacotes
• Setup do PostgreSQL
• Arquitetura modular
• Código organizado por camadas
• App Streamlit final funcionando

Compartilhe este post:
Voltar para a Home