5.29. Códigos de barras e códigos Data Matrix¶
Mais duas famílias de códigos completam os decodificadores da câmera. Os códigos de barras unidimensionais – as listras na lateral de uma caixa de cereal, a pulseira de identificação de um hospital, uma etiqueta de remessa – são os símbolos legíveis por máquina mais antigos ainda em uso cotidiano. Os códigos Data Matrix são bidimensionais como os códigos QR, mas mais densos para o mesmo tamanho de carga útil e voltados para a marcação industrial – a marca do fabricante gravada a laser em uma placa de circuito, em vez do cartaz numa parede. O módulo image tem um decodificador dedicado para cada um, cobrindo as aplicações industriais, de varejo e de inventário que os códigos 2D de consumo nunca alcançaram bem.
5.29.1. Códigos de barras 1D¶
Um código de barras unidimensional codifica sua carga útil como uma sequência de barras verticais de larguras variadas, lidas da esquerda para a direita (ou de cima para baixo, no caso de códigos orientados verticalmente). As larguras são quantizadas para um de um pequeno conjunto de valores, e a sequência de larguras soletra caracteres conforme a simbologia que a impressora escolheu: numérica para um código de produto UPC, alfanumérica para um número de peça de almoxarifado, ou texto arbitrário para uma etiqueta Code 128.
find_barcodes() varre o quadro em busca de códigos de barras 1D em qualquer uma das simbologias suportadas e retorna uma lista de objetos de resultado BarCode:
codes = img.find_barcodes()
for c in codes:
img.draw_rectangle(c.rect, color=(0, 255, 0))
print(c.payload, c.type, c.quality)
O decodificador varre o quadro tanto na horizontal quanto na vertical em uma única chamada, de modo que um código de barras impresso em qualquer ângulo é encontrado em uma só passagem, sem que a aplicação precise rotacionar a entrada. roi restringe a busca; não existem outros parâmetros de ajuste – o decodificador é autocontido.
As simbologias suportadas cobrem as famílias comuns de consumo e industriais. O conjunto de varejo é image.EAN2, image.EAN5, image.EAN8, image.UPCE, image.UPCA, image.EAN13 (os códigos numéricos de comprimento fixo presentes na maioria das embalagens de consumo), image.ISBN10 e image.ISBN13 (as mesmas famílias reaproveitadas para livros). O conjunto de uso geral é image.I25 (Interleaved 2 of 5, comum em etiquetas de remessa), image.CODABAR (usado em bibliotecas e bancos de sangue), image.CODE39, image.CODE93 e image.CODE128 (simbologias alfanuméricas de comprimento variável para texto arbitrário). A família de borda de prateleira image.DATABAR (RSS-14) e image.DATABAR_EXP (RSS-Expanded) completam a lista.
Cada detecção carrega o vocabulário de caixa delimitadora – x, y, w, h, rect, corners – e a payload decodificada como uma string. type é a constante de simbologia da lista acima, que uma aplicação verifica quando se importa especificamente com qual família foi decodificada (por exemplo, aceitando apenas EAN13 em uma aplicação de scanner de supermercado).
Os dois campos que importam para a filtragem são rotation e quality. rotation é o ângulo no plano da imagem do código de barras em radianos: o decodificador lida com rotações arbitrárias, mas o código a jusante que queira exibir a detecção de forma limpa pode querer filtrar os códigos que retornam inclinados além de algum limiar.
quality é a contagem de decodificações: o número de linhas de varredura que decodificaram com sucesso a mesma carga útil. O decodificador percorre cada linha (e coluna) do quadro que intercepta o código de barras e incrementa o contador a cada decodificação bem-sucedida. Um código de barras impresso, em foco nítido e com boa iluminação, produz um quality na casa das dezenas; um código de barras parcialmente ocluído ou borrado pode decodificar em apenas uma ou duas linhas de varredura e relatar quality de 1 a 2. Filtrar as detecções abaixo de quality > 5 descarta decodificações errôneas transitórias de linha única sem custo para as detecções genuínas.
Uma aplicação de código de barras 1D é pequena. Capture um quadro, chame find_barcodes(), itere sobre a lista retornada, filtre por c.type e c.quality e encaminhe c.payload por UART ou USB para qualquer estágio a jusante que esteja registrando ou contabilizando a leitura.
5.29.2. Data Matrix¶
Um código Data Matrix é um símbolo 2D que codifica sua carga útil como uma grade de células pretas e brancas, da mesma forma que um código QR faz. Ele difere de um código QR em dois aspectos práticos: é menor para o mesmo tamanho de carga útil (a codificação é mais densa) e é voltado para uso industrial em vez de uso de consumo (onde os códigos QR dominam). Os padrões gravados a laser em peças metálicas no chão de fábrica, as etiquetas impressas em encapsulamentos de circuitos integrados, as marcas colocadas em seringas médicas – todos esses costumam ser Data Matrix, não códigos QR.
find_datamatrices() varre o quadro em busca de códigos Data Matrix e retorna uma lista de objetos de resultado DataMatrix:
codes = img.find_datamatrices()
for c in codes:
img.draw_rectangle(c.rect, color=(0, 255, 0))
print(c.payload, c.rows, c.columns)
roi restringe a busca da maneira habitual. O único botão de ajuste específico do decodificador é effort, um inteiro que controla o quanto o decodificador se esforça para encontrar uma correspondência. Valores mais altos melhoram a detecção de códigos fracos, danificados ou oblíquos ao custo da taxa de quadros; valores mais baixos rodam mais rápido, mas podem perder códigos que o esforço maior teria encontrado. Valores abaixo de cerca de 160 praticamente falham em detectar; valores acima de cerca de 240 trazem retornos decrescentes. O padrão de 200 é um equilíbrio razoável para uma imagem nítida, e o ponto de partida certo para uma nova aplicação é o padrão mais ou menos 20, dependendo de os alvos serem limpos (menor) ou desgastados (maior).
Cada detecção carrega o vocabulário de caixa delimitadora e os quatro cantos detectados, a payload decodificada e a rotation no plano da imagem em radianos. Os metadados de layout descrevem o tamanho e a densidade do símbolo que o decodificador leu: rows e columns são as contagens de células da grade de dados; capacity é o número máximo de caracteres de carga útil que o símbolo poderia carregar naquele tamanho; padding é quantos desses espaços ficaram sem uso (capacity - len(payload)).
Os campos de layout são úteis para aplicações que precisam validar o formato de uma marca gravada em vez do seu conteúdo. Um sistema de rastreamento de peças pode exigir que todas as marcas sejam códigos 12 por 12 com no máximo dois caracteres de preenchimento; uma detecção que retornou em 8 por 8 (um símbolo menor do que a especificação exige) ou com 10 caracteres de preenchimento (em sua maior parte vazio) é sinalizada para remarcação.
5.29.3. Quando escolher cada um¶
Onde QR versus AprilTag se resumia ao tipo de carga útil (dados arbitrários versus um pequeno ID), códigos de barras versus códigos Data Matrix se resumem à densidade física e à indústria.
Quando a aplicação é voltada ao consumidor e os códigos já existem em campo – mercearias, livros, etiquetas de remessa, livros de biblioteca – o detector certo é find_barcodes(). Os códigos que a aplicação está lendo foram impressos para que outro sistema os lesse, e as simbologias de varejo padronizadas são o que esse sistema esperava.
Quando a aplicação é industrial e os códigos estão sendo impressos para a aplicação – rastreamento de inventário no chão de fábrica, códigos de lote gravados em peças, marcas de rastreabilidade em dispositivos médicos – o detector certo é find_datamatrices() ou find_qrcodes(), dependendo de a aplicação precisar da maior densidade do Data Matrix ou do suporte de ferramentas mais amplo do QR.
Um punhado de aplicações combina todos os quatro detectores em um único pipeline. Uma câmera de inspeção de embalagens pode rodar uma passagem de find_barcodes() para o UPC impresso, uma passagem de find_qrcodes() para um código QR de remessa na mesma caixa e uma passagem de find_datamatrices() para um código de peça gravado, tudo no mesmo quadro capturado; as três listas de resultados são correlacionadas pela posição da caixa delimitadora e relatadas como um único registro de detecção. O custo de cada detector se soma, então as aplicações que fazem isso normalmente estreitam cada passagem com um roi apropriado em vez de varrer o quadro inteiro para cada tipo de código.