为快速上手,先从第三类的Mat类开始。
比较具有代表性的是 cv::Mat和cv::SparseMat 类型。
cv::Mat针对的是密集连续性的存储,大多数的图像数据被存储为这种类,即使数据为空,预留的存储空间仍然存在;
cv::SparseMat针对的是稀疏的存储方式,只有数据不为0才保留空间,否则不会预留。显然cv::SparseMat存储更为节省空间,典型使用cv::SparseMat的例如直方图数据的存储。
Mat 是一个类,由两个数据部分组成:
矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)
一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。
imread功能是加载图像文件成为一个Mat对象,其中:
第一个参数为图像文件的路径;
第二个参数,表示加载的图像是什么类型,支持常见的三个参数值:
注意:OpenCV支持JPG、PNG、TIFF等常见格式图像文件加载。
同时用到的函数还有:
//加载图像
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://1.jpg"); //加载图像
if (image.empty())
{
return -1;
}
namedWindow("Example1", WINDOW_AUTOSIZE); //创建窗口
imshow("Example1", image); //显示图像
waitKey(0);
destroyWindow("Example1"); //销毁窗口
return 0;
}
//加载为灰度图
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://1.jpg", 0); //IMREAD_GRAYSCALE (=0)表示把原图作为灰度图像加载进来
if (image.empty())
{
return -1;
}
namedWindow("Example1", WINDOW_AUTOSIZE);
imshow("Example1", image);
waitKey(0);
destroyWindow("Example1");
return 0;
}
imwrite功能是保存图像文件到指定目录路径,有三个参数:
第一个参数表示需要写入的路径&文件名,必须要加上后缀,比如“123.png”;
第二个参数表示Mat类型的图像数据,是你要保存的对象。
第三个参数表示为特定格式保存的参数编码,它有一个默认值std::vector< int >(),所以一般情况下不用写。
注意:只有8位、16位的PNG、JPG、Tiff文件格式而且是单通道或者三通道的BGR的图像才可以通过这种方式保存,保存PNG格式的时候可以保存透明通道的图片
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://1.jpg");
if (image.empty())
{
return -1;
}
Mat image_out;
cvtColor(image, image_out, COLOR_BGR2HLS); //转换为灰度图
imwrite("D://1_out.png", image_out); //将转换后image_out的写入到"D://1_out.png"
namedWindow("Output Window", WINDOW_AUTOSIZE);
imshow("Output Window", image_out);
waitKey(0);
destroyWindow("Output Window");
return 0;
}
赋值一个全为0的Mat:img = Scalar(0)。
//三种建立Mat类的方式
#include
#include
using namespace cv;
using namespace std;
int main(int argc, char** args) {
Mat M;
M.create(4, 3, CV_32FC3);
M = Scalar((126.3928), (12.3334), (246.12));
cout << "M = " << endl << M << endl << endl;
Mat A = Mat(3, 2, CV_16SC3);
A = Scalar(1, 2, 3);
cout << endl << "A = " << endl << A << endl << endl;
Mat D = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "D = " << endl << D << endl << endl;
waitKey(0);
return 0;
}
/*
M =
[126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12;
126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12;
126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12;
126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12]
A =
[1, 2, 3, 1, 2, 3;
1, 2, 3, 1, 2, 3;
1, 2, 3, 1, 2, 3]
D =
[0, -1, 0;
-1, 5, -1;
0, -1, 0]
C:\Users\CK Yang\source\repos\LXCV01\x64\Debug\LXCV01.exe (进程 18808)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
*/
}
创建Mat类型的函数十分灵活:
#include
#include
using namespace cv;
using namespace std;
int main(int argc, char** args) {
Mat M;
M.create(4, 3, CV_32FC3);
M = Scalar((126.3928), (12.3334), (246.12));
cout << "M = " << endl << M << endl << endl;
Mat A = Mat(3, 2, CV_16SC3);
A = Scalar(1, 2, 3);
cout << endl << "A = " << endl << A << endl << endl;
Mat B = Mat(Size(3, 4), CV_8SC3);
B = Scalar(2, 3, 4);
cout << "B = " << endl << B << endl << endl;
Mat C = Mat(Size(2, 3), CV_16SC2, Scalar(2, 3, 4));
cout << "C = " << endl << C << endl << endl;
Mat D = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "D = " << endl << D << endl << endl;
waitKey(0);
return 0;
}
/*
A =
[1, 2, 3, 1, 2, 3;
1, 2, 3, 1, 2, 3;
1, 2, 3, 1, 2, 3]
B =
[ 2, 3, 4, 2, 3, 4, 2, 3, 4;
2, 3, 4, 2, 3, 4, 2, 3, 4;
2, 3, 4, 2, 3, 4, 2, 3, 4;
2, 3, 4, 2, 3, 4, 2, 3, 4]
C =
[2, 3, 2, 3;
2, 3, 2, 3;
2, 3, 2, 3]
D =
[0, -1, 0;
-1, 5, -1;
0, -1, 0]
*/
还可通过imread(“C:/2.jpg”);的方式建立图像的Mat
#include
#include
using namespace cv;
using namespace std;
int main(int argc, char** args)
{
Mat image = imread("C:/2.jpg");
if (image.empty())
{
return -1;
}
namedWindow("My Image", WINDOW_AUTOSIZE);
imshow("My Image", image);
Mat M;
M.create(4, 3, CV_32FC3);
M = Scalar((126.3928), (12.3334), (246.12));
cout << "M = " << endl << " " << M << endl << endl;
uchar* firstRow = M.ptr<uchar>(0);
printf("%d\n", *firstRow);
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
waitKey(0);
cvDestroyAllWindows();
return 0;
}
Mat的type和depth:
例:CV_32FC3——表示的是元素类型是一个32位的浮点数,通道为3。
type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用depth:
depth是矩阵中元素的一个通道的数据类型,这个值和type是相关的。
例如: type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。
常用方法:
注:
部分复制:一般情况下只会复制Mat对象的头和指针部分,不会复制数据部分
完全复制:如果想把Mat对象的头部和数据部分一起复制,可以通过如下两个API实现
#include
#include
using namespace cv;
using namespace std;
int main(int argc, char** args) {
Mat M;
M.create(4, 3, CV_32FC3);
M = Scalar((126.3928), (12.3334), (246.12));
cout << "M = " << endl << M << endl << endl;
Mat A = Mat(3, 2, CV_16SC3);
A = Scalar(1, 2, 3);
cout << endl << "A = " << endl << A << endl << endl;
Mat B = Mat(Size(3, 4), CV_8SC3);
B = Scalar(2, 3, 4);
cout << "B = " << endl << B << endl << endl;
Mat E;
A.copyTo(E);
cout << "E = " << endl << E << endl << endl;
Mat F;
A.convertTo(F, CV_32FC3);
cout << "F = " << endl << F << endl << endl;
Mat G = B.clone();
cout << "G = " << endl << G << endl << endl;
cvtColor( image, gray_image, COLOR_BGR2GRAY )
cvtColor功能是把图像从一个彩色空间转换到另外一个色彩空间,有三个参数:
第一个参数表示源图像;
第二参数表示色彩空间转换之后的图像;
第三个参数表示源和目标色彩空间如:COLOR_BGR2HLS 、COLOR_BGR2GRAY 等。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://1.jpg");
if (image.empty())
{
return -1;
}
Mat image_out;
cvtColor(image, image_out, COLOR_BGR2HLS); //转换色彩空间为COLOR_BGR2HLS
namedWindow("Output Window", WINDOW_AUTOSIZE);
imshow("Output Window", image_out);
waitKey(0);
destroyWindow("Output Window");
return 0;
}
Mat.ptr(int i=0) 功能是获取像素矩阵的指针,索引 i 表示行数,从0开始计数。
注意:掩膜Mask也被称之为Kernel。
实现一个下图的掩膜操作,功能是提高图像的对比度:
这能干嘛?我下载的某个PDF文档看着比较瞎眼,就可以用这个来让它看着没那么瞎眼。
saturate_cast< uchar>()是像素范围处理函数,功能是确保RGB值的范围在0~255之间。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://2.png");
Mat image_output = Mat::zeros(image.size(), image.type()); //创立一个元素全为0的Mat类型作为输出图像,size和type与输入图像保持一致。
if (image.empty())
{
return -1;
}
namedWindow("Input",WINDOW_AUTOSIZE);
imshow("Input", image);
int cols = image.cols * image.channels();
int rows = image.rows;
for (int row = 1; row < (rows - 1); row++)
{
const uchar* current = image.ptr<uchar>(row); //获取当前行指针
const uchar* next = image.ptr<uchar>(row + 1); //获取下一行指针
const uchar* previous = image.ptr<uchar>(row - 1); //获取上一行指针
uchar* output = image_output.ptr<uchar>(row); //获取想要输出的图像的行指针
for (int col = 1 * image.channels(); col < cols; col++) //为什么要* image.channels()?思考下彩色图像显示的原理。
{
output[col] = saturate_cast<uchar> ( current[col] * 5 - (current[col] - image.channels()) - (current[col] + image.channels()) - next[col] - previous[col] );
//掩膜操作
}
}
namedWindow("Output", WINDOW_AUTOSIZE);
imshow("Output", image_output);
waitKey(0);
destroyWindow("Output");
destroyWindow("INput");
return 0;
}
除了利用图像像素指针进行掩膜操作,还能用OpenCV的api完成同样的操作,操作步骤:
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://2.png");
Mat image_output = Mat::zeros(image.size(), image.type());
if (image.empty())
{
return -1;
}
namedWindow("Input", WINDOW_AUTOSIZE);
imshow("Input", image);
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); //定义掩膜
filter2D(image, image_output, image.depth(), kernel); //调用filter2D
namedWindow("Output", WINDOW_AUTOSIZE);
imshow("Output", image_output);
waitKey(0);
destroyWindow("Output");
destroyWindow("INput");
return 0;
}
//获取灰度图像的像素值
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://wn.jpg");
Mat image_gray;
if (image.empty())
{
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", image);
cvtColor(image, image_gray, COLOR_BGR2GRAY);
namedWindow("output_gray", WINDOW_AUTOSIZE);
imshow("output_gray", image_gray);
int height = image_gray.rows; //获取image_gray的行数,作为图像的高度
int width = image_gray.cols; //获取image_gray的列数,作为图像的宽度
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int pixel_gray = image_gray.at<uchar>(row, col); //获取每一个灰度像素点的值
cout << "rows = " << row << ",cols = " << col << ",pixel_gray = " << pixel_gray << endl;
}
}
waitKey(0);
return 0;
}
//灰度图像颜色反转
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://wn.jpg");
Mat image_gray;
if (image.empty())
{
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", image);
cvtColor(image, image_gray, COLOR_BGR2GRAY);
namedWindow("output_gray", WINDOW_AUTOSIZE);
imshow("output_gray", image_gray);
int height = image_gray.rows; //获取image_gray的行数,作为图像的高度
int width = image_gray.cols; //获取image_gray的列数,作为图像的宽度
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int pixel_gray = image_gray.at<uchar>(row, col); //获取每一个灰度像素点的值,赋值给pixel_gray
cout << "rows = " << row << ",cols = " << col << ",pixel_gray = " << pixel_gray << endl;
image_gray.at<uchar>(row, col) = 255 - pixel_gray; //修改每一个灰度像素点的值,效果是颜色反转。
}
}
namedWindow("output_gray_reverse", WINDOW_AUTOSIZE);
imshow("output_gray_reverse", image_gray); //显示修改像素值后的图片
waitKey(0);
return 0;
}
彩色像素值的修改,以颜色反转为例:
//rgb图像颜色反转
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://wn.jpg");
Mat image_gray;
if (image.empty())
{
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", image);
cvtColor(image, image_gray, COLOR_BGR2GRAY);
namedWindow("output_gray", WINDOW_AUTOSIZE);
imshow("output_gray", image_gray);
int height = image.rows;
int width = image.cols;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int pixel_b = image.at<Vec3b>(row, col)[0];
int pixel_g = image.at<Vec3b>(row, col)[1];
int pixel_r = image.at<Vec3b>(row, col)[2];
cout << "rows = " << row << ",cols = " << col << ",Blue = " << pixel_b << ",Green = " << pixel_g << ",Red = " << pixel_r << endl;
image.at<Vec3b>(row, col)[0] = 255 - pixel_b;
image.at<Vec3b>(row, col)[1] = 255 - pixel_g;
image.at<Vec3b>(row, col)[2] = 255 - pixel_r;
}
}
namedWindow("output_reverse", WINDOW_AUTOSIZE);
imshow("output_reverse", image);
waitKey(0);
return 0;
}
注意:OpenCV中有api可以实现图片色彩反转:bitwise_not(image, image_out);
//利用bitwise_not(image, image_out);进行色彩反转
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://wn.jpg");
Mat image_gray;
Mat image_out;
if (image.empty())
{
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", image);
cvtColor(image, image_gray, COLOR_BGR2GRAY);
namedWindow("output_gray", WINDOW_AUTOSIZE);
imshow("output_gray", image_gray);
bitwise_not(image, image_out);
namedWindow("output_reverse", WINDOW_AUTOSIZE);
imshow("output_reverse", image_out);
waitKey(0);
return 0;
}
f0——第一张图像;
f1——第二张图像;
α——混合参数。
相关api:addWeighted(),作用就是线性混合。
注意:两张图像的大小和类型必须一致才可以进行操作。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image0 = imread("C://eg//wn.jpg"); //输入图像0
Mat image1 = imread("C://eg//m.jpg"); //输入图像1
Mat image_out;
float alpha = 0.1; //混合参数α
if (image0.empty() || image1.empty()) {
cout << "images are not found " << endl;
return -1;
}
namedWindow("input0", WINDOW_AUTOSIZE);
imshow("input0", image0);
namedWindow("input1", WINDOW_AUTOSIZE);
imshow("input1", image1);
if (image0.cols == image1.cols && image0.rows == image1.rows && image0.type() == image1.type()) {
addWeighted(image0, alpha, image1, (1 - alpha), 0.0, image_out);
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", image_out);
}
else {
cout << "2 images' size or type are different " << endl;
return -1;
}
waitKey(0);
cvDestroyAllWindows();
return 0;
}
}
权重不同,混合程度也不同,结果如下:
alpha = 0.25
alpha = 0.75
图像变换可以看作如下:
调整图像亮度和对比度属于像素变换-点操作。
相关api:
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("C://eg//jr.jpg"); //输入图像
Mat image_gray;
cvtColor(image, image_gray, COLOR_BGR2GRAY);
Mat image_out = Mat::zeros(image.size(), image.type()); //创立和image同规格的图像一张,装满0
float alpha = 1.3; //参数
float beta = 30;
if (image.empty()) {
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", image);
int height = image.rows;
int width = image.cols;
Mat image_cvt2F;
image.convertTo(image_cvt2F, CV_32FC3); //image.at(row, col)[0];会报错,需要先convertTo CV_32FC3类型再image_cvt2F.at(row, col)[0];即可
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (image.channels() == 3) {
float pixel_b = image_cvt2F.at<Vec3f>(row, col)[0];
float pixel_g = image_cvt2F.at<Vec3f>(row, col)[1];
float pixel_r = image_cvt2F.at<Vec3f>(row, col)[2];
image_out.at<Vec3f>(row, col)[0] = saturate_cast<uchar>(pixel_b * alpha + beta);
image_out.at<Vec3f>(row, col)[1] = saturate_cast<uchar>(pixel_g * alpha + beta);
image_out.at<Vec3f>(row, col)[2] = saturate_cast<uchar>(pixel_r * alpha + beta);
}
else if(image.channels() == 1) {
int pixel_gray = image.at<uchar>(row, col);
image_out.at<uchar>(row, col) = saturate_cast<uchar>(pixel_gray * alpha + beta);
}
}
}
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", image_out);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
#include
#include
using namespace std;
using namespace cv;
Mat image; //声明全局的Mat image
void Line(); //声明绘制Line的函数Line()
void Rectangle(); //矩形
void Ellipse(); //椭圆
void Circle(); //圆
void FillPolygon(); //实心多边形
void Text(); //文本
int main()
{
image = imread("C://eg//2.jpg"); //输入图像
if (image.empty()) {
return -1;
}
Line(); //
Rectangle(); //
Ellipse(); //
Circle();
FillPolygon();
Text();
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", image);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
void Line() {
Point a = Point(10, 20); //坐标
Point b = Point(110, 120);
Scalar color = Scalar(0, 0, 255); //颜色为红
line(image, a, b, color, 1, LINE_4);
}
void Rectangle() {
Rect rect = Rect(50, 100, 50, 50); //坐标&形状
Scalar color = Scalar(255, 0, 0); //蓝色
rectangle(image, rect, color, 1, LINE_4);
}
void Ellipse() {
Point a = Point(image.rows / 2, image.cols / 2); //圆心
Size b = Size(image.rows / 8, image.cols / 4); //尺寸
Scalar color = Scalar(0, 255, 0); //绿色
ellipse(image, a, b, 90, 0, 360, color, 1, LINE_4);
}
void Circle() {
Point a = Point(image.rows / 1.5, image.cols / 1.2); //圆心
int r = image.rows / 6; //半径
Scalar color = Scalar(203, 192, 255); //pink
circle(image, a, r, color, 1, LINE_4);
}
void FillPolygon() {
Point pts[1][5];
pts[0][0] = Point(100, 100);
pts[0][1] = Point(150, 225);
pts[0][2] = Point(200, 100);
pts[0][3] = Point(80, 180);
pts[0][4] = Point(220, 180);
const Point* ppts[] = {
pts[0] };
int npt[] = {
5 };
Scalar color = Scalar(219, 112, 147);
fillPoly(image, ppts, npt, 1, color, 8);
}
void Text() {
Point p = Point(160, 460);
Scalar color = Scalar(60, 20, 220);
string text = "????";
putText(image, text, p, CV_FONT_NORMAL, 2, color, 2, LINE_4);
}
#include
#include
using namespace std;
using namespace cv;
Mat image; //声明全局的Mat image
void RDMLine();
int main()
{
image = imread("C://eg//2.jpg"); //输入图像
if (image.empty()) {
return -1;
}
RDMLine();
waitKey(0);
cvDestroyAllWindows();
return 0;
}
void RDMLine() {
RNG rng(getTickCount());
Point p1, p2;
Mat image0 = Mat::zeros(image.size(), image.type());
namedWindow("RDMLine", WINDOW_AUTOSIZE);
for (int i = 0; i < 100000; i++) {
p1.x = rng.uniform(0, image.cols);
p2.x = rng.uniform(0, image.cols);
p1.y = rng.uniform(0, image.rows);
p2.y = rng.uniform(0, image.rows);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
if (waitKey(50) > 0) {
break;
}
line(image0, p1, p2, color, 1, LINE_4);
imshow("RDMLine", image0);
}
}
#include
#include
using namespace cv;
void SPNoice(Mat image_0, int n);
int main(int argc, char** argv) {
Mat image;
image = imread("C://eg//akane.jpg");
if (image.empty()) {
return -1;
}
char input_title[] = "input image";
char output_title0[] = "output image";
namedWindow(input_title, CV_WINDOW_AUTOSIZE);
namedWindow(output_title0, CV_WINDOW_AUTOSIZE);
imshow(input_title, image);
SPNoice(image,500);
imwrite("C://eg//akane_spnoice.jpg", image); //保存椒盐噪声图
imshow(output_title0, image);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
//
void SPNoice( Mat image_0, //输入输出图像
int n //椒盐个数
)
{
int noice = 0; //噪声种类,0为椒噪声,1为盐噪声
int C_row = 0; //像素点
int C_col = 0;
//每循环一次,添加一个椒盐噪声
while (n > 0) {
noice = rand() % 2; //随机确定椒盐噪声种类
//随机产生函数获取被修改的图像Mat对应像素点的位置
C_row = rand() % image_0.rows;
C_col = rand() % image_0.cols;
//灰度图
if (image_0.channels() == 1) {
if (noice == 0) {
image_0.at<uchar>(C_row, C_col) = 0;
}
else if(noice == 1) {
image_0.at<uchar>(C_row, C_col) = 255;
}
}
//RGB图
else if(image_0.channels() == 3) {
if (noice == 0) {
image_0.at<Vec3b>(C_row, C_col)[0] = 0;
image_0.at<Vec3b>(C_row, C_col)[1] = 0;
image_0.at<Vec3b>(C_row, C_col)[2] = 0;
}
else if (noice == 1) {
image_0.at<Vec3b>(C_row, C_col)[0] = 255;
image_0.at<Vec3b>(C_row, C_col)[1] = 255;
image_0.at<Vec3b>(C_row, C_col)[2] = 255;
}
}
n--;
}
}
卷积:假设有6x6的图像像素点矩阵。6x6上面是个3x3的窗口,从左向右,从上向下移动,黄色的每个像个像素点值之和取平均值赋给中心红色像素作为它卷积处理之后新的像素值。每次移动一个像素格。
#include
#include
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_blur, image_Gblur;
image = imread("C://eg//akane_spnoice.jpg");
if (image.empty()) {
return -1;
}
char input_title[] = "input image";
char output_title0[] = "blur image";
String output_title1 = "gaussian blur image";
namedWindow(input_title, CV_WINDOW_AUTOSIZE);
namedWindow(output_title0, CV_WINDOW_AUTOSIZE);
namedWindow(output_title1, WINDOW_AUTOSIZE);
imshow(input_title, image);
blur(image, image_blur, Size(5, 5), Point(-1, -1)); //均值模糊
imshow(output_title0, image_blur);
GaussianBlur(image, image_Gblur, Size(11, 11), 11, 11); //高斯模糊
imshow(output_title1, image_Gblur);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
中值模糊medianBlur(Mat src, Mat dest, ksize)
双边模糊bilateralFilter(src, dest, d, sigma color, sigma space)
#include
#include
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_median_blur, image_bilateral_blur;
image = imread("C://eg//akane_spnoice.jpg");
if (image.empty()) {
return -1;
}
char input_title[] = "input image";
char output_title0[] = "median blur image";
String output_title1 = "bilateral blur image";
namedWindow(input_title, CV_WINDOW_AUTOSIZE);
namedWindow(output_title0, CV_WINDOW_AUTOSIZE);
namedWindow(output_title1, WINDOW_AUTOSIZE);
imshow(input_title, image);
medianBlur(image, image_median_blur, 3);
imshow(output_title0, image_median_blur);
bilateralFilter(image, image_bilateral_blur, 15, 150, 20);
imshow(output_title1, image_bilateral_blur);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
关键api:
getStructuringElement(int shape, Size ksize, Point anchor):返回指定形状和尺寸的结构元素。
shape形状 (MORPH_RECT \MORPH_CROSS \MORPH_ELLIPSE);
ksize大小;
anchor锚点,默认是Point(-1, -1)意思就是中心像素。
dilate(image, image_dilate, kernel)膨胀;
erode(image, image_erode, kernel)腐蚀.
#include
#include
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_dilate, image_erode;
image = imread("C://eg//i.png");
if (image.empty()) {
return -1;
}
imshow("image", image);
Mat kernel = getStructuringElement(MORPH_RECT, Size(7, 7), Point(-1, -1));
dilate(image, image_dilate, kernel);
erode(image, image_erode, kernel);
imshow("image_dilate", image_dilate);
imshow("image_erode", image_erode);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
开:先腐蚀后膨胀,可以去掉小的对象,假设对象是前景色,背景是黑色;
闭:先膨胀后腐蚀,可以填充小的洞,假设对象是前景色,背景是黑色;
形态学梯度:膨胀减去腐蚀,又称为基本梯度(其它还包括-内部梯度、方向梯度);
顶帽:原图像和开操作图像的差值;
黑帽:闭操作图像和原图像的差值。
关键api:
morphologyEx(src, dest, CV_MOP_BLACKHAT, kernel);
#include
#include
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_open, image_close, image_gradient, image_tophat, image_blackhat;
image = imread("C://eg//i_spnoice.jpg");
if (image.empty()) {
return -1;
}
imshow("image", image);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(-1, -1));
morphologyEx(image, image_open, CV_MOP_OPEN, kernel); //开
morphologyEx(image, image_close, CV_MOP_CLOSE, kernel); //闭
morphologyEx(image, image_gradient, CV_MOP_GRADIENT, kernel); //形态学梯度
morphologyEx(image, image_tophat, CV_MOP_TOPHAT, kernel); //顶帽
morphologyEx(image, image_blackhat, CV_MOP_BLACKHAT, kernel); //黑帽
imshow("image_open", image_open);
imshow("image_close", image_close);
imshow("image_gradient", image_gradient);
imshow("image_tophat", image_tophat);
imshow("image_blackhat", image_blackhat);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
#include
#include
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_gray, image_binary, imahe_line_w, imahe_line_h;
image = imread("C://eg//3.png");
if (image.empty()) {
return -1;
}
imshow("image", image);
if (image.channels() == 3) {
cvtColor(image, image_gray, COLOR_BGR2GRAY);
}
else if(image.channels() == 1) {
image_gray = image;
}
namedWindow("image_gray", WINDOW_AUTOSIZE);
imshow("image_gray", image_gray);
adaptiveThreshold(image_gray, image_binary, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, 1);
bitwise_not(image_binary, image_binary);
namedWindow("image_binary", WINDOW_AUTOSIZE);
imshow("image_binary", image_binary);
Mat line_w, line_h;
line_w = getStructuringElement(MORPH_RECT, Size(image.cols / 30, 1), Point(-1, -1));
line_h = getStructuringElement(MORPH_RECT, Size(1, image.rows / 30), Point(-1, -1));
morphologyEx(image_binary, imahe_line_w, CV_MOP_OPEN, line_w);
morphologyEx(image_binary, imahe_line_h, CV_MOP_OPEN, line_h);
imshow("imahe_line_w", imahe_line_w);
imshow("imahe_line_h", imahe_line_h);
waitKey(0);
cvDestroyAllWindows();
return 0;
}
两种采样的目的是放缩图片大小,放缩图像也可以使用几何变换,这里使用图像金字塔的方式,
升采样(cv::pyrUp) – zoom in 放大
降采样 (cv::pyrDown) – zoom out 缩小
高斯金子塔是从底向上,逐层降采样得到。
降采样之后图像大小是原图像MxN的M/2 x N/2 ,就是对原图像删除偶数行与列,即得到降采样之后上一层的图片。高斯金子塔的生成过程分为两步:
这样即可得到上一层的图像,这样上一层跟下一层相比,都只有它的1/4大小
高斯不同的定义:就是把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像。称为高斯不同(DOG)。
高斯不同是图像的内在特征,在灰度图像增强、角点检测中经常用到。
#include
#include
#include "math.h"
using namespace std;
using namespace cv;
int main(int agrc, char** argv) {
Mat image;
image = imread("C://eg//3.jpg");
if (image.empty()) {
return -1;
}
Mat image_big, image_small;
namedWindow("image", CV_WINDOW_AUTOSIZE);
namedWindow("image_big", CV_WINDOW_AUTOSIZE);
imshow("image", image);
pyrUp(image, image_big, Size(image.cols * 2, image.rows * 2));//升采样
imshow("image_big", image_big);
pyrDown(image, image_small, Size(image.cols / 2, image.rows / 2));//降采样
imshow("image_small", image_small);
Mat image_gray, g1, g2, g3;
cvtColor(image, image_gray, CV_BGR2GRAY);
GaussianBlur(image_gray, g1, Size(5, 5), 0, 0);
GaussianBlur(g1, g2, Size(5, 5), 0, 0);
subtract(g1, g2, g3, Mat()); //高斯不同
Mat image_DOG = g3;
normalize(image_DOG, image_DOG, 255, 0, NORM_MINMAX);
imshow("DOG Image", image_DOG);
waitKey(0);
return 0;
}
基本的图像阈值操作有:二值化、反二值化、截断、取零、反取零,还有后面三个以后再学。
#include
#include
#include
using namespace std;
using namespace cv;
int thresh = 128;
int main(int argc, char** argv) {
Mat image, image_gray, image_out0, image_out1, image_out2, image_out3, image_out4;
image = imread("C://eg//3.png");
if (image.empty()) {
printf("could not load image...\n");
return -1;
}
cvtColor(image, image_gray, CV_BGR2GRAY);
threshold(image_gray, image_out0, thresh, 255, THRESH_BINARY); //二值化
threshold(image_gray, image_out1, thresh, 255, THRESH_BINARY_INV); //反二值化
threshold(image_gray, image_out2, thresh, 255, THRESH_TRUNC); //截断
threshold(image_gray, image_out3, thresh, 255, THRESH_TOZERO); //取零
threshold(image_gray, image_out4, thresh, 255, THRESH_TOZERO_INV); //反取零
imshow("THRESH_BINARY", image_out0);
imshow("THRESH_BINARY_INV", image_out1);
imshow("THRESH_TRUNC", image_out2);
imshow("THRESH_TOZERO", image_out3);
imshow("THRESH_TOZERO_INV",image_out4);
waitKey(0);
return 0;
}
利用二值化阈值操作来去水印
#include
#include
#include
using namespace std;
using namespace cv;
int thresh = 175;
int main(int argc, char** argv) {
Mat image, image_gray, image_out0;
image = imread("C://eg//4.png");
if (image.empty()) {
printf("could not load image...\n");
return -1;
}
imshow("image", image);
cvtColor(image, image_gray, CV_BGR2GRAY);
threshold(image_gray, image_out0, thresh, 255, THRESH_BINARY);
imshow("THRESH_BINARY", image_out0);
imwrite("C://eg//4_out.png", image_out0);
waitKey(0);
return 0;
}
卷积是图像处理中一个操作,是kernel在图像的每个像素上的操作。Kernel本质上一个固定大小的矩阵数组,其中心点称为锚点(anchor point)。
把kernel放到像素数组之上,求锚点周围覆盖的像素乘积之和(包括锚点),用来替换锚点覆盖下像素点值称为卷积处理。
自定义卷积模糊:
filter2D(
Mat src, //输入图像
Mat dst, // 模糊图像
int depth, // 图像深度32/8
Mat kernel, // 卷积核/模板
Point anchor, // 锚点位置
double delta // 计算出来的像素+delta
)
注意:其中 kernel是可以自定义的卷积核
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_out;
image = imread("C://eg//jr.jpg");
if (image.empty()) {
printf("could not load image...\n");
return -1;
}
imshow("image", image);
int ksize = 5;
Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (ksize * ksize);
filter2D(image, image_out, -1, kernel, Point(-1, -1));
imshow("image_out", image_out);
waitKey(0);
return 0;
}
卷积边界问题:图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3x3滤波时候有1个像素的边缘没有被处理,5x5滤波的时候有2个像素的边缘没有被处理。
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_out0, image_out1, image_out2, image_out3;
image = imread("C://eg//jr.jpg");
if (image.empty()) {
printf("could not load image...\n");
return -1;
}
imshow("image", image);
copyMakeBorder(image, image_out0, 9, 9, 9, 9, BORDER_CONSTANT, Scalar(123, 123, 123));
imshow("image_out", image_out0);
copyMakeBorder(image, image_out1, 9, 9, 9, 9, BORDER_DEFAULT, Scalar(123, 123, 123));
imshow("image_out1", image_out1);
copyMakeBorder(image, image_out2, 9, 9, 9, 9, BORDER_WRAP, Scalar(123, 123, 123));
imshow("image_out2", image_out2);
copyMakeBorder(image, image_out3, 9, 9, 9, 9, BORDER_REPLICATE, Scalar(123, 123, 123));
imshow("image_out3", image_out3);
waitKey(0);
return 0;
}
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_addborder, image_out;
image = imread("C://eg//jr.jpg");
if (image.empty()) {
return -1;
}
imshow("image", image);
int ksize = 3;
copyMakeBorder(image, image_addborder, ksize / 2, ksize / 2, ksize / 2, ksize / 2, BORDER_REPLICATE, Scalar(123, 123, 123));
imshow("image_addborder", image_addborder);
Mat kernel = Mat::ones(Size(ksize, ksize) , CV_32F) / (float)(ksize * ksize);
filter2D(image_addborder, image_out, -1, kernel, Point(-1, -1), (0, 0), BORDER_REPLICATE);
imshow("image_out", image_out);
waitKey(0);
return 0;
}
如何捕捉/提取边缘?
对图像求导数,delta = f(x) – f(x-1), delta越大,说明像素在X方向变化越大,边缘信号越强。
注意:相比sobel算子,scharr算子能计算出更小的梯度变化
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_GaussianBlur, image_gray;
image = imread("C://eg//2.jpg");
if (image.empty()) {
return -1;
}
imshow("image", image);
GaussianBlur(image, image_GaussianBlur, Size(3, 3), 0, 0);
cvtColor(image_GaussianBlur, image_gray, CV_BGR2GRAY);
imshow("image_gray", image_gray);
//sobel
Mat image_sobel_x, image_sobel_y;
Sobel(image_gray, image_sobel_x, CV_16S, 1, 0, 3);
Sobel(image_gray, image_sobel_y, CV_16S, 0, 1, 3);
convertScaleAbs(image_sobel_x, image_sobel_x);
convertScaleAbs(image_sobel_y, image_sobel_y);
Mat image_sobel = Mat(image_sobel_x.size(), image_sobel_x.type());
int width = image_sobel_x.cols;
int height = image_sobel_x.rows;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int pixel_x = image_sobel_x.at <uchar>(row, col);
int pixel_y = image_sobel_y.at <uchar>(row, col);
image_sobel.at<uchar>(row, col) = saturate_cast<uchar>(pixel_x + pixel_y);
}
}
imshow("image_sobel", image_sobel);
//scharr
Mat image_scharr_x, image_scharr_y;
Scharr(image_gray, image_scharr_x, CV_16S, 1, 0, 3);
Scharr(image_gray, image_scharr_y, CV_16S, 0, 1, 3);
convertScaleAbs(image_scharr_x, image_scharr_x);
convertScaleAbs(image_scharr_y, image_scharr_y);
Mat image_scharr = Mat(image_scharr_x.size(), image_scharr_x.type());
int width1 = image_scharr_x.cols;
int height1 = image_scharr_x.rows;
for (int row = 0; row < height1; row++) {
for (int col = 0; col < width1; col++) {
int pixel_x = image_scharr_x.at<uchar>(row, col);
int pixel_y = image_scharr_y.at<uchar>(row, col);
image_scharr.at<uchar>(row, col) = saturate_cast<uchar>(pixel_x + pixel_y);
}
}
imshow("image_scharr", image_scharr);
waitKey(0);
return 0;
}
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_GaussianBlur, image_gray;
image = imread("C://eg//2.jpg");
if (image.empty()) {
return -1;
}
imshow("image", image);
GaussianBlur(image, image_GaussianBlur, Size(3, 3), 0, 0);
cvtColor(image_GaussianBlur, image_gray, CV_BGR2GRAY);
imshow("image_gray", image_gray);
Mat image_laplance;
Laplacian(image_gray, image_laplance, CV_16S, 3);
convertScaleAbs(image_laplance, image_laplance);
//threshold(image_laplance, image_laplance, 0, 255, THRESH_OTSU | THRESH_BINARY);
imshow("image_laplance", image_laplance);
waitKey(0);
return 0;
}
Canny算子被很多人推崇为当今最优的边缘检测的算法。
高斯滤波消除噪声;
将图像转换为灰度图;
用双阈值算法检测和连接边缘:T1, T2为阈值,凡是高于T2的都保留,凡是小于T1都丢弃,从高于T2的像素出发,凡是大于T1而且相互连接的,都保留。最终得到一个输出二值图像。
推荐的高低阈值比值为 T2: T1 = 3:1/2:1其中T2为高阈值,T1为低阈值。
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image, image_GaussianBlur, image_gray;
int value_min = 50;
float value_mul = 2.0;
int value_max = 255;
image = imread("C://eg//3.jpg");
if (image.empty()) {
return -1;
}
imshow("image", image);
GaussianBlur(image, image_GaussianBlur, Size(3, 3), 0, 0);
cvtColor(image_GaussianBlur, image_gray, CV_BGR2GRAY);
imshow("image_gray", image_gray);
Mat image_canny;
Canny(image_gray, image_canny, value_min, value_min * value_mul, 3, false);
imshow("image_canny", image_canny);
waitKey(0);
return 0;
}
在边缘检测已经完成的条件下,霍夫直线变换用来做直线检测,主要是平面空间到极坐标空间转换。
直线从直角坐标系转换到极坐标系:r = xcosθ + ysinθ
可以看到,根据以上公式,同时在图像空间处于一条直线的 n个点 将他们的坐标代入,会得到n个在参数空间中关于r和θ的函数,表示为图像就是n条曲线,n条曲线总会相交于同一点。图像空间中共线的点在参数空间中曲线相交于一点。
也就是说,如果一幅图像空间(x-y)中的像素构成一条直线,那么这些像素坐标值(x, y)在参数空间对应的曲线一定相交于一个点,所以我们只需要将图像空间(x-y)中的所有像素点变换成参数空间(r-θ)的曲线,并在参数空间检测曲线的交点,得到交点(r,θ)就可以确定直线了。
霍夫变换的直线检测是把图像空间中的直线变换到参数空间中的点,通过统计特性来解决检测问题。为何要用?以及如何使用统计特性?因为确定一点时,找到一个能够与这点构成直线的点有无限种可能。在现实应用里,我们就要限定(r-θ)的密度,也就是要将θ离散化。
霍夫变换检测直线的原理:
//标准霍夫变换、多尺度霍夫变换
CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double srn = 0, double stn = 0,
double min_theta = 0, double max_theta = CV_PI );
//InputArray image:输入图像,必须是8位单通道图像。
//OutputArray lines:检测到的线条参数集合。
//double rho:以像素为单位的距离步长。
//double theta:以弧度为单位的角度步长。
//int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。
//double srn:默认值为0,用于在多尺度霍夫变换中作为参数rho的除数,rho=rho/srn。
//double stn:默认值为0,用于在多尺度霍夫变换中作为参数theta的除数,theta=theta/stn。
//如果srn和stn同时为0,就表示HoughLines函数执行标准霍夫变换,否则就是执行多尺度霍夫变换。
//渐进概率式霍夫变换
CV_EXPORTS_W void HoughLinesP( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double minLineLength = 0, double maxLineGap = 0 );
//InputArray image:输入图像,必须是8位单通道图像。
//OutputArray lines:检测到的线条参数集合。
//double rho:直线搜索时的距离步长,以像素为单位。
//double theta:直线搜索时的角度步长,以弧度为单位。
//int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。
//double minLineLength:默认值为0,表示最小线段长度阈值(像素)。
//double maxLineGap:线段上最近两点之间的阈值.默认值为0,表示直线断裂的最大间隔距离阈值。即如果有两条线段是在一条直线上,但它们之间有间隙,那么如果这个间隔距离小于该值,则被认为是一条线段,否则认为是两条线段。
#include
#include
using namespace cv;
using namespace std;
int thresh_b = 128;
int main(int argc, char** argv) {
Mat image, image_gray,image_GaussianBlur, image_binary, image_canny, ;
image = imread("C://eg//line1.png");
if (image.empty()) {
return -1;
}
namedWindow("image",CV_WINDOW_AUTOSIZE);
imshow("image", image);
cvtColor(image, image_gray, CV_BGRA2GRAY);
threshold(image_gray, image_binary, thresh_b, 255, THRESH_BINARY);
GaussianBlur(image_binary, image_GaussianBlur, Size(3, 3), 0);
Canny(image_GaussianBlur, image_canny, 50, 100);
vector<Vec2f> lines;
HoughLines(image_canny, lines, 1, CV_PI / 180, 150, 0, 0);
for (size_t i = 0; i < lines.size(); i++) {
float rho = lines[i][0];
float theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a * rho, y0 = b * rho;
pt1.x = cvRound(x0 + 1000 * (-b)); //cvRound四舍五入
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
line(image, pt1, pt2, Scalar(0, 0, 255), 1, CV_AA);
}
imshow("image_out0", image);
waitKey(0);
return 0;
}
#include
#include
using namespace cv;
using namespace std;
int thresh_b = 128;
int main(int argc, char** argv) {
Mat image, image_gray,image_GaussianBlur, image_binary, image_canny, ;
image = imread("C://eg//line1.png");
if (image.empty()) {
return -1;
}
namedWindow("image",CV_WINDOW_AUTOSIZE);
imshow("image", image);
cvtColor(image, image_gray, CV_BGRA2GRAY);
threshold(image_gray, image_binary, thresh_b, 255, THRESH_BINARY);
GaussianBlur(image_binary, image_GaussianBlur, Size(3, 3), 0);
Canny(image_GaussianBlur, image_canny, 50, 100);
vector<Vec4f> plines;
HoughLinesP(image_canny, plines, 1, CV_PI / 180.0, 10, 0, 10);
Scalar color = Scalar(0, 0, 255);
for (size_t i = 0; i < plines.size(); i++) {
Vec4f hline = plines[i];
line(image, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA);
}
imshow("image_output", image);
waitKey(0);
return 0;
}
HoughCircles(
InputArray image, // 输入图像 ,必须是8位的单通道灰度图像
OutputArray circles, // 输出结果,发现的圆信息圆心坐标,圆半径
Int method, // 方法 - HOUGH_GRADIENT
Double dp, // dp = 1;
Double mindist, // 10 最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8
Double param1, // canny edge detection low threshold
Double param2, // 中心点累加器阈值 – 候选圆心
Int minradius, // 最小半径
Int maxradius // 最大半径
)
#include
#include
using namespace cv;
using namespace std;
int thresh_b = 128;
int main(int argc, char** argv) {
Mat image, image_gray, image_GaussianBlur, image_binary, image_canny;
image = imread("C://eg//circle.png");
if (image.empty()) {
return -1;
}
namedWindow("image",CV_WINDOW_AUTOSIZE);
imshow("image", image);
cvtColor(image, image_gray, CV_BGRA2GRAY);
threshold(image_gray, image_binary, thresh_b, 255, THRESH_BINARY);
GaussianBlur(image_binary, image_GaussianBlur, Size(5, 5), 0);
vector<Vec3f> circles;
HoughCircles(image_GaussianBlur, circles, HOUGH_GRADIENT, 1, 10, 100, 32, 70, 125);
for (size_t i = 0; i < circles.size(); i++) {
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int r = cvRound(circles[i][2]);
circle(image, center, r, Scalar(0, 0, 255), 8, LINE_4, 0);
}
imshow("image_out", image);
waitKey(0);
return 0;
}
相关api:
remap(
InputArray src, //输入图像
OutputArray dst, //输出图像
InputArray map1, //x映射表 CV_32FC1/CV_32FC2
InputArray map2, //y映射表
Int interpolation, //插值方式
Int borderMode, //边界模式
Const Scalar borderValue //边界Color
)
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat image, image_out, map_x, map_y;
image = imread("C://eg//1.png");
image_out.create(image.size(), image.type());
map_x.create(image.size(), CV_32FC1);
map_y.create(image.size(), CV_32FC1);
for (size_t j = 0; j < image.rows; j++)
{
for (size_t i = 0; i < image.cols; i++)
{
//旋转180度
map_x.at<float>(j, i) = static_cast<float>(image.cols - i);
map_y.at<float>(j, i) = static_cast<float>(image.rows - j);
//缩小
// if (i > (image.cols * 0.25) && i <= (image.cols * 0.75) && j > (image.rows * 0.25) && j <= (image.rows * 0.75)) {
// map_x.at(j, i) = 2 * (i - (image.cols * 0.25));
// map_y.at(j, i) = 2 * (j - (image.rows * 0.25));
// }
// else {
// map_x.at(j, i) = 0;
// map_y.at(j, i) = 0;
// }
}
}
remap(image, image_out, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
imshow("image", image);
imshow("image_out", image_out);
waitKey(0);
return 0;
}
灰度直方图是表示不同灰度出现频率的图像,为柱状图;是图像的统计学特征。
目的是提高图像的对比度,拉伸图像的灰度值范围。
相关api:
equalizeHist(
InputArray src,//输入图像,必须是8-bit的单通道图像
OutputArray dst// 输出结果
)
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat image, image_gray, image_out;
image = imread("C://eg//4.jpg");
if (image.empty()) {
return -1;
}
cvtColor(image, image_gray, CV_BGR2GRAY);
equalizeHist(image_gray, image_out);
imshow("image", image);
imshow("image_gray", image_gray);
imshow("image_out", image_out);
waitKey(0);
return 0;
}
split(// 把多通道图像分为多个单通道图像
const Mat &src, //输入图像
Mat* mvbegin)// 输出的通道图像数组
calcHist(
const Mat* images,//输入图像指针
int images,// 图像数目
const int* channels,// 通道数
InputArray mask,// 输入mask,可选,不用
OutputArray hist,//输出的直方图数据
int dims,// 维数
const int* histsize,// 直方图级数
const float* ranges,// 值域范围
bool uniform,// true by default
bool accumulate// false by defaut
)