Estrutura de dados OpenCV e desenho básico
1. Contêiner de imagens básico Mat
1.1 Visão geral do armazenamento de imagens digitais
Temos vários métodos de aquisição de imagens digitais do mundo real: câmeras digitais, scanners, tomografia computadorizada e ressonância magnética, para citar alguns. Em cada caso, o que nós (humanos) vemos são imagens. No entanto, ao traduzir isso para nossos dispositivos digitais, o que registramos é o valor numérico de cada ponto da imagem.
Por exemplo, na imagem acima, você pode ver que o retrovisor do carro nada mais é do que uma matriz contendo os valores de intensidade de todos os pixels. A forma como obtemos e armazenamos valores de pixel pode variar de acordo com nossas necessidades, mas no final das contas todas as imagens no mundo da informática podem ser reduzidas a uma matriz de números e outras informações que descrevem a própria matriz.
1.2 Uso do Tapete
Sobre a classe Mat primeiro precisamos saber:
- Você não precisa alocar espaço manualmente para ele
- O espaço não precisa ser liberado imediatamente quando não é necessário
A maioria das funções do OpenCV aloca automaticamente o espaço necessário para seus dados de saída. Se passar um objeto Mat já existente, que já tenha alocado o espaço necessário para a matriz, este será reaproveitado. Em outras palavras, usamos apenas a memória necessária para realizar a tarefa a qualquer momento.
Mat é basicamente uma classe com duas partes de dados: o cabeçalho da matriz (contendo informações como o tamanho da matriz, o método usado para armazenamento, em qual endereço a matriz está armazenada etc.) os valores de pixel (Tome qualquer tamanho, dependendo do método de armazenamento escolhido). O tamanho do cabeçalho da matriz é constante, mas o tamanho da própria matriz pode variar de imagem para imagem e geralmente é muito maior.
Para resolver este problema, o OpenCV usa um sistema de contagem de referências. A ideia é que cada objeto Mat tenha seu próprio cabeçalho, mas uma matriz pode ser compartilhada entre dois objetos Mat, desde que seus ponteiros de matriz apontem para o mesmo endereço . Além disso, o operador de cópia copiará apenas o cabeçalho e os ponteiros para a matriz grande, não os dados em si.
Pergunta: Se a matriz pertencer a vários objetos Mat, quem é responsável por limpá-la quando ela não for mais necessária?
O último objeto que a utilizou. Isso é obtido por meio do mecanismo do contador de referência. Sempre que copiarmos o cabeçalho de informação de um objeto Mat, aumentaremos o número de referências à matriz. Por outro lado, quando um cabeçalho é liberado, a contagem é decrementada em um; quando a contagem é 0, a matriz é limpa.
Mas às vezes você quer copiar a própria matriz, neste momento você pode usar a função clone() ou copyTo()
1.3 Método de armazenamento do valor do pixel
- O espaço de cores RGB é o espaço de cores mais comumente usado. Isso se deve ao fato de que também é a maneira como o olho humano compõe as cores. Suas cores primárias são vermelho, verde e azul. Às vezes, um quarto elemento alfa (A)
é adicionado para representar cores transparentes. - HSV e HLS dividem as cores em matiz, saturação e luminosidade. Esta é uma forma mais natural de descrever as cores, por exemplo, descartando o último elemento, tornando o algoritmo insensível às condições de iluminação da imagem de entrada.
- YCrCb é amplamente utilizado no formato de imagem JPEG
- COE L a b* é um espaço de cor perceptivamente uniforme adequado para medir a distância entre duas cores.
1.4 Exibição criando objeto Mat
- Use o construtor Mat()
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
cout << "M=" << endl << " " << M << endl << endl;
Para uma imagem multicanal bidimensional, primeiro defina suas dimensões, ou seja, linhas e colunas. Em seguida, você precisa especificar o tipo de dados dos elementos de armazenamento e o número de canais para cada ponto da matriz.
CV_[número de dígitos[assinado ou não][tipo de prefixo]C[número de canais]
Por exemplo, CV_8UC3 significa usar o tipo de caractere não assinado de 8 bits e cada pixel consiste em três pixels para formar três canais. O número de canais predefinidos pode ser de até quatro. Além disso, Scalar é um vetor curto que pode ser usado para inicializar uma matriz com um valor personalizado especificado e também pode ser usado para representar uma cor.
- Use matrizes C/C++ e inicialize-as por meio de construtores
int sz[3] = {
2,2,2};
Mat L(3,sz, CV_8UC(1), Scalar::all(0));
O exemplo acima demonstra como criar uma matriz com mais de duas dimensões: especifique as dimensões e passe um ponteiro para um array contendo as dimensões de cada dimensão.
- Use a função criar()
Mat M;
M.create(4, 4, CV_8UC(2));
cout << "M=" << endl<< M << endl;
Este método de criação não pode definir o valor inicial para a matriz, mas apenas reabre a memória para os dados da matriz quando o tamanho é alterado.
- Usando o método de inicialização no estilo Matlab
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E=" << endl << " " << E << endl << endl;
Mat O = Mat::ones(2, 2, CV_32F);
cout << "O=" << endl << " " << O << endl << endl;
Mat Z = Mat::zeros(3, 3, CV_8UC1);
cout << "Z=" << endl << " " << Z << endl << endl;
2. Estruturas e funções de dados comumente usadas
2.1 Representação de ponto: classe de ponto
A estrutura de dados da classe Point representa um ponto em um sistema de coordenadas bidimensional, ou seja, um ponto 2D especificado por suas coordenadas de imagem x e y.
Point point;
point.x=10;
point.y=8
//或者
Point point=Point(10,8);
OpenCV tem a seguinte definição:
typedef Point_<int> Point2i;
typedef Point2i Point;
typedef Point_<float> Point2f;
Portanto, Point<int>, Point2i e Point são equivalentes entre si e Point_<float>, Point2f são equivalentes entre si.
2.3 Representação da cor: Classe escalar
Scalar() representa um array com quatro elementos, que é amplamente utilizado no OpenCV para passar valores de pixel, como valores de cores RGB. O valor da cor RGB tem três parâmetros. Na verdade, para a função Scalar, se o quarto parâmetro não for usado, você não precisa escrevê-lo; se você escrever apenas três parâmetros, o OpenCV pensará que representamos três parâmetros.
Se Scalar(a,b,c) for fornecido
, então os valores de cor RGB definidos: c para o componente vermelho, b para o componente verde e a para o componente azul.
2.4 Representação de tamanho: Classe de tamanho
Size(width,heigth)
Size(5,5)
constrói Size com largura e altura de 5,
ou seja, XXX.width=5, XXX.height=5
2.5 Representação de um retângulo: a classe Rect
As variáveis de membro da classe Rect incluem x, y, largura e altura, que são as coordenadas do canto superior esquerdo e a largura e altura do retângulo, respectivamente.
As funções de membro comumente usadas são:
Size() retorna Size;
area(), retorna a área do retângulo;
contains(Point) determina se o ponto está dentro do retângulo;
inside(Rect) determina se o retângulo está dentro do retângulo retângulo
tl() retorna o canto superior esquerdo As coordenadas do ponto de
br() retornam as coordenadas do ponto do canto inferior direito
Encontre a interseção de dois retângulos:Rect rect = rect1 & rect2;
encontre a união de dois retângulos:Rect rect = rect1 | rect2;
operação de translação:Rect rectShift = rect + point;
operação de zoom:Rect rectSccale = rect + size;
2.5 Conversão do espaço de cores: função cvtColor()
A função cvtColor() é uma função de conversão de espaço de cores no OpenCV, que pode converter cores RGB em HSV, HSI e outros espaços de cores, e também pode ser convertida em imagens em tons de cinza.
void cvtColor(InputArray src, OutputArray dst,int code, int dstCn=0)
- O primeiro parâmetro: a imagem de entrada
- O segundo parâmetro: a imagem de saída
- O terceiro parâmetro: o identificador da conversão do espaço de cores
- O quarto parâmetro: o número de canais da imagem de destino, se este parâmetro for 0, significa que a imagem de destino leva o número de canais da imagem de origem.
Por exemplo:cvtColor(srcImage,dstImage,COLOR_GRAY2BGR)
3. Desenho de gráficos básicos
- A função de linha para desenhar linhas retas
- função elipse para desenhar elipses
- A função retângulo para desenhar retângulos
- função de círculo para desenhar círculos
- A função fillPoly usada para desenhar polígonos preenchidos
Defina várias funções de desenho personalizadas e, em seguida, chame essas funções personalizadas para desenhar uma figura: um mapa de átomos químicos.
definição de macro do programa
#define WINDOW_WIDTH 600 //定义窗口大小
3.1 Como escrever a função DrawEllipse()
//自定义绘制函数:实现了绘制不同角度、相同尺寸的椭圆
void DrawEllipse(Mat img, double angle) {
int thickness = 2;
int lineType = 8;
ellipse(img,
Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),
Size(WINDOW_WIDTH/4,WINDOW_WIDTH/16),
angle,
0,
360,
Scalar(255,129,0),
thickness,
lineType
);
}
Os parâmetros da função elipse e seus significados:
3.2 Como escrever a função DrawFilledCircle()
//自定义的绘制函数,实现了实心圆的绘制
void DrawFilledCircle(Mat img, Point center) {
int thickness = -1;
int lineType = 8;
circle(img,
center,
WINDOW_WIDTH / 32,
Scalar(0, 0, 255),
thickness,
lineType
);
}
3.3 Função main()
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
#define WINDOW_WIDTH 600
#define WINDOW_NAME "绘制图"
int main() {
Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
//1.1:绘制椭圆
DrawEllipse(atomImage, 90);
DrawEllipse(atomImage, 0);
DrawEllipse(atomImage, 45);
DrawEllipse(atomImage, -45);
//1.2:绘制圆心
DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));
imshow(WINDOW_NAME, atomImage);
waitKey(0);
}