openCV入门
测试环境 xcode8
macOS 10.12.5
openCV 2.4
读取/显示图像
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, const char * argv[]) {
showImage2();
return 0;
}
void showImage(){
//老版本cvLoadImage
//新版imread
//为图形分配空间
IplImage *image = cvLoadImage("/Users/tyrad/Desktop/openCV/aaa.png");
namedWindow("example");
cvShowImage("example", image);
cvWaitKey(0);
cvReleaseImage(&image);
cvDestroyWindow("example");
}
void showImage2(){
cv::Mat image ;
std::cout << "image.rows = " << image.rows << " \nimage.cols=" <<image.cols << std::endl;
image = cv::imread("/Users/tyrad/Desktop/openCV/aaa.png");
std::cout << "image.rows = " << image.rows << " \nimage.cols=" <<image.cols << std::endl;
if (image.empty()) {
printf("未创建图像");
//未创建图像
return;
}
cv::namedWindow("window");
cv:imshow("window", image);
cv::waitKey(0);
}
| 解释 |
---|
cv::Mat image ; | 创建了空图像,尺寸为0,0: |
cv::imread("path/to/image"); | 读取图片 |
image = cv::imread("path/to/image", CV_LOAD_IMAGE_GRAYSCALE); | 读取文件并转换为灰度图像 |
image = cv::imread("path/to/image", CV_LOAD_IMAGE_GRAYSCALE); | 读取文件并转换为三通道彩色图像 |
绘图
//绘图功能
void drawRect(){
cv::Mat image ;
image = cv::imread("/Users/tyrad/Desktop/lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
if (image.empty()) {
printf("未创建图像");
return;
}
cv::circle(image,
cv::Point(image.cols/2,image.rows/2),//中心点坐标
65, //半径
0, //颜色(黑色)
3); //厚度
cv::putText(image,
"this is a pic",
cv::Point(40,200),
cv::FONT_HERSHEY_PLAIN,
2.0,
255,
2);
//其他还有circle/ellipse/line
cv::namedWindow("window");
cv:imshow("window", image);
cv::waitKey(0);
}
认识cv::Mat
获取图像大小: cv::Size
新建一个240行320列的新图像:
cv::Mat image1(240,320,CV_8U,100);
CV_8U:表示每个像素对应1字节(u表示无符号)
CV_8UC3:表示彩色图像,三通道类型
创建一个灰色图像:
cv::namedWindow("image");
cv::Mat image1(240,320,CV_8U,100);
cv::imshow("image", image1);
cv::waitKey(0);
创建一个红色图像,cv::Scalar
用于在调用函数时传递像素值:
cv::namedWindow("image");
cv::Mat image2(240,320,CV_8UC3,cv::Scalar(0,0,255));
cv::imshow("image", image2);
cv::waitKey(0);
create
方法用于重新分配图像的数据块:
image1.create(200,200,CV_8U)
定义兴趣区域,类似添加水印logo
void addLogo(){
cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
cv::Mat logo = cv::imread("/Users/tyrad/Desktop/juhua.png");
cv::Mat imageROI(image, cv::Rect(image.cols-logo.cols,image.rows-logo.rows,logo.cols,logo.rows));
logo.copyTo(imageROI);
cv::imshow("image", image);
cv::waitKey(0);
}
图像的掩码:
void addLogo(){
cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
cv::Mat logo = cv::imread("/Users/tyrad/Desktop/juhua.png");
cv::Mat imageROI(image, cv::Rect(image.cols-logo.cols,image.rows-logo.rows,logo.cols,logo.rows));
cv::Mat mask(logo);
logo.copyTo(imageROI,logo);
cv::imshow("image", image);
cv::waitKey(0);
}
访问像素值
使用cv::Mat的at(int y,int x)方法可以访问图像矩阵的元素。
因为cv::Mat可以接受任何类型的元素,因此需要我们制定返回值的预期类型(保证指定的类型和矩阵内的类型是一致的)。
彩色图像每个像素对应三个部分:红、绿、蓝。因为cv::Mat类返回一个向量,向量包含三个八位的值。
void salt(cv::Mat image , int n ){
int i, j;
for (int k = 0; k < n; k ++) {
j = std::rand()%image.cols;
i = std::rand()%image.rows;
if (image.type() == CV_8UC1) {
image.at<uchar>(j,i) = 255;
}else if(image.type() == CV_8UC3){
image.at<cv::Vec3b>(i,j)[0] = 255;
image.at<cv::Vec3b>(i,j)[1] = 255;
image.at<cv::Vec3b>(i,j)[2] = 255;
}
}
}
void pixCntrol(){
cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
salt(image, 500);
cv::imshow("image", image);
cv::waitKey(0);
}
指针扫描像素
写一个减色算法: 减少图片颜色的数量,利用整数除法的特性,将一区块的色彩调整到这个区块的中间值。只需要遍历所有的像素值就可以了。
ptr
方法可以直接访问图像中的一个行的地址。
void myColorReduce(cv::Mat image ,int div=64){
//行
int nl = image.rows;
//每行的元素数量
int nc = image.cols * image.channels();
//遍历每行
for (int j = 0; j < nl; j++) {
//获取行j的地址
uchar *data = image.ptr<uchar>(j);
//遍历每列
for (int i = 0 ; i < nc; i++) {
data[i] = data[i]/div*div + div/2;
}
}
}
void colorReduce(){
cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
myColorReduce(image);
cv::imshow("image", image);
cv::waitKey(0);
}
效果
高效的图像扫描循环
简单的图像运算(以图像组合为例)
需要用到addWeighted
方法:
cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
cv::Mat image2 = cv::imread("/Users/tyrad/Desktop/256.jpg");
if (image.empty()) {
std::cout<< "Error image"<<std::endl;
}
if (image2.empty()) {
std::cout<< "Error image2"<<std::endl;
}
Mat dst;
cv::addWeighted(
image,//图片1
0.7, //alpha 图片1的融合比例
image2,//图片2
0.9, //图片2的融合比例
0, //偏差
dst //输出图片
);
cv::imshow("image", dst);
cv::waitKey(0);
需要注意的是需要保证两张图片的大小和类型相同。
修改像素位置
使用remap函数,首先定义处理中使用的映射参数,然后把映射参数引用到输入图像。
void wave(const cv::Mat &image , cv::Mat &result ){
//映射参数,分别反映x,y坐标的变化
cv::Mat srcX(image.rows, image.cols, CV_32F);
cv::Mat srcY(image.rows, image.cols, CV_32F);
//创建映射参数
for (int i=0; i<image.rows; i++) {
for (int j=0; j<image.cols; j++) {
//调整位置关系
srcX.at<float>(i,j) = j; //列位置不变
srcY.at<float>(i,j) = i + 5 * sin(j/10.0); //行位置根据正弦函数移动
}
}
cv::remap(image, result, srcX, srcY, cv::INTER_LINEAR);
}
void myWave(){
cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
cv::Mat result;
wave(image, result);
cv::imshow("image", result);
cv::waitKey(0);
}
可以看到图片被变形了:
待续...