c++ tensorflow数据转换

主要参考tensorflow examples,其中重要的两个label_imagemultibox_detector

跑代码的话这两个例子应该就可以顺利跑通了。

但是,中间想要转换成其他格式的话就需要自己探索了。

我的探索:

指针转化成tensor图像

        //pimage为指向图像的指针,input存放图像tensor    
        int Depth = 1;//Depth 图像深度,height, width为图像的宽和高
	Tensor input(
		tensorflow::DT_FLOAT,
		tensorflow::TensorShape({ 1, height, width, 1 }));

	auto input_map = input.tensor<float, 4>();

	for (int y = 0; y < height; ++y) {
		const uchar* source_row = (uchar*)pimage + y * (width);
		for (int x = 0; x < width; ++x) {
			const uchar* source_pixel = source_row + (x * Depth);
			for (int c = 0; c < Depth; ++c) {
				const uchar* source_value = source_pixel + c;
				input_map(0, y, x, c) = *source_value;
			}
		}
	}

tensor转化成mat图像 

        //out_image_tensors为session->Run的输出,由3个维度组成,tres为mat类型数据存放图像,这里操作的只是单通道图像
        //第一种方法,相当于循环赋值
        const Tensor& encoded_locations = out_image_tensors[0];
	auto input_map = encoded_locations.tensor<uint8_t, 3>();

	int width =512, height = 512;

	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			tres.at<uint8_t>(y, x) = input_map(y, x, 0);
		}
	}

        //第二种方法,拷贝地址
        tres.convertTo(tres, CV_32FC1);
	tensorflow::StringPiece tmp_data = out_tensor.tensor_data();
	memcpy(tres.data,const_cast<char*>(tmp_data.data()),height * width * sizeof(float));
	tres.convertTo(tres, CV_8UC1);
	tres = tres * 255;

踩坑1:mat图像存在深拷贝、浅拷贝的问题,如果是浅拷贝,保存到新的指针之后,原始指针也会随着新的变换而变换。

    //img2发生变化时,img1也发生变化
    Mat img1=imread("test.jpg");  //将任意一张名为test.jpg的图片放置于工程文件夹test中
    Mat img2=img1;                //拷贝方式为浅拷贝
    
    //img2发生变化时,img1不会发生变化
    Mat img1=imread("test.jpg");  //将任意一张名为test.jpg的图片放置于工程文件夹test中
    Mat img2=img1.clone();        //拷贝方式是深拷贝

mat深拷贝浅拷贝再探: 

深浅拷贝不止上面的两种,更新下~

//img2发生变化时,img1也发生变化
Mat img1=imread("test.jpg");  //将任意一张名为test.jpg的图片放置于工程文件夹test中
Mat img2=img1;                //1——copy assignment 操作符,拷贝方式为浅拷贝
Mat img3(img1);                //2——拷贝构造函数,拷贝方式为浅拷贝
    
//img2发生变化时,img1不会发生变化
Mat img1=imread("test.jpg");  //将任意一张名为test.jpg的图片放置于工程文件夹test中
Mat img2=img1.clone();        //3——拷贝方式是深拷贝
Mat img3;
img1.copyTo(img3);      //4——拷贝方式是深拷贝

在opencv 2.x之前,OpenCV基于 C 语言接口而建。为了在内存(memory)中存放图像,当时采用名为 IplImage 的C语言结构体作为基本的图像容器。但是该方式有一个极大的弊端就是要手动内存管理,其依据是用户要为开辟和销毁内存负责。虽然对于小型的程序来说手动管理内存不是问题,但一旦代码开始变得越来越庞大,你需要越来越多地纠缠于这个问题,而不是着力解决你的开发目标。 
之后,新的Mat类型代替了之前的IplImage,这次改变也带来了Opencv最强大的数据类型—Mat。Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此,当在程序中传递图像并创建拷贝时,大的开销是由矩阵造成的,而不是信息头。所以,除非有必要,否则我们不应该拷贝大的图像,因为这会降低程序速度。 
为了搞定这个问题,OpenCV使用引用计数机制。其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。而拷贝构造函数则只拷贝信息头和矩阵指针 ,而不拷贝矩阵。但某些时候你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo() 。

小结:

 OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。 
 使用OpenCV的C++接口时不需要考虑内存释放问题,引用机制会有效解决内存释放。 
 赋值运算符和拷贝构造函数( ctor )只拷贝信息头。 
 使用函数 clone() 或者 copyTo() 来拷贝一副图像的矩阵。
 

踩坑2:(CV_8UC1)访问像素时,uchar类型无法cout输出正常的整型数值

       C++的主要数据类型,主要分为三类:布尔型,整型(char型从本质上说,也是种整型类型,它是长度为1的整数,通常用来存放字符的ASCII码),浮点型。 

*_t 只是c++ 通过typedef给类型起得别名:

typedef signed char             int8_t;
typedef short int               int16_t;
typedef int                     int32_t;

typedef unsigned char           uint8_t;
typedef unsigned short int      uint16_t;
typedef unsigned int            uint32_t;

注意,uint8_t实际上就是一个uchar,所以输出 uint8_t类型的变量实际上输出对应的字符,而不是数值,比如:

uint8_t  num=67;
cout << num << endl;		//输出结果为C

因此想要打印整型的像素值时需要进行强制转化

uint8_t  num=67;
cout << num << endl;		//输出结果为C
cout<<int(num)<<endl;           //输出结果为67
cout<<to_string(num)<<endl;     //输出结果为67,但是!!!string(num)是不能实现强制转换的!!

ps:相对路径是针对后缀为vcxproj文件而言的。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值