[Tutorial introdutório 6 do OpenCV] Criar Trackbar e ajustar o contraste da imagem e o valor de brilho
Esta série de artigos é produzida por @ 浅 墨 _ 毛星云, favor indicar a fonte para a reimpressão.
Link do artigo: http://blog.csdn.net/poem_qianmo/article/details/21176257
A versão OpenCV usada ao escrever a postagem atual do blog: 4.20
Neste artigo, aprendemos como criar e usar track bars com a função createTrackbar no OpenCV, bem como o ajuste dinâmico dos valores de contraste e brilho da imagem.
O artigo primeiro explicou em detalhes a função createTrackbar na nova versão do OpenCV2.0 e deu um exemplo de um comentário detalhado.
Em seguida, explique os detalhes do contraste da imagem e ajuste de brilho, e finalmente lançou um código-fonte do programa que usa a função createTrackbar para criar uma barra de controle para auxiliar no contraste da imagem e ajuste de brilho.
Ainda coloque uma captura de tela da operação primeiro:
OK, vamos começar oficialmente nossa explicação.
1. A criação e uso de Trackbar em OpenCV
<1> Crie uma barra de trilha - uma explicação detalhada da função createTrackbar
A função createTrackbar será usada freqüentemente no futuro.Ela cria uma barra de controle que pode ajustar o valor e anexa a barra de controle à janela especificada, o que é muito conveniente de usar. Em primeiro lugar, todos devem se lembrar que geralmente é usado em conjunto com uma função de retorno de chamada. Primeiro, olhe para seu protótipo de função:
C++: int createTrackbar(conststring& trackbarname, conststring& winname,
int* value, int count, TrackbarCallback onChange=0,void* userdata=0);
O primeiro parâmetro , trackbarname de const string & type, representa o nome da barra de trilha, que é usado para representar a barra de trilha que criamos.
O segundo parâmetro , winname de const string & type, preenche o nome da janela, que indica a qual janela a barra de controle será anexada, o que corresponde a um determinado nome de janela preenchido quando namedWindow () cria a janela.
O terceiro parâmetro , valor do tipo int *, um ponteiro para um inteiro, indicando a posição do controle deslizante. E quando ele é criado, a posição inicial do controle deslizante é o valor atual da variável.
O quarto parâmetro , contagem do tipo int, representa o valor da posição máxima que o controle deslizante pode alcançar. PS: O valor da menor posição do controle deslizante é sempre 0.
O quinto parâmetro , onChange do tipo TrackbarCallback, primeiro observe que ele tem um valor padrão de 0. Este é um ponteiro para uma função de retorno de chamada. Essa função fará um retorno de chamada toda vez que a posição do controle deslizante mudar. E o protótipo desta função deve ser void XXXX (int, void *); o primeiro parâmetro é a posição da barra de controle e o segundo parâmetro são os dados do usuário (veja o sexto parâmetro abaixo). Se o retorno de chamada for um ponteiro NULL, significa que não há chamada de função de retorno de chamada e apenas o terceiro valor do parâmetro foi alterado.
O sexto parâmetro , userdata do tipo void *, também tem um valor padrão de 0. Este parâmetro são os dados passados pelo usuário para a função de retorno de chamada para processar os eventos da barra de controle. Se o terceiro argumento de valor de parâmetro usado for uma variável global, você pode ignorar o parâmetro userdata.
Esta função createTrackbar cria uma barra de trilha (Trackbar ou ferramenta de controle de faixa deslizante) com um nome e intervalo específicos para nós, e especifica uma variável que é sincronizada com a posição da barra de trilha. E você precisa especificar a função de retorno de chamada onChange (o quinto parâmetro), que é chamada quando a posição da barra de controle muda. E sabemos que a barra de trilha criada é exibida na janela representada pelo winname especificado (o segundo parâmetro).
Depois de ler a explicação da função, vamos primeiro dar um pequeno exemplo de uso da função:
//创建轨迹条
createTrackbar("对比度:", "【效果图窗口】",&g_nContrastValue,
300,ContrastAndBright );// g_nContrastValue为全局的整型变量,ContrastAndBright为回调函数的函数名(即指向函数地址的指针)
Vamos dar um exemplo completo de uso. Este é o programa de amostra oficial do OpenCV, um programa que demonstra o uso de barras de controle para controlar a detecção e o preenchimento do contorno. Qianmo disponibiliza sua modificação, simplificação de código e comentários detalhados para que todos possam digerir e estudar. Haverá uma explicação mais detalhada sobre a detecção de contorno na postagem atualizada do blog posteriormente.
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
//-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
using namespace std;
//-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数声明
//-----------------------------------------------------------------------------------------------
Mat img;
int threshval = 160; //轨迹条滑块对应的值,给初值160
//-----------------------------【on_trackbar( )函数】------------------------------------
// 描述:轨迹条的回调函数
//-----------------------------------------------------------------------------------------------
static void on_trackbar(int, void*)
{
Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);
//定义点和向量
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
//查找轮廓
findContours(bw, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
//初始化dst
Mat dst = Mat::zeros(img.size(), CV_8UC3);
//开始处理
if (!contours.empty() && !hierarchy.empty())
{
//遍历所有顶层轮廓,随机生成颜色值绘制给各连接组成部分
int idx = 0;
for (; idx >= 0; idx = hierarchy[idx][0])
{
Scalar color((rand() & 255), (rand() & 255), (rand() & 255));
//绘制填充轮廓
drawContours(dst, contours, idx, color, FILLED, 8, hierarchy);
}
}
//显示窗口
imshow("Connected Components", dst);
}
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main()
{
system("color 5F");
//载入图片
img = imread("1.jpg", 0);
if (!img.data) { printf("Oh,no,读取img图片文件错误~! \n"); return -1; }
//显示原图
namedWindow("Image", 1);
imshow("Image", img);
//创建处理窗口
namedWindow("Connected Components", 1);
//创建轨迹条
createTrackbar("Threshold", "Connected Components", &threshval, 255, on_trackbar);
on_trackbar(threshval, 0);//轨迹条回调函数
waitKey(0);
return 0;
}
Imagem original:
Imagem de efeito de corrida:
Arraste a barra de rolagem para alterar o valor do limiar (limite) para obter imagens com efeitos diferentes:
Além disso, você pode encontrar o código oficial original no caminho OpenCV opencv_source_code / samples / cpp / connected_components.cpp.
A propósito, vamos falar sobre uma função usada com createTrackbar, a função getTrackbarPos usada para obter a posição da barra de controle atual.
<2> Obtenha a posição da função track bar-getTrackbarPos atual
<2> Obtenha a posição da função track bar-getTrackbarPos atual
Esta função é usada para obter a posição da barra de trilha atual e retorná-la.
O primeiro parâmetro , trackbarname do tipo const string &, representa o nome da trackbar.
O segundo parâmetro , winname do tipo const string &, representa o nome da janela pai da barra de controle.
Provavelmente é isso para esta parte. Vamos para a próxima parte sem parar :)
2. Base teórica para ajuste de brilho e contraste
Primeiro, damos o conceito de operadores. O operador geral de processamento de imagem é uma função que aceita uma ou mais imagens de entrada e produz uma imagem de saída. A fórmula a seguir fornece a forma geral do operador:
A operação de ajuste do brilho e contraste da imagem que estamos explicando hoje é, na verdade, um tipo relativamente simples de operadores de ponto de transformação de processamento de imagem. A operação de ponto tem uma característica, apenas com base no valor do pixel de entrada (às vezes pode adicionar algumas informações ou parâmetros globais), para calcular o valor do pixel de saída correspondente. Esses operadores incluem ajustes de brilho e contraste, bem como correções e transformações de cores.
As duas operações de ponto (ou operadores de ponto) mais comumente usadas, obviamente, são multiplicar por uma constante (correspondendo ao ajuste do contraste) e adicionar uma constante (correspondendo ao ajuste do valor de brilho). A fórmula é assim:
Vendo essa fórmula, nossa estratégia de ajuste de brilho e contraste da imagem está pronta para sair.
entre eles:
** Parâmetro f (x) ** representa o pixel da imagem de origem.
O parâmetro g (x) representa os pixels da imagem de saída.
O parâmetro a (precisa satisfazer a> 0) é chamado de ganho e é freqüentemente usado para controlar o contraste da imagem.
O parâmetro b é geralmente chamado de viés e é freqüentemente usado para controlar o brilho da imagem.
E um passo mais perto, reescrevemos esta fórmula assim:
onde i e j indicam que o pixel está localizado na i-ésima linha e j-ésima coluna.
Então, esta fórmula pode ser usada como uma fórmula teórica para controlar o brilho e o contraste da imagem no OpenCV.
3. Sobre o acesso a pixels em imagens
Existem muitas maneiras de acessar os pixels na imagem, e a tinta clara usará um tópico para explicar no futuro. No momento, podemos primeiro entender o seguinte.
Para realizar esta operação, precisamos acessar cada pixel da imagem. Como a operação é realizada em imagens GBR, cada pixel possui três valores (G, B, R), portanto, devemos acessá-los separadamente (o modo de armazenamento de imagens em PS: OpenCV é GBR). O seguinte é um snippet de código para acessar o pixel. Três loops for resolvem o problema:
//三个for循环,执行运算 new_image(i,j) =a*image(i,j) + b
for(int y = 0; y < image.rows; y++ )
{
for(int x = 0; x < image.cols; x++ )
{
for(int c = 0; c < 3; c++ )
{
new_image.at<Vec3b>(y,x)[c]= saturate_cast<uchar>( (g_nContrastValue*0.01)*(image.at<Vec3b>(y,x)[c] ) + g_nBrightValue );
}
}
}
Vamos explicar em três aspectos:
Para acessar cada pixel da imagem, usamos esta sintaxe: image.at (y, x) [c]
Entre eles, y é a linha onde o pixel está localizado, x é a coluna onde o pixel está localizado e c é um de R, G e B (correspondendo a 0, 1, 2).
Como o resultado do nosso cálculo pode exceder a faixa de valores de pixel (estouro) ou pode ser um número não inteiro (se for um número de ponto flutuante), precisamos usar saturate_cast para converter o resultado para garantir que seja um valor válido.
Aqui, a é o contraste. Geralmente, o valor é um valor de ponto flutuante de 0,0 a 3,0 para o efeito da observação, mas nossa barra de controle geralmente assume um valor inteiro, então aqui podemos definir o parâmetro nContrastValue que representa o valor de contraste. é um número inteiro entre 0 e 300. Multiplique 0,01 na fórmula final, para que você possa completar a alteração de 300 valores diferentes na barra de controle. Portanto, na fórmula, veremos g_nContrastValue * 0,01 em saturate_cast ((g_nContrastValue 0,01) (image.at (y, x) [c]) + g_nBrightValue).
Quatro, contraste de imagem, programa de amostra de ajuste de valor de brilho
Ainda é uma postagem de blog com anotações detalhadas que dá suporte ao programa de amostra que cada artigo será atribuído a você. Os pontos de conhecimento apresentados neste artigo são usados como uma portadora para mostrar o código.
Este programa de amostra usa duas barras de controle para controlar os valores de contraste e brilho, respectivamente, que tem um certo grau de capacidade de reprodução. Sem muita bobagem, vamos ao código:
// 操作系统: Windows 10 64bit
// 开发语言: C++
// IDE 版 本:Visual Studio 2019
// OpenCV版本:4.20
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
//-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace std;
using namespace cv;
//-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数声明
//-----------------------------------------------------------------------------------------------
static void ContrastAndBright(int, void*);
void ShowHelpText();
//-----------------------------------【全局变量声明部分】--------------------------------------
// 描述:全局变量声明
//-----------------------------------------------------------------------------------------------
int g_nContrastValue; //对比度值
int g_nBrightValue; //亮度值
Mat g_srcImage, g_dstImage;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main()
{
//改变控制台前景色和背景色
system("color 2F");
// 读入用户提供的图像
g_srcImage = imread("1.jpg");
if (!g_srcImage.data) { printf("读取g_srcImage图片错误~! \n"); return false; }
g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type());
//设定对比度和亮度的初值
g_nContrastValue = 80;
g_nBrightValue = 80;
//创建窗口
namedWindow("【效果图窗口】", 1);
//创建轨迹条
createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, ContrastAndBright);
createTrackbar("亮 度:", "【效果图窗口】", &g_nBrightValue, 200, ContrastAndBright);
//调用回调函数
ContrastAndBright(g_nContrastValue, 0);
ContrastAndBright(g_nBrightValue, 0);
//输出一些帮助信息
cout << endl << "\t运行成功,请调整滚动条观察图像效果\n\n"
<< "\t按下“q”键时,程序退出\n";
//按下“q”键时,程序退出
while (char(waitKey(1)) != 'q') {}
return 0;
}
//-----------------------------【ContrastAndBright( )函数】------------------------------------
// 描述:改变图像对比度和亮度值的回调函数
//-----------------------------------------------------------------------------------------------
static void ContrastAndBright(int, void*)
{
// 创建窗口
namedWindow("【原始图窗口】", 1);
// 三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b
for (int y = 0; y < g_srcImage.rows; y++)
{
for (int x = 0; x < g_srcImage.cols; x++)
{
for (int c = 0; c < 3; c++)
{
g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrastValue * 0.01) * (g_srcImage.at<Vec3b>(y, x)[c]) + g_nBrightValue);
}
}
}
// 显示图像
imshow("【原始图窗口】", g_srcImage);
imshow("【效果图窗口】", g_dstImage);
}
Finalmente, dê uma olhada nas imagens da operação.Quando você executa este programa, você obterá duas janelas de exibição de imagens. A primeira é a janela da imagem original e a segunda é a janela da imagem do efeito. Duas barras de controle podem ser ajustadas na janela da imagem de efeito para alterar o contraste e o brilho da imagem atual.
Imagem original: