Opencv备忘之Load Image,Operator Pixels
从《OpenCV 2 Computer Vision Application Programming Cookbook》中整理
刚好现在有时间,将Opencv的基础操作,整理为code,偶尔翻翻,避免遗忘!
1.Loading, displaying, and saving images
#include <opencv2\opencv.hpp>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
int main()
{
//opencv现由Intel负责维护,同时其将cv::Mat读入图像的方式同IPL(Intel图像处理库)综合;
//这也是随后的一种趋势,需要使用IplImage类部分代替cv类读取图像,或者进行图像处理!
IplImage* iplImage = cvLoadImage("road.jpg");
cv::Mat image(iplImage, false);
//另一种直接使用cv::Mat读入图像的方式;
//cv::Mat image;
//image = cv::imread("road.jpg");
//检查图片是否读取成功!
if (!image.data)
{
std::cout << "ERROR:no image has been created." << std::endl;
}
//读取图像的宽高
std::cout << "size: " << image.size().height << ","
<< image.size().width << std::endl;
//先定义一个窗口,然后在该窗口显示图片!
cv::namedWindow("Original Image");//define the window
cv::imshow("Original Image",image);//show the image
//flip这个function是做什么功能?
//reverses the order of rows,columns or both in the matrix
cv::Mat result;
cv::flip(image,result,1);//positive for horizontal
//0 for vertical, negative for both ?
cv::Mat resultScd, resultTrd;
//仅复制了图片指针,内存并没有copy
resultScd = result;//the two pointers have the same memory
//新建了一块内存
result.copyTo(resultTrd);//new memory block;
//可以使用多个窗口显示图片,但前提是需要定义不同的窗口名称!
cv::namedWindow("Output Image");//define the window
cv::imshow("Output Image", result);//show the image
//因为控制台窗口在程序执行结束后会关掉所有的窗口,所以需有添加此方法,用于观察图片显示!
cv::waitKey(0);
//将处理后的图片,保存为bmp
cv::imwrite("flipOutImage.bmp",result);
//若使用IPL则需要在使用结束后释放掉内存!
cvReleaseImage(&iplImage);
return 0;
}
2.Accessing pixel values
#include<opencv2\opencv.hpp>
#include<iostream>
#include<string>
using namespace std;
using namespace cv;
void salt(cv::Mat &image,int n);
int main(void)
{
//open image as new to old model
IplImage* IPLmg = cvLoadImage("Tulips.jpg");
cv::Mat cvImg(IPLmg, false);
//add noise to the image
salt(cvImg, 3000);
//display image that has noise
cv::namedWindow("noise window");
cv::imshow("noise window",cvImg);
cv::waitKey(0);
//write the noise image
cv::imwrite("noise.bmp",cvImg);
return 0;
}
//function: add salt-and-pepper noise
//向图像中添加椒盐噪声
//输入Mat image,及添加噪声的数目;
void salt(cv::Mat &image, int n)
{
for (int k = 0; k < n; ++k)
{
//rand() is the MFC random number generator
//try qrand() with Qt
int i = rand() % image.cols;
int j = rand() % image.rows;
if (image.channels() == 1)//gray-level image
{
image.at<uchar>(j, i) = 255;
}
else if (image.channels()==3)//color image
{
//cv::Vec3b用于3通道的RGB图像的向量-vector
image.at<cv::Vec3b>(j, i)[0] = 255;
image.at<cv::Vec3b>(j, i)[1] = 255;
image.at<cv::Vec3b>(j, i)[2] = 255;
//类似的对于2-element或4-element vectors可以用cv::Vec2b或cv::Vec4b
// In this later case, the last letter is replaced by s for short, i for int,
// f for float, and d for double.All of these types are defined using the
//template class cv::Vec<T, N> where T is the type and N is the number of vector elements.
//使用方法.at或许会有些笨拙;所以在已知图像元素类型的情况下可以使用cv::Mat的子类cv::Mat_
//进行访问像素,此方法为操作符重载,即operator();
//cv::Mat_<uchar> ImgOperator=image;//ImgOperator refers to image
//ImgOperator()=0;//access to row 50 and column 100;
}
}
}
3.Scanning an image with pointers
#include <opencv2\opencv.hpp>
#include <iostream>
#include <string>
//#include assert.h
using namespace std;
using namespace cv;
void colorReduce(cv::Mat &image,cv::Mat &outImg, int div = 64);
int main(void)
{
IplImage* IPLImg = cvLoadImage("Tulips.jpg");
cv::Mat MatImg(IPLImg);
if (!MatImg.data)
{
std::cout << "Error: it's error!" << std::endl;
assert(0);
}
//clone原图像方法一
//cv::Mat ImgClone = MatImg.clone();
//颜色降级
//colorReduce(ImgClone);
cv::Mat ImgClone;
colorReduce(MatImg,ImgClone);
//display
cv::namedWindow("output Image");
cv::imshow("output Image", ImgClone);
waitKey(0);
//释放memory
cvReleaseImage(&IPLImg);
return 0;
}
//对8bit的RGB图像进行降级;由256×256×256将为16*16*16
void colorReduce(cv::Mat &image,cv::Mat &outImg, int div)
{
//克隆源图像方法二:
//克隆源图像,然后对克隆的图像进行处理!
outImg.create(image.rows,image.cols,image.type());
int RowsImg = outImg.rows;//number of lines
int ColsChannelsImg = (outImg.cols)*(outImg.channels());//total number of elements per line
//为了内存对齐便于快捷操作访问等原因,在某些地方会填充一些非图片像素;
//使用下述方法可检测像素是否连续,若是则可以将图像中所有的行连接,看作一维,进行操作;
if (image.isContinuous())
{
//then no padded pixels,没有填充
ColsChannelsImg = ColsChannelsImg*RowsImg;
RowsImg = 1;//it is now a 1D array
//若图像没有填充像素,则可以进行图像改造;
image.reshape(1,//new number of channels
image.cols*image.rows);//new number of rows
}
for (int j = 0; j < RowsImg; ++j)
{
//get the address of row j
//取得指向每行的指针.ptr
const uchar* data_in = image.ptr<uchar>(j);
uchar* data_out = outImg.ptr<uchar>(j);
for (int i = 0; i < ColsChannelsImg; ++i)
{
//process each pixel---------------
//16*16*16,如何能保证每个通道分别除以16??
//Opencv的三通道按BGR顺序排列
//方法1:直接除法
data_out[i] = data_in[i] / div*div + div / 2;
//方法2:取模运算
//data_out[i] = data_in[i] - data_in[i] % div + div / 2;
//方法3:位运算
//mask used to round the pixel value
//uchar mask = 0xFF << n;//e.g. for div=16, mask=0xF0;
//data_out[i] = (data_in[i] & mask) + div / 2;
//end of pixels process!
}
//下述写法可替代上述循环
//*data++=*data/div*div+div/2;
//当然还有一些低层次的访问方式
//uchar* data = image.data;//获取图像第一行的指针
//data += image.step;//next line,step方法提供一行中所有的字节数,包括填充像素
//采取下述方式也可以访问像素,但是在ROI问题时会造成一些ERROR,所以建议不要使用,知道即可!
//address of pixel at (j,i) that is &image.at(j,i);
//data = image.data + j*image.step + i*image.elemSize();
}//end process!
}
明天继续贴---