Processamento de imagens em OpenCV - Introdução aos recursos Contour + Contour

Processamento de imagens em OpenCV - Introdução aos recursos Contour + Contour

1. Contornos no OpenCV

1.1 Visão geral do perfil

Sempre o mesmo. Antes de aprender sobre contornos no OpenCV, vamos entender o que é um contorno. Um contorno pode ser simplesmente interpretado como uma curva que conecta todos os pontos consecutivos (ao longo de um limite) com a mesma cor ou intensidade. Contornos são usados ​​para formas Úteis ferramentas para análise e objeto e detecção e reconhecimento

  • Para melhor precisão, usamos imagens binárias, portanto, antes de encontrar contornos, aplicamos limiarização ou detecção de arestas Canny
  • No OpenCV, encontrar o contorno é encontrar o objeto branco a partir do fundo preto, então o objeto que estamos procurando deve ser branco e o fundo deve ser preto (depois de aplicar limiar ou detecção de borda Canny)

Para encontrar a imagem de uma imagem binária, precisamos usar uma função: cv.findContours() , esta função inclui três parâmetros, o primeiro é a imagem original, o segundo é o modo de recuperação do contorno e o terceiro é a aproximação do contorno método, esta função gera contornos e hierarquias, contornos referem-se a uma lista Python de todas as imagens na imagem, cada contorno individual é uma matriz Numpy de objeto de coordenadas (x,y) dos pontos de limite

import numpy as np
import cv2 as cv
im = cv.imread('test.jpg')
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE,
cv.CHAIN_APPROX_SIMPLE)

O valor do segundo modo de recuperação de contorno de parâmetro pode ser:

  • RETR_EXTERNAL: recupera apenas os contornos mais externos

  • RETR_LIST: Recupere todos os contornos e salve-os em uma lista encadeada

  • RETR_CCOMP: Recupera todos os contornos e os organiza em duas camadas: a camada superior são os limites externos das peças, a segunda camada são os limites das cavidades

  • RETR_TREE: comum, recupera todos os contornos e reconstrói toda a hierarquia de contornos aninhados. salvar todos os contornos

O valor do método de aproximação de contorno do terceiro parâmetro pode ser:

  • CHAIN_APPROX_NONE: Contornos de saída como códigos de cadeia de Freeman, todos os outros métodos de saída de polígonos (sequências de vértices). Desenhe todos os contornos normalmente

  • CHAIN_APPROX_SIMPLE: Comprime as partes horizontal, vertical e oblíqua, ou seja, a função retém apenas as coordenadas do ponto final. A compressão resulta em resultados mais simplificados, por exemplo, um contorno retangular precisa apenas de 4 pontos para salvar as informações do contorno

A referência de dados vem do artigo: [opencv] (6) Processamento de contorno de imagem

1.2 Desenho de contorno

