本节主要介绍OpenCV的core模块基础部分:
1. 访问像素的一些方法
2. 图像混合的方法
3. 图像对比度、亮度调整
4. 离散傅里叶变换
5. XML和YAML文件的读取写入
本章示例较多,示例列表:
1.用指针访问像素
2.用迭代器访问像素
3.用动态地址计算配合at访问像素
4.遍历图像像素的14种方法
5.初级图像混合
6.多通道图像混合
7.图像对比度、亮度值调整
8.离散傅里叶变换
9.XML和YAML文件写入
10.XML和YAML文件读取
源码:
#include
#include
using namespace cv;
using namespace std;
//颜色空间缩减函数
void colorReduce(Mat& inputImage, Mat& outputImage, int div);
int main()
{
Mat srcImage = imread("poster_tower.jpg");
imshow("【原始图像】", srcImage);
Mat dstImage;
dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());
double time0 = static_cast<double>(getTickCount());
colorReduce(srcImage, dstImage, 64);
time0 = ((double)getTickCount()-time0)/getTickFrequency();
cout << "此方法运行时间为:" << time0 << "秒" << endl;
imshow("【效果图】", dstImage);
waitKey(6000);
return 0;
}
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
outputImage = inputImage.clone();
int rowNumber = outputImage.rows;
int colNumber = outputImage.cols*outputImage.channels(); //每一行元素的个数
for (int i = 0; i < rowNumber; i++)
{
uchar* data = outputImage.ptr(i);
for (int j = 0; j < colNumber; j++)
{
data[j] = data[j] / div * div + div / 2;
}
}
}
素材:
效果图:
提示:
对应数学表达公式:I(x,y)= I(x,y)/div *div + div/2;
源码:
#include
#include
using namespace cv;
using namespace std;
void colorReduce(Mat& inputImage, Mat& outputImage, int div);
int main()
{
Mat srcImage = imread("poster_tower.jpg");
imshow("【原始图像】", srcImage);
Mat dstImage;
dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());
double time0 = static_cast<double>(getTickCount());
colorReduce(srcImage, dstImage, 64);
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "此方法运行时间为:" << time0 << "秒" << endl;
imshow("【效果图】", dstImage);
waitKey(6000);
return 0;
}
//缩减颜色空间
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
outputImage = inputImage.clone();
Mat_::iterator it = outputImage.begin(); //初始位置迭代器
Mat_::iterator itend = outputImage.end(); //终止位置迭代器
for (; it != itend; it++)
{
(*it)[0] = (*it)[0]/div * div + div / 2;
(*it)[1] = (*it)[1] / div * div + div / 2;
(*it)[2] = (*it)[2] / div * div + div / 2;
}
}
素材:
同1
效果图:
同1
提示:
对应数学表达公式:I(x,y)= I(x,y)/div *div + div/2;
源码:
#include
#include
using namespace cv;
using namespace std;
void colorReduce(Mat& inputImage, Mat& outputImage, int div);
int main()
{
Mat srcImage = imread("poster_tower.jpg");
imshow("【原始图像】", srcImage);
Mat dstImage;
dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());
double time0 = static_cast<double>(getTickCount());
colorReduce(srcImage, dstImage, 64);
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "此方法运行时间为:" << time0 << "秒" << endl;
imshow("【效果图】", dstImage);
waitKey(6000);
return 0;
}
//缩减颜色空间
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
outputImage = inputImage.clone();
int rowNumber = outputImage.rows;
int colNumber = outputImage.cols;
for (int i = 0; i < rowNumber; i++)
{
for (int j = 0; j < colNumber; j++)
{
outputImage.at(i, j)[0] = outputImage.at(i, j)[0] / div*div + div / 2; //蓝色通道
outputImage.at(i, j)[1] = outputImage.at(i, j)[1] / div*div + div / 2; //绿色通道
outputImage.at(i, j)[2] = outputImage.at(i, j)[2] / div*div + div / 2; //红色通道
}
}
}
素材:
同1
效果图:
同1
提示:
对应数学表达公式:I(x,y)= I(x,y)/div *div + div/2;
源码:
素材:
效果图:
提示:
源码:
#include
#include
using namespace cv;
using namespace std;
int main() {
Mat srcImage = imread("poster_dota.jpg");
imshow("【原图】", srcImage);
Mat logoImage = imread("poster_dota_logo.jpg");
Mat imageROI = srcImage(Rect(200, 250, logoImage.cols, logoImage.rows));
// Mat mask = Mat::zeros(logoImage.cols, logoImage.rows, CV_8UC1); //模板为单通道即可
Mat mask = Mat::ones(logoImage.cols, logoImage.rows, CV_8UC1);
logoImage.copyTo(imageROI, mask);
imshow("【利用ROI实现图像叠加示例窗口】", srcImage);
waitKey(6000);
return 0;
}
素材:
效果图:
提示:
copyto()函数,mask中元素值为非0时才copy
源码:
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat srcImage, logoImage;
vector channels;
//多通道混合——蓝色分量
Mat imageBlueChannel;
logoImage = imread("poster_dota_logo.jpg", IMREAD_GRAYSCALE); //载入灰度图
srcImage = imread("poster_dota.jpg");
split(srcImage, channels);
imageBlueChannel = channels.at(0); //注意此处是引用,修改其中一个另一个跟着变
addWeighted(imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows) ), 1.0, logoImage, 0.5, 0, imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows) ) );
merge(channels, srcImage);
imshow("【游戏原画+蓝色通道logo】", srcImage);
//多通道混合——绿色分量
Mat imageGreenChannel;
logoImage = imread("poster_dota_logo.jpg", IMREAD_GRAYSCALE); //载入灰度图
srcImage = imread("poster_dota.jpg");
split(srcImage, channels);
imageGreenChannel = channels.at(1); //注意此处是引用,修改其中一个另一个跟着变
addWeighted(imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows)));
merge(channels, srcImage);
imshow("【游戏原画+绿色通道logo】", srcImage);
//多通道混合——红色分量
Mat imageRedChannel;
logoImage = imread("poster_dota_logo.jpg", IMREAD_GRAYSCALE); //载入灰度图
srcImage = imread("poster_dota.jpg");
split(srcImage, channels);
imageRedChannel = channels.at(2); //注意此处是引用,修改其中一个另一个跟着变
addWeighted(imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows)));
merge(channels, srcImage);
imshow("【游戏原画+红色通道logo】", srcImage);
waitKey(16000);
return 0;
}
素材:
同5
效果图:
提示:
5中时直接将像素叠加上去,此例程中先用split()分离通道,然后在单通道上使用addWeighted()叠加,最后再使用merge()将各通道合并。
源码:
#include
#include
using namespace cv;
using namespace std;
static void on_ContrastAndBright(int, void* );
void useCLAHE();
int g_nContrastValue=80; //对比度值
int g_nBrightValue=80; //亮度值
Mat g_srcImage, g_dstImage;
int main()
{
g_srcImage = imread("poster_girl_1.jpg");
useCLAHE();
g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type());
namedWindow("【效果图窗口】");
createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, on_ContrastAndBright);
createTrackbar("亮度:", "【效果图窗口】", &g_nBrightValue, 200, on_ContrastAndBright);
on_ContrastAndBright(0, 0); //进入中断一次
while (char(waitKey(1)) != 'q');
return 0;
}
//此函数传入的参数k即为g_nContrastValue或g_nBrightValue
static void on_ContrastAndBright(int k, void*) {
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(y, x)[c] = saturate_cast((g_nContrastValue*0.01)*(g_srcImage.at(y,x)[c])+g_nBrightValue);
}
}
}
imshow("【原始窗口】", g_srcImage);
imshow("【效果图窗口】", g_dstImage);
}
//Contrast Limited Adaptive histgram equalization/CLAHE
void useCLAHE() {
Mat grayImage;
cvtColor(g_srcImage, grayImage, COLOR_RGB2GRAY);
imshow("【灰度图】", grayImage);
Ptr clahe = createCLAHE();
clahe->setClipLimit(4);
Mat dst;
clahe->apply(grayImage, dst);
imshow("【CLAHE效果图】", dst);
}
素材:
效果图:
提示:
改变图像亮度和对比度的数学表达式:I(x, y) = ContrastValue * I(x, y) + BrightValue;
本示例中在书本例程的基础上添加了CLAHE算法(Contrast Limited Adaptive Histogram Equalization,自适应直方图均衡化)在OpenCV中的使用。关于直方图均衡化和规定化这一块可参见:《数字图像处理与机器视觉》3.7节直方图均衡化 3.8节直方图规定化(匹配) 和《数字图像处理原理与实践》2.5节灰度均衡和2.6节直方图规定化。
源码:
#include
#include
using namespace std;
using namespace cv;
int main()
{
//读取灰度图
Mat srcImage = imread("poster_meal.jpg", IMREAD_GRAYSCALE);
imshow("【原始图像】", srcImage);
//将输入图像延扩到最佳尺寸,边界用0补充
int m = getOptimalDFTSize(srcImage.rows);
int n = getOptimalDFTSize(srcImage.cols);
Mat padded;
copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));
//为傅里叶变换的结果(实部和虚部)分配存储空间, 将planes数组组合合并成一个多通道的数组complesI
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
//进行就地离散傅里叶变换
dft(complexI, complexI);
//将复数转换为幅值,即=>log(1+sqrt(Re(DFT(I))^2+Im(DFT(I))^2))
split(complexI, planes);//planes[0]=Re(DFT(I)), planes[1]=Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]); //planes[0]=magnitude
Mat magnitudeImage = planes[0];
//进行对数尺度(logarithm scale)缩放
magnitudeImage += Scalar::all(1);
log(magnitudeImage, magnitudeImage); //求自然对数
//剪切(若有奇数行或奇数列,进行频谱裁剪)和重分布幅度图像限
magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols&-2, magnitudeImage.rows&-2));
int cx = magnitudeImage.cols / 2;//重新排列傅里叶图像中的象限,使得原点位于图像中心
int cy = magnitudeImage.rows / 2;
Mat q0(magnitudeImage, Rect(0, 0, cx, cy)); //ROI区域的左上
Mat q1(magnitudeImage, Rect(cx, 0, cx, cy)); //右上
Mat q2(magnitudeImage, Rect(0, cy, cx, cy)); //左下
Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); //右下
Mat tmp; //交换象限
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
//归一化,用0和1之间的浮点值将矩阵变换为可视的图像格式
normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);
//显示效果图
imshow("【频谱幅值】", magnitudeImage);
waitKey();
return 0;
}
源码:
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
FileStorage fs("yaml_test.yaml", FileStorage::WRITE); //初始化
//开始写入文件
fs << "frameCount" << 5;
time_t rawtime;
time(&rawtime);
fs << "CalibrationDate" << asctime(localtime(&rawtime));
Mat cameraMatrix = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
Mat distCoeffs = (Mat_<double>(5, 1)<<0.1, 0.01, -0.001, 0, 0);
fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;
fs << "features" << "[";
for (int i = 0; i < 3; i++)
{
int x = rand() % 640;
int y = rand() % 480;
uchar lbp = rand() % 256;
fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";
for (int j = 0; j < 8; j++)
fs << ((lbp>>j)&1);
fs << "]" << "}";
}
fs << "]";
fs.release();
cout << "文件读写完毕,请在工程目录下查看生成的文件" << endl;
waitKey();
return 0;
}
素材:
无
效果图:
无
提示:
YAML用的还是挺广泛的。
10 . XML和YAML文件读取
源码:
#include
#include
using namespace std;
using namespace cv;
int main()
{
FileStorage fs2("yaml_test.yaml", FileStorage::READ);
int frameCount = (int)fs2["frameCount"]; //第一种方法,对FileNode操作
string date;
fs2["calibrationDate"] >> date; //第二种方法,使用FileNode运算符>>
Mat cameraMatrix2, distCoeffs2;
fs2["cameraMatrix"] >> cameraMatrix2;
fs2["distCoeffs"] >> distCoeffs2;
cout << "frameCount: " << frameCount << endl
<< "calibration date: " << date << endl
<< "camera matrix: " << cameraMatrix2 << endl
<< "distortion coeffs: " << distCoeffs2 << endl;
FileNode features = fs2["features"];
FileNodeIterator it = features.begin(), it_end = features.end();
int idx = 0;
vector lbpval;
//使用FileNodeIterator遍历序列
for (; it != it_end; ++it, idx++)
{
cout << "feature #" << idx << ": ";
cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: (";
(*it)["lbp"] >> lbpval;
for (int i = 0; i < (int)lbpval.size(); i++)
cout << " " << (int)lbpval[i];
cout << ")" << endl;
}
fs2.release();
cout << "文件读取完毕,请输入任意键结束程序" << endl;
return 0;
}
素材:
无
效果图:
无
提示:
YAML用的还是挺广泛的。