关于Point类特殊要说的:
typedef Point2i Point1;
typedef Point_<int> Pointi;
typedef Point2f Point2;
typedef Point_<float> Pointf;
这些类型根据其基于的基本数据类型,之间都有等价关系。
Scalar:其本质就是4个元素的数组,在opencv中大量被用于存储像素值,例如RGB元素值。
切记:用几个参数,写几个,别没事找事!
Size:size(int width,int height)构建的时候给定宽高,再没别的用法
Rect:x,y左上点坐标,width,height矩形宽高,area返回面积,contain(Point)判断点是否在矩形里,inSide(Rect)判断矩阵是否在矩阵里,tl()返回左上点左边,br()返回右下角坐标
色彩空间转换:void cvtColor(InputArray src,OutputArray dst,int code,int dstCn = 0);
第三个参数:色彩空间转换的表示符,理解着用,例如BGR转HSV COLOR_BGR2HSV,这个2就是to。
第四个参数:默认值0,表示目标图像获取原图像的通道数。基本上用不到,不输参数,默认值就好了。
基本图像绘制:line直线,ellipse椭圆,rectangle矩形绘制函数,circle圆,fillPoly函数(填充多边形)
具体参数看vs的提示和下面示例测试程序
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#define WINDOW_NAME1 "【绘制图1】" //为窗口标题定义的宏
#define WINDOW_NAME2 "【绘制图2】" //为窗口标题定义的宏
#define WINDOW_WIDTH 600//定义窗口大小的宏
using namespace cv;
void DrawEllipse(Mat img, double angle);//绘制椭圆
void DrawFilledCircle(Mat img, Point center);//绘制圆
void DrawPolygon(Mat img);//绘制多边形
void DrawLine(Mat img, Point start, Point end);//绘制线段
int main(int argc, char** argv) {
Mat src = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
double angle = 0.0;
DrawEllipse(src, angle);
DrawLine(src, Point(20, 20), Point(100, 100));
DrawFilledCircle(src, Point(150, 150));
DrawPolygon(src);
imshow("画布", src);
waitKey(0);
}
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,//拓展的弧度0-360
360,
Scalar(255, 129, 0),//通道颜色,蓝色
thickness,//线宽
lineType);//线型
}
void DrawLine(Mat img, Point start, Point end)
{
int thickness = 2;
int lineType = 8;
line(img,//输出图像
start,//初始点
end,//结束点
Scalar(255, 129, 0),//通道颜色。蓝色
thickness,//线宽
lineType);//线型
}
void DrawFilledCircle(Mat img, Point center)
{
//线宽为-1,则会用通道颜色填充圆
//正整值,则是正常的空心圆
int thickness = -1;
int lineType = 8;
circle(img,
center,//圆心
WINDOW_WIDTH / 32,//半径
Scalar(255, 129, 0),//通道颜色,蓝色
thickness,//线宽
lineType);//线型
}
void DrawPolygon(Mat img)
{
int lineType = 8;
//创建一些点
Point rookPoints[1][20];
rookPoints[0][0] = Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
rookPoints[0][1] = Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
rookPoints[0][2] = Point(3 * WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
rookPoints[0][3] = Point(11 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoints[0][4] = Point(19 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoints[0][5] = Point(3 * WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
rookPoints[0][6] = Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoints[0][7] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][8] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][9] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][10] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][11] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][12] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][13] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][14] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
rookPoints[0][17] = Point(13 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoints[0][18] = Point(5 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
const Point* ppt[1] = { rookPoints[0] };
int npt[] = { 20 };
fillPoly(img,
ppt,
npt,
1,
Scalar(255, 255, 255),
lineType);
}
图像在内存中的存储方式:
灰度图像:
多通道图像:
Opencv中的子列存储顺序是反过来的,BGR而不是RGB(了解)
色彩空间缩减:
由于多通道图像,像素值太太太多,决定将现有色彩空间除以某个输入值,以获得较少的颜色数。
0~9取0,10~19取10,类似这样。
LUT函数:批量进行图像元素查找,扫描与操作图像。
函数原型:void LUT(InputArray src, InputArray lut, OutputArray dst);
一参:输入图像(单,多通道都可)
二参:查找表(与输入图像通道数保持一致)
三参:输出图像
具体使用方法:
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
int main(int argc, char** argv) {
uchar lutData[256];
for (int i = 0; i < 256; i++) {
if (i <= 100) {
lutData[i] = 0;
}
if (i > 100 && i <= 200) {
lutData[i] = 100;
}
if (i > 200) {
lutData[i] = 255;
}
}
Mat lut(1, 256, CV_8UC1, lutData);//1行,256列,像素类型为8字节无符号char类型1通道,通道已定义的uchar类型数组
Mat src = imread("E://hashiqi.jpg", IMREAD_GRAYSCALE);
Mat dst;
LUT(src, lut, dst);
imshow("///", src);
imshow("------", dst);
waitKey(0);
}
其中测试程序的代码不难理解,这个lut查找表的机制原理,暂时没搞懂,之后再说
计时函数(常用):getTickCount():返回某个事件以来走过的时钟周期数
getTickFrequency():返回cpu一秒钟所走过的时钟周期数
这样用
访问图片像素的三种方法:
1.指针访问:
mat.ptr<type>(row)[col],没啥好说的,用就完事了
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
//参数准备
outputImage = inputImage.clone(); //拷贝实参到临时变量
int rowNumber = outputImage.rows; //行数
int colNumber = outputImage.cols * outputImage.channels(); //列数 x 通道数=每一行元素的个数
//双重循环,遍历所有的像素值
for (int i = 0; i < rowNumber; i++) //行循环
{
uchar* data = outputImage.ptr<uchar>(i); //获取第i行的首地址
for (int j = 0; j < colNumber; j++) //列循环
{
// ---------【开始处理每个像素】-------------
/*data[j] = data[j] / div * div + div / 2;*/
std::cout << (int)data[j];
// ----------【处理结束】---------------------
} //行处理结束
}
}
2.迭代器iterator:
获取图像矩阵的begin和end,然后解指针访问像素内容,遍历跟寻常迭代器一致
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
//参数准备
outputImage = inputImage.clone(); //拷贝实参到临时变量
//获取迭代器
Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器
Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //终止位置的迭代器
//存取彩色图像像素
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;
std::cout << (*it)[0];
//多通道的话就再自行发挥语句
// ------------------------【处理结束】----------------------------
}
}
3.动态地址计算:
没啥说的,at<像素类型>[通道下标],就可
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++)
{
// ------------------------【开始处理每个像素】--------------------
std::cout << outputImage.at<Vec3b>(i, j)[0];
std::cout << outputImage.at<Vec3b>(i, j)[1];
std::cout << outputImage.at<Vec3b>(i, j)[2];
//outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div * div + div / 2; //蓝色通道
//outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div * div + div / 2; //绿色通道
//outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div * div + div / 2; //红是通道
// -------------------------【处理结束】----------------------------
} // 行处理结束
}
}
分离颜色通道函数split,多通道图像混合merge
void split(const Mat &src,Mat *mvbegin)
void split(InputArray m,OutputArrayOfArrays mv);
void merge(const Mat* mv,size_tcount,OutputArray dst)
void merge(InputArrayofArrays mv,OutputArray dst)
split 和 merge 是互逆操作,用法,参数均类似
at方法是取的引用,修改值,原值也会变动
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat src = imread("E://hashiqi.jpg");
Mat imageROI,image2_ROI,image3_ROI;
Mat dst;
vector<Mat> channels;
split(src, channels);
imageROI = channels.at(0);//多通道图像分离通道号,像素值从0~255,理论上来说应该是灰度图像
image2_ROI = channels.at(1);
image3_ROI = channels.at(2);
merge(channels, dst);//合并三通道
imshow("merge", dst);
imshow("split", imageROI);
waitKey(0);
}
点操作(仅通过输入的像素值变化,最多加上些全局信息):亮度调整,对比度调整,颜色校正,变换
f(x):原图像素,g(x):输出图像素,a>0称增益,控制图像对比度,b称偏置,控制图像亮度
下面是使用方法的测试程序:
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
int ContrastValue; //对比度值
int BrightValue; //亮度值
Mat src, dst;//输入图像,输出图像
static void ContrastAndBright(int, void*);
int main(int argc, char** argv) {
system("color 2F");
src = imread("E://hashiqi.jpg");
dst = Mat::zeros(src.size(), src.type());//初始化dst尺寸
namedWindow("【原始图窗口】",1);
ContrastValue = 80;
BrightValue = 80;
createTrackbar("对比度", "【原始图窗口】", &ContrastValue, 300,ContrastAndBright);//ContrastAndBright实现修改功能的回调函数
createTrackbar("亮度", "【原始图窗口】", &BrightValue, 200, ContrastAndBright);
ContrastAndBright(ContrastValue, 0);
ContrastAndBright(BrightValue, 0);
waitKey(0);
}
static void ContrastAndBright(int, void*) {
namedWindow("【原始图窗口】", 1);
for (int y = 0; y < src.rows; y++)
{
for (int x = 0; x < src.cols; x++)
{
for (int c = 0; c < 3; c++)
{
dst.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((ContrastValue * 0.01) * (src.at<Vec3b>(y, x)[c]) + BrightValue);
}
}
}
// 显示图像
imshow("【原始图窗口】", src);
imshow("【效果图窗口】", dst);
}