Depois de encontrar o contorno através da função cv.findContours(), normalmente desenhamos o contorno através da função cv.drawContours(). Desde que haja pontos de contorno, ela também pode ser usada para você desenhar qualquer forma. O primeiro parâmetro dessa função é o recurso image, o segundo parâmetro é o contorno que deve ser passado como uma lista Python, o terceiro parâmetro é o índice do contorno (útil ao desenhar um único contorno, se precisar desenhar todos os contornos, passe o parâmetro -1), e o restante dos parâmetros são parâmetros comuns, como espessura de cor

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('E:/image/test09.png')
# 在寻找轮廓时要传入单通道图像
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, threshold = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
contours, hierarchy = cv.findContours(threshold, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 绘制所有轮廓
img1 = cv.drawContours(img, contours, -1, (0, 255, 0), 3)
# 绘制单个轮廓
img2 = cv.drawContours(img, contours, 3, (0, 255, 0), 3)
# 常用的绘制轮廓的方法
cnt = contours[3]
img3 = cv.drawContours(img, [cnt], 0, (0, 255, 0), 3)
plt.subplot(1, 3, 1), plt.imshow(img, cmap='gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2), plt.imshow(img, cmap='gray')
plt.title('ALL_IMG'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3), plt.imshow(img, cmap='gray')
plt.title('Usually'), plt.xticks([]), plt.yticks([])
plt.show()

insira a descrição da imagem aqui

1.3 Método de Aproximação de Contorno

A função cv.findContours() que mencionamos anteriormente tem três parâmetros para passar, e o terceiro parâmetro é o método de aproximação de contorno, que é o método de aproximação de contorno sobre o qual falaremos aqui. Um contorno é o limite de uma forma com a mesma força, ele armazena as coordenadas (x,y) do limite da forma, mas não armazena todas as coordenadas

E se ele armazena todas as coordenadas depende do uso do método de aproximação de contorno. Se passarmos no parâmetro cv.CHAIN_APPROX_NONE, o contorno armazenará todas as coordenadas, mas em situações reais, é necessário armazenarmos todas as coordenadas ? ?

Por exemplo, se encontrarmos um contorno retangular, armazenar todas as coordenadas desperdiçará muito espaço, só precisamos armazenar as coordenadas de quatro vértices, esta operação é o parâmetro cv.CHAIN_APPROX_SIMPLE, este método de aproximação de contorno pode atender às nossas necessidades

2. Recursos de contorno

2.1 Momentos característicos

Antes de aprender o momento do recurso, devemos primeiro entender seu conceito. Primeiro, o momento do recurso representa os recursos globais de um contorno e uma imagem. A informação do momento contém diferentes tipos de recursos geométricos do objeto correspondente. O momento do recurso é dividido em três tipos: momento espacial, momentos centrais e momentos centrais normalizados

Momento central: Para imagens de alta ordem, o momento característico mudará com a mudança de posição. Para resolver esse problema, o momento central surgiu. Ele obtém a invariância da translação subtraindo o valor médio, para poder comparar duas imagens em posições diferentes. Se o objeto é consistente, ou seja, o momento central tem as características de invariância de translação

Momento central normalizado: Além da translação, algumas imagens também encontrarão zoom, ou seja, suas características também podem ser julgadas após o zoom. O momento central normalizado é obtido dividindo-se pelo tamanho total do objeto para obter a invariância do zoom

Voltando aos momentos do nosso recurso OpenCV, ele pode nos ajudar a calcular alguns recursos, como centróide, área, etc., uma função importante cv.moments() fornece um dicionário de todos os valores de momento calculados

import numpy as np
import cv2 as cv
img = cv.imread('star.jpg',0)
ret,thresh = cv.threshold(img,127,255,0)
contours,hierarchy = cv.findContours(thresh, 1, 2)
cnt = contours[0] 
M = cv.moments(cnt)
print( M )

2.2 Área de contorno + perímetro

A área do contorno é dada pela função cv.contourArea () ou a partir do momento M['m00']

O perímetro também é conhecido como comprimento do arco e pode ser encontrado usando a função cv.arcLength (). O segundo parâmetro especifica se a forma é um contorno fechado ( True ) ou uma curva

area = cv.contourArea(cnt)
perimeter = cv.arcLength(cnt,True)

2.3 Aproximação de Contorno

Aproximação de contorno é aproximar a forma do contorno a outras formas com menos vértices através do algoritmo de Douglas-Puk de acordo com a leitura intensiva que especificamos.

Quando se trata do algoritmo de Douglas-Puke, devemos observar sua essência: ao digitalizar uma imagem, a curva é amostrada, ou seja, um número limitado de pontos é retirado da curva e a conexão se torna uma polilinha e, para até certo ponto, o original alguma forma

import cv2
import numpy as np
from matplotlib import pyplot as plt

src = cv2.imread('E:/image/test10.png')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(~thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
img1 = src.copy()
epslion1 = 0.05 * cv2.arcLength(cnt, True)
approx1 = cv2.approxPolyDP(cnt, epslion1, True)
img1 = cv2.drawContours(img1, [approx1], 0, (0, 0, 255), 2)
img2 = src.copy()
epslion2 = 0.01 * cv2.arcLength(cnt, True)
approx2 = cv2.approxPolyDP(cnt, epslion2, True)
img2 = cv2.drawContours(img2, [approx2], 0, (0, 255, 0), 2)
plt.subplot(1, 2, 1), plt.imshow(img1, cmap='gray')
plt.title('JinSi1'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 2, 2), plt.imshow(img2, cmap='gray')
plt.title('JInSi2'), plt.xticks([]), plt.yticks([])
plt.show()

insira a descrição da imagem aqui

Como revisão do conhecimento anterior, analisamos todo o processo de desenhar dois contornos aproximados da imagem com diferentes precisões. Primeiro, carregamos algumas bibliotecas de terceiros que precisamos, para que possamos usar seus métodos no código e, em seguida, na quinta linha de código Nós lemos uma imagem através da função cv,imread(), e então usamos a função cv.cvtColor() para convertê-la em uma imagem em tons de cinza Por que devemos convertê-la em uma imagem em tons de cinza? Porque vamos usar a limiarização de imagem a seguir! Na função de limite global cv.threshold(), temos quatro parâmetros para passar. O primeiro parâmetro é nossa imagem de origem, que geralmente é uma imagem em tons de cinza, e é por isso que precisamos alterar img para cinza, e o segundo O primeiro parâmetro é o limite que definimos, que é usado para classificar os pixels na imagem, e os pixels maiores que o limite serão definidos para o valor máximo, o terceiro parâmetro é o valor máximo que definimos e o quarto parâmetro é um representação diferentes tipos de sinais

O código chega à 8ª linha. Daqui em diante, envolverá o que aprendemos nesta parte. A primeira coisa a suportar o peso é encontrar o contorno. Usamos a função cv.findContours(). Os três parâmetros deste função são o modo de recuperação de imagem e contorno. e o método de aproximação de contorno

Na linha 11, apareceu uma coisa estranha, que é um dos códigos centrais da aproximação de contorno. Epslion é um parâmetro intimamente relacionado à precisão de aproximação, que representa a distância máxima do contorno ao contorno aproximado. Como calcular a distância máxima ? Então precisamos usar uma função: cv.arcLength(), é familiar, é a função que mencionamos acima para encontrar o perímetro do contorno, precisamos escolher o epsilon correto para obter a saída correta

Se alterarmos 0,05 das 11 linhas de código para 0,1, descobriremos que não podemos obter a saída que desejamos. Isso ocorre porque o parâmetro epsilon é muito grosseiro. Quanto menor o parâmetro, mais aproximado será o contorno obtido

2.4 Casco Convexo

O contorno convexo envoltório e a aproximação de contorno parecem semelhantes, mas na verdade não têm nada a ver um com o outro. Em relação ao contorno convexo envoltório, também temos uma função importante função cv.convexHull(), que é usada para verificar se a curva tem defeitos convexos e côncavos e corrija. Sem correção, depende dos parâmetros desta função

De um modo geral, uma curva convexa é geralmente uma curva convexa ou plana. Se for convexa (côncava) por dentro, chamamos de defeito de convexidade. Falaremos mais sobre o defeito de convexidade mais tarde.

Vamos falar sobre a sintaxe da função cv.convexHull(). Fica assim:

hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]

point é o contorno que passamos (por que usar point? Você esqueceu que a essência do contorno é uma curva conectada por todos os pontos consecutivos com a mesma cor ou intensidade), hull (bulge) é a saída, geralmente é ignorado, sentido horário é uma marca de direção, se for passado em True, é sentido horário, caso contrário é anti-horário, returnPoint é True por padrão, retorna as coordenadas do casco convexo, se for False, retorna o ponto correspondente ao convexo casco O índice do ponto de contorno de

Então vá para o exemplo! (Continue praticando para crescer!)

import cv2
import numpy as np
from matplotlib import pyplot as plt

src = cv2.imread('E:/image/test11.png')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
    hull = cv2.convexHull(cnt)
    length = len(hull)
    # 如果凸包点集中的点个数大于5
    if length > 5:
        # 绘制图像凸包的轮廓
        for i in range(length):
            cv2.line(src, tuple(hull[i][0]), tuple(hull[(i + 1) % length][0]), (0, 0, 255), 2)
cv2.imshow('line', src)
cv2.waitKey()

insira a descrição da imagem aqui

Fonte de referência do artigo: entrada OpenCV para encontrar o casco convexo da imagem (casco convexo)

2.5 Retângulo delimitador (retângulo em ângulo reto + retângulo girado)

2.5.1 Retângulo retângulo

O retângulo retângulo não considera a rotação do objeto, então a área do retângulo delimitador retângulo não é a menor. Encontrar este retângulo envolve uma função: cv.boundingRect()

# (x,y)为左上角坐标,(w,h)为宽和高
x,y,w,h = cv.boundingRect(cnt)
cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

2.5.2 Girar retângulo

O retângulo delimitador girado é desenhado com a área mínima, portanto leva em consideração a rotação do objeto. A função envolvida em encontrar o retângulo delimitador girado é cv.minAreaRect(), que retorna uma estrutura Box2D que contém coordenadas, largura, altura , e ângulo de rotação , mas queremos obter este retângulo é bastante problemático, precisamos das coordenadas dos quatro vértices

rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img,[box],0,(0,0,255),2)

2.8 Círculo fechado mínimo + elipse de encaixe

2.8.1 Círculo fechado mínimo

Encontrar o círculo mínimo envolvente de um objeto requer o uso da função: cv.minEnclosingCircle(), que é um círculo que cobre completamente o objeto com a menor área

(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv.circle(img,center,radius,(0,255,0),2)

2.8.2 Ajustando uma elipse

Ajustar a elipse de um objeto usará a função: cv.fitEllipse()

ellipse = cv.fitEllipse(cnt)
cv.ellipse(img,ellipse,(0,255,0),2)

Vamos dar uma olhada no código do aplicativo:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('E:/image/test13.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 设置阈值
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
x, y, w, h = cv2.boundingRect(cnt)
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
img = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyWindow()

insira a descrição da imagem aqui


(Observação: para o conteúdo do artigo, consulte o documento oficial chinês do OpenCV4.1)
Se o artigo for útil para você, lembre-se de apoiá-lo com um clique e três links

Acho que você gosta

Origin blog.csdn.net/qq_50587771/article/details/123819620
Recomendado
Clasificación