常用工具,持续更新,记录点滴……
1. 计时
1.1 Windows下C语言计时,精确到毫秒
#include <windows.h>
#include <stdio.h>
int main()
{
/* 计时相关变量 */
LARGE_INTEGER Frequency;
LARGE_INTEGER Begin;
LARGE_INTEGER End;
double time = 0.0;
QueryPerformanceFrequency(&Frequency);
/* 开始计时 */
QueryPerformanceCounter(&Begin);
/* 此处添加待测试性能的模块 */
/* 结束计时 */
QueryPerformanceCounter(&End);
/* 乘以1000将秒转换为毫秒 */
time = (double)(End.QuadPart - Begin.QuadPart) / (double)Frequency.QuadPart * 1000.0;
printf("time cost: %.3f ms\n", time);
}
1.2 Linux下C语言计时,精确到毫秒
1.2.1 使用int gettimeofday(struct timeval*tv, struct timezone *tz);
实现
#include <stdio.h>
#include <sys/time.h>
int main(){
/* 计时相关变量 */
struct timeval tv_begin;
struct timeval tv_end;
/* 开始计时 */
gettimeofday(&tv_begin, NULL);
/* 此处添加待测试性能的模块 */
//sleep(30); //sleep30秒
/* 结束计时 */
gettimeofday(&tv_end, NULL);
/* 除以1000.f将微秒转换为毫秒 */
float time_cost = (tv_end.tv_sec * 1000000 + tv_end.tv_usec) / 1000.f -
(tv_begin.tv_sec * 1000000 + tv_begin.tv_usec) / 1000.f;
printf("millisecond:%.3f\n", time_cost);
}
1.2.2 使用clock_gettime
#include <time.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
struct timespec time_begin = {0, 0};
struct timespec time_end = {0, 0};
clock_gettime(CLOCK_MONOTONIC_RAW, &time_begin);
usleep(1000);
clock_gettime(CLOCK_MONOTONIC_RAW, &time_end);
float time_cost = ((time_end.tv_sec * 1'000'000'000 + time_end.tv_nsec) - (time_begin.tv_sec * 1'000'000'000 + time_begin.tv_nsec)) / 1000000;
printf("time_cost = %f ms\n", time_cost);
}
1.3 可跨平台,用boost计时方法:
#include <boost/date_time/posix_time/posix_time.hpp>
/* #include <boost/thread.hpp> */
#include <iostream>
int main()
{
boost::posix_time::ptime begin, end;
boost::posix_time::millisec_posix_time_system_config::time_duration_type time_elapse;
/* 开始计时,以微秒为单位 */
begin = boost::posix_time::microsec_clock::universal_time();
/* 待测试模块 */
/* boost::this_thread::sleep(boost::posix_time::millisec(100)); */
/* 结束计时 */
end = boost::posix_time::microsec_clock::universal_time();
/* 时间差,直接获取以毫秒为单位的时间差 */
time_elapse = end - begin;
int msec = time_elapse.total_milliseconds();
std::cout << "Time costs: " << msec << " ms." << std::endl;
system("pause");
}
1.4 其它计时方法
参考:https://www.runoob.com/w3cnote/c-time-func-summary.html
#include<stdio.h>
#include<time.h>
int main()
{
clock_t start_t,finish_t;
double total_t = 0;
int i = 0;
start_t = clock();
for(;i<100000;++i)
{
//do someting;
}
finish_t = clock();
total_t = (double)(finish_t - start_t) / CLOCKS_PER_SEC;//将时间转换为秒
printf("CPU 占用的总时间:%f\n", total_t);
return 0;
}
2. Windows下C语言逐行读取txt内容,每行分别存储。
#include <stdio.h>
#define TXT_ROW (60) /* txt文本行数 */
#define CHARACTER_LENGTH (200) /* 每行字符长度 */
/* 读取txt文本,并保存到数组 */
int main()
{
char imagelist[TXT_ROW][CHARACTER_LENGTH] = { "\0" }; /* 全部初始化为字符串终止符,便于使用 */
int index = 0;
FILE *pf = fopen("data.txt", "r");
char *per_line = (char*)malloc(CHARACTER_LENGTH * sizeof(char));
while (fgets(per_line, CHARACTER_LENGTH, pf) != NULL)
{
memcpy(imagelist[index], per_line, strlen(per_line) - 1);/* 减1是去掉最后的换行符'\n' */
++index;
}
free(per_line);
fclose(pf);
}
例如data.txt
存储的是如下内容:
D:\\imagelist\\1.jpg
D:\\imagelist\\2.jpg
D:\\imagelist\\3.jpg
D:\\imagelist\\4.jpg
D:\\imagelist\\5.jpg
那么imagelist
中每一行存储的就是图片的绝对路径。使用该方法在需要读取大量图片时很有用,从txt文档读取图片不会一下子吃掉很多内存。
3. 从彩色jpg图像中抽取rgb三个通道
注意OpenCV中颜色通道排序方式是BGR。
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
int main()
{
std::string filename = "test.jpg";
cv::Mat img = cv::imread(filename);
/* 分别为rgb三个通道申请内存保存从img中提取出的数据 */
uchar *buffer_r = new uchar[img.rows * img.cols];
uchar *buffer_g = new uchar[img.rows * img.cols];
uchar *buffer_b = new uchar[img.rows * img.cols];
int totalpixel = 0;
int count = 0;
/* 逐像素提取data */
for (int i = 0; i != img.rows; ++i)
{
for (int j = 0; j != img.cols; ++j)
{
buffer_b[count] = img.data[totalpixel];
buffer_g[count] = img.data[totalpixel + 1];
buffer_r[count] = img.data[totalpixel + 2];
++count;
totalpixel += 3;
}
}
/* 创建rgb三个单通道的图像 */
cv::Mat r = cv::Mat(img.rows, img.cols, CV_8UC1, buffer_r);
cv::Mat g = cv::Mat(img.rows, img.cols, CV_8UC1, buffer_g);
cv::Mat b = cv::Mat(img.rows, img.cols, CV_8UC1, buffer_b);
/* 分别显示rgb */
cv::imshow("r", r);
cv::imshow("g", g);
cv::imshow("b", b);
cv::imshow("img", img);
cv::waitKey(0);
/* 释放内存 */
delete[] buffer_r;
delete[] buffer_g;
delete[] buffer_b;
system("pause");
return 0;
}
或者用下面方法实现:
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <vector>
int main()
{
std::string filename = "test.jpg";
cv::Mat img = cv::imread(filename);
/* 逐像素提取data */
int totalpixel = 0;
const unsigned int image_byte_size = img.channels() * img.rows * img.cols;
std::vector<uchar> inputImageData;
inputImageData.resize(image_byte_size);
unsigned int countR_o = 0;
unsigned int countG_o = img.rows * img.cols;
unsigned int countB_o = 2 * img.rows * img.cols;
unsigned int step = 1;
for (int i = 0; i != img.rows; ++i)
{
for (int j = 0; j != img.cols; ++j)
{
inputImageData[countR_o] = img.data[totalpixel + 2];
inputImageData[countG_o] = img.data[totalpixel + 1];
inputImageData[countB_o] = img.data[totalpixel];
countR_o += step;
countG_o += step;
countB_o += step;
totalpixel += 3;
}
}
/* 创建rgb三个单通道的图像 */
cv::Mat r = cv::Mat(img.rows, img.cols, CV_8UC1, inputImageData.data());
cv::Mat g = cv::Mat(img.rows, img.cols, CV_8UC1, (inputImageData.data()+ img.rows * img.cols));
cv::Mat b = cv::Mat(img.rows, img.cols, CV_8UC1, (inputImageData.data() + 2 * img.rows * img.cols));
/* 分别显示rgb */
cv::imshow("r", r);
cv::imshow("g", g);
cv::imshow("b", b);
cv::imshow("img", img);
cv::waitKey(0);
system("pause");
return 0;
}
4. std::min\std::max语法错误error C2059
有时在使用std::min
和std::max
时会报错误 6 error C2059: 语法错误:“::”
,有可能是工程中包含了多个对min max
的定义。
在包含#include <windows.h>
后会递归地包含minwindef.h
,而在 minwindef.h
中有宏定义:
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
而我们实际想用的max min
是在"Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\algorithm"
中定义的:
const _Ty& (max)(const _Ty& _Left, const _Ty& _Right)
{ // return larger of _Left and _Right
return (_DEBUG_LT(_Left, _Right) ? _Right : _Left);
}
const _Ty& (min)(const _Ty& _Left, const _Ty& _Right)
{ // return smaller of _Left and _Right
return (_DEBUG_LT(_Right, _Left) ? _Right : _Left);
}
因此就冲突了。
5. Visual Studio编译时出现error LNK2005
在使用自己的工程链接其它静态库的时候有时会报如下的错误:
错误 2 error LNK2005: __xi_z 已经在 MSVCRT.lib(cinitexe.obj) 中定义
错误 1 error LNK2005: __xi_a 已经在 MSVCRT.lib(cinitexe.obj) 中定义
错误 4 error LNK2005: __xc_z 已经在 MSVCRT.lib(cinitexe.obj) 中定义
错误 3 error LNK2005: __xc_a 已经在 MSVCRT.lib(cinitexe.obj) 中定义
错误 7 error LNK1169: 找到一个或多个多重定义的符号
这种错误的原因是自己工程的属性->C/C++->代码生成->运行库
设置和静态库的设置不同,将二者改为相同的设置即可解决问题。还需要注意有可能Debug版本的设置和Release版本的设置也不同,例如,如果Debug版本的运行库设置的是多线程调试 DLL (/MDd)
而Release版本的设置是多线程 DLL (/MD)
,如果需要链接的静态库的运行库是在多线程 DLL (/MD)
下编译的,则自己的工程在Release版本下是可以正常编译、运行的,但是在Debug下虽然可以编译通过但是无法正常运行。可参考VS编译链接时错误(Error Link2005)的解决方法。
6. cv::Mat_
如果不想用臃肿的at
取像素操作,可使用cv::Mat_
。例如,下面两段代码等价:
使用cv::Mat
的at
方法取像素:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main()
{
cv::Mat img = cv::imread("ALBT9701.jpg");
cv::resize(img, img, cv::Size(img.cols / 10, img.rows / 10));
for (int i = img.rows / 4; i != img.rows / 4 * 3; ++i)
{
for (int j = img.cols / 2; j != img.cols; ++j)
{
img.at<cv::Vec3b>(i, j) = cv::Vec3b(255, 255, 0);
}
}
cv::imshow("img", img);
cv::waitKey(0);
return 0;
}
使用cv::Mat_
的()
方法直接取像素:
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main(int argc, char** argv)
{
cv::Mat_<cv::Vec3b> img = cv::imread("ALBT9701.jpg");
cv::resize(img,img,cv::Size(img.cols/10,img.rows/10));
for (int i = img.rows/4; i != img.rows/4*3; ++i)
{
for (int j = img.cols / 2; j != img.cols; ++j)
{
img(i, j) = cv::Vec3b(255, 255, 0);
}
}
cv::imshow("img", img);
cv::waitKey(0);
return 0;
}
两段代码结果相同,如图1所示:
7. 指针高效访问像素
相比at
方法用指针取像素更高效。
对于彩色图:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main()
{
cv::Mat img = cv::imread("ALBT9701.jpg",1);
cv::resize(img, img, cv::Size(img.cols / 10, img.rows / 10));
for (int i = img.rows / 4; i != img.rows / 4 * 3; ++i)
{
/* data表示第i行的首地址 */
cv::Vec3b * data = img.ptr<cv::Vec3b>(i);
for (int j = img.cols / 2; j != img.cols; ++j)
{
/* 下面三句是等价的 */
// img.at<cv::Vec3b>(i, j) = cv::Vec3b(255, 255, 0);
// *(data +j)= cv::Vec3b(255, 255, 0);
data[j] = cv::Vec3b(255, 0, 0);
}
}
cv::imshow("img", img);
cv::waitKey(0);
return 0;
}
对于灰度图:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main()
{
cv::Mat img = cv::imread("ALBT9701.jpg",0);
cv::resize(img, img, cv::Size(img.cols / 10, img.rows / 10));
for (int i = img.rows / 4; i != img.rows / 4 * 3; ++i)
{
uchar * data = img.ptr<uchar>(i);
for (int j = img.cols / 2; j != img.cols; ++j)
{
/* 下面三句是等价的 */
//img.at<uchar>(i, j) = 255;
//data[j] = 255;
*(data +j)= 255;
}
}
cv::imshow("img", img);
cv::waitKey(0);
return 0;
}
8. OpenCV—isContinuous()
OpenCV的isContinuous()
函数可判断图像data是不是连续存储的,如果是连续的就可以在一个循环中处理数据。
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main()
{
cv::Mat img = cv::imread("ALBT9701_.jpg", 1);
/* 如果图像数据连续 */
if (img.isContinuous())
{
/* ptr为图像数据首地址 */
cv::Vec3b *ptr = img.ptr<cv::Vec3b>();
for (int i = 0; i != img.rows*img.cols; ++i)
{
/* 将图像前50行置为黄色,供测试用 */
if (i<=50*img.cols)
{
ptr[i][0] = 0;
ptr[i][1] = 255;
ptr[i][2] = 255;
}
}
cv::imshow("img", img);
cv::waitKey(0);
}
}
9. OpenCV—reshape()
如果图像时连续的没有填充,可以进行reshape()
,可以利用reshape()
函数返回高度为1、宽度为img.rows*img.cols
(或高度为img.rows*img.cols
、宽度为1)的图像,从而去除外出的循环。
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
int main()
{
cv::Mat img = cv::imread("ALBT9701_.jpg", 1);
cv::resize(img, img, cv::Size(img.cols, img.rows));
if (img.isContinuous())
{
cv::Mat img_reshape = img.reshape(3, 1);
cv::imshow("img", img);
cv::imshow("img_reshape", img_reshape);
cv::waitKey(0);
}
}
由于一行图像不好展示,此处就不附图了。
10. OpenCV使用迭代器访问像素
int main()
{
cv::Mat img = cv::imread("ALBT9701_.jpg", 1);
/* 获取图像迭代器的方式一 */
cv::Mat_<cv::Vec3b>::iterator it = img.begin<cv::Vec3b>();
/* 获取图像迭代器的方式二 */
cv::MatIterator_<cv::Vec3b> itend = img.end<cv::Vec3b>();
for (;it != itend;++it)
{
(*it)[0] = 255;
(*it)[1] = 0;
(*it)[2] = 255;
}
cv::imshow("img", img);
cv::waitKey(0);
}
效果如图3所示: