常用工具&常见问题

4 篇文章 0 订阅

常用工具,持续更新,记录点滴……

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::minstd::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::Matat方法取像素:

#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所示:

ellipse
图1 cv::Mat & cv::Mat_

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);
	}
}
ellipse
图2 单个循环中处理连续存储的图像数据
如果图像数据是连续的,那么也会满足:`img.step == img.cols*img.elemSize()`。`img.step`代表图像一行所占字节数,`img.elemSize()`代表图像单个像素所占字节数,如果是三通道、`uchar`数据,则`img.elemSize()==3`。 需要注意的是有时为了性能的考虑,会在图像每行末尾进行填充,因此就有可能出现`img.step > img.cols*img.elemSize()`的情况。

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所示:

ellipse
图3 使用迭代器操作像素
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值