caffe.proto中TransformationParameter部分

// Message that stores parameters used to apply transformation
// to the data layer's data
message TransformationParameter {
// For data pre-processing, we can do simple scaling and subtracting the
// data mean, if provided. Note that the mean subtraction is always carried
// out before scaling.
//像素幅度缩放参数,默认不缩放
optional float scale = [default = ];
// Specify if we want to randomly mirror data.
//图像随机镜像开关。默认false,不进行随机镜像操作
optional bool mirror = [default = false];
// Specify if we would like to randomly crop an image.
//图像随机切块的大小。默认不切块。
optional uint32 crop_size = [default = ];
// mean_file and mean_value cannot be specified at the same time
//存储图像均值的文件
optional string mean_file = ;
// if specified can be repeated once (would subtract it from all the channels)
// or can be repeated the same number of times as channels
// (would subtract them from the corresponding channel)
//均值数值。无需读取文件。
//如果均值数目和通道数一致,则每个通道分别减去对应均值;
//如果没有均值只有一个,则每个通道减去相同的均值。
repeated float mean_value = ;
// Force the decoded image to have 3 color channels.
//强制为三通道三色图像输入 默认false
optional bool force_color = [default = false];
// Force the decoded image to have 1 color channels.
//强制为单通道灰度图像输入 默认false
optional bool force_gray = [default = false];
}

caffe.proto中Datum部分

//Datum用来从LMDB/LEVELDB 中读取数据,或将数据写入其中。和BlobProto有相似功能,只是
//BlobProto用于模型权值序列化反序列化,而Datum用于数据或特征图(feature map)提供序列化反序列化
message Datum {
//数据维度信息:channels*height*width
optional int32 channels = ;
optional int32 height = ;
optional int32 width = ;
// the actual image data, in bytes
//图像数据,以字节类型存储
optional bytes data = ;
//标签数据,统一用int32类型存储
optional int32 label = ;
// Optionally, the datum could also hold float data.
repeated float float_data = ;
// If true data contains an encoded image that need to be decoded
optional bool encoded = [default = false];//是否为编码数据,默认false
}

include/caffe/data_transformer.hpp

 #ifndef CAFFE_DATA_TRANSFORMER_HPP
#define CAFFE_DATA_TRANSFORMER_HPP #include <vector> #include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/proto/caffe.pb.h" namespace caffe { /**
* @brief Applies common transformations to the input data, such as
* scaling, mirroring, substracting the image mean...
*/
template <typename Dtype>
class DataTransformer {
public:
explicit DataTransformer(const TransformationParameter& param, Phase phase);
virtual ~DataTransformer() {} /**
* @brief Initialize the Random number generations if needed by the
* transformation.
*/
//初始化随机数种子函数
void InitRand(); /**
* @brief Applies the transformation defined in the data layer's
* transform_param block to the data.
*
* @param datum
* Datum containing the data to be transformed.
* @param transformed_blob
* This is destination blob. It can be part of top blob's data if
* set_cpu_data() is used. See data_layer.cpp for an example.
*/
//数据读取层中transform_param块所声明的变换应用到输入数据中。
//以下为多个重载函数
void Transform(const Datum& datum, Blob<Dtype>* transformed_blob); /**
* @brief Applies the transformation defined in the data layer's
* transform_param block to a vector of Datum.
*
* @param datum_vector
* A vector of Datum containing the data to be transformed.
* @param transformed_blob
* This is destination blob. It can be part of top blob's data if
* set_cpu_data() is used. See memory_layer.cpp for an example.
*/
void Transform(const vector<Datum> & datum_vector,
Blob<Dtype>* transformed_blob);
//使用OPENCV
#ifdef USE_OPENCV
/**
* @brief Applies the transformation defined in the data layer's
* transform_param block to a vector of Mat.
*
* @param mat_vector
* A vector of Mat containing the data to be transformed.
* @param transformed_blob
* This is destination blob. It can be part of top blob's data if
* set_cpu_data() is used. See memory_layer.cpp for an example.
*/
void Transform(const vector<cv::Mat> & mat_vector,
Blob<Dtype>* transformed_blob); /**
* @brief Applies the transformation defined in the data layer's
* transform_param block to a cv::Mat
*
* @param cv_img
* cv::Mat containing the data to be transformed.
* @param transformed_blob
* This is destination blob. It can be part of top blob's data if
* set_cpu_data() is used. See image_data_layer.cpp for an example.
*/
void Transform(const cv::Mat& cv_img, Blob<Dtype>* transformed_blob);
#endif // USE_OPENCV /**
* @brief Applies the same transformation defined in the data layer's
* transform_param block to all the num images in a input_blob.
*
* @param input_blob
* A Blob containing the data to be transformed. It applies the same
* transformation to all the num images in the blob.
* @param transformed_blob
* This is destination blob, it will contain as many images as the
* input blob. It can be part of top blob's data.
*/
void Transform(Blob<Dtype>* input_blob, Blob<Dtype>* transformed_blob); /**
* @brief Infers the shape of transformed_blob will have when
* the transformation is applied to the data.
*
* @param datum
* Datum containing the data to be transformed.
*/
//获取执行变换后输出Blob的形状
vector<int> InferBlobShape(const Datum& datum);
/**
* @brief Infers the shape of transformed_blob will have when
* the transformation is applied to the data.
* It uses the first element to infer the shape of the blob.
*
* @param datum_vector
* A vector of Datum containing the data to be transformed.
*/
vector<int> InferBlobShape(const vector<Datum> & datum_vector);
/**
* @brief Infers the shape of transformed_blob will have when
* the transformation is applied to the data.
* It uses the first element to infer the shape of the blob.
*
* @param mat_vector
* A vector of Mat containing the data to be transformed.
*/
#ifdef USE_OPENCV
vector<int> InferBlobShape(const vector<cv::Mat> & mat_vector);
/**
* @brief Infers the shape of transformed_blob will have when
* the transformation is applied to the data.
*
* @param cv_img
* cv::Mat containing the data to be transformed.
*/
vector<int> InferBlobShape(const cv::Mat& cv_img);
#endif // USE_OPENCV protected:
/**
* @brief Generates a random integer from Uniform({0, 1, ..., n-1}).
*
* @param n
* The upperbound (exclusive) value of the random number.
* @return
* A uniformly random integer value from ({0, 1, ..., n-1}).
*/
//产生取值0到n-1的随机整数,服从均匀分布
virtual int Rand(int n); void Transform(const Datum& datum, Dtype* transformed_data);
// Tranformation parameters
//变换参数,该数据结构由ProtoBuffer工具自动生成
TransformationParameter param_; //随机数生成器。声明在include/caffe/common.hpp中
shared_ptr<Caffe::RNG> rng_;
Phase phase_;//当前运行阶段。TRAIN或TEST。阶段不同,变换会有差异
Blob<Dtype> data_mean_;//均值图像,从均值文件中读取
vector<Dtype> mean_values_;//均值数值,从param_中提取
}; } // namespace caffe #endif // CAFFE_DATA_TRANSFORMER_HPP_

src/caffe/data_transformer.cpp

 #ifdef USE_OPENCV
#include <opencv2/core/core.hpp>
#endif // USE_OPENCV #include <string>
#include <vector> #include "caffe/data_transformer.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp" namespace caffe { template<typename Dtype>
DataTransformer<Dtype>::DataTransformer(const TransformationParameter& param,
Phase phase)
: param_(param), phase_(phase) {//初始化
// check if we want to use mean_file
//查看是否使用均值文件
if (param_.has_mean_file()) {
//如果param_中指定了均值文件又指定了均值数值则报错,二选一
CHECK_EQ(param_.mean_value_size(), ) <<
"Cannot specify mean_file and mean_value at the same time";
const string& mean_file = param.mean_file();//获取均值文件名
if (Caffe::root_solver()) {
LOG(INFO) << "Loading mean file from: " << mean_file;
}
BlobProto blob_proto;//从均值文件中读取数据到blob_proto对象中
ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto);
data_mean_.FromProto(blob_proto);//从blob_proto将均值反序列化到data_mean_内存中
}
// check if we want to use mean_value
//查看是否使用均值数值
if (param_.mean_value_size() > ) {
CHECK(param_.has_mean_file() == false) <<
"Cannot specify mean_file and mean_value at the same time";
for (int c = ; c < param_.mean_value_size(); ++c) {
//读取均值数值,不再读取均值文件
mean_values_.push_back(param_.mean_value(c));
}
}
}
//若干重载变换函数
//该函数以Datum作为输入,结构体在caffe.proto中可见。输出为数据指针
template<typename Dtype>
void DataTransformer<Dtype>::Transform(const Datum& datum,
Dtype* transformed_data) {
//获得datum数据字串、维度信息
const string& data = datum.data();
const int datum_channels = datum.channels();
const int datum_height = datum.height();
const int datum_width = datum.width(); //从param_获得处理参数。如切块大小、幅度缩放、随机镜像、图像均值等
const int crop_size = param_.crop_size();
const Dtype scale = param_.scale();
const bool do_mirror = param_.mirror() && Rand();
const bool has_mean_file = param_.has_mean_file();
const bool has_uint8 = data.size() > ;
const bool has_mean_values = mean_values_.size() > ; CHECK_GT(datum_channels, );//保证输入通道数大于零
CHECK_GE(datum_height, crop_size);//保证输入数据的宽和高大于切块尺寸
CHECK_GE(datum_width, crop_size); //获得图像均值
Dtype* mean = NULL;
if (has_mean_file) {//若指定了图像均值文件
//保证图像的均值文件的维度和输入图像数据的维度完全相同
CHECK_EQ(datum_channels, data_mean_.channels());
CHECK_EQ(datum_height, data_mean_.height());
CHECK_EQ(datum_width, data_mean_.width());
mean = data_mean_.mutable_cpu_data();//获得图像均值数据控制权
}
if (has_mean_values) {//未指定图像均值文件,直接给出均值数值
//保证均值数据维数是1,或与输入图像数据的通道数相同
CHECK(mean_values_.size() == || mean_values_.size() == datum_channels) <<
"Specify either 1 mean_value or as many as channels: " << datum_channels;
if (datum_channels > && mean_values_.size() == ) {
// Replicate the mean_value for simplicity
//如果均值数据维度为1,且输入数据通道数大于1,则重复channels次
for (int c = ; c < datum_channels; ++c) {
mean_values_.push_back(mean_values_[]);
}
}
} //输入图像的宽和高
int height = datum_height;
int width = datum_width; //开始图像切块
int h_off = ;
int w_off = ;
if (crop_size) {//若不为0则进行切块,为0不切块
height = crop_size;
width = crop_size;
// We only do random crop when we do training.
// 在训练的时候随机crop图像块,这里需要自己实现Rand这个函数来确定是如何随机的
if (phase_ == TRAIN) {
h_off = Rand(datum_height - crop_size + );// 产生从0到datum_height - crop_size的随机数
w_off = Rand(datum_width - crop_size + );//切块的width偏移量
} else {//测试阶段只切取图像中心位置
h_off = (datum_height - crop_size) / ;
w_off = (datum_width - crop_size) / ;
}
} // 对数据进行变换,主要是将原来的像素值减去均值,然后乘以scale这么一个操作
// 如果需要crop则最终转换的Blob的大小即为crop*crop
// 如果不是,则最终的Blob大小即为datum_height*datum_width
Dtype datum_element;//存放输入图像的像素值
int top_index, data_index;//分别存放输出index和输入index
for (int c = ; c < datum_channels; ++c) {
for (int h = ; h < height; ++h) {
for (int w = ; w < width; ++w) {
data_index = (c * datum_height + h_off + h) * datum_width + w_off + w;
if (do_mirror) {//若需要镜像操作,则输出index时设置width反向
top_index = (c * height + h) * width + (width - - w);
} else {
top_index = (c * height + h) * width + w;
}
if (has_uint8) {// Datum中如果是uint8存储图像数据则转换为float
datum_element =
static_cast<Dtype>(static_cast<uint8_t>(data[data_index]));
} else {//否则为float
datum_element = datum.float_data(data_index);
}
if (has_mean_file) {//若指定了均值文件
transformed_data[top_index] =
(datum_element - mean[data_index]) * scale;//去均值,幅度缩放
} else {
if (has_mean_values) {//若指定了均值数值
transformed_data[top_index] =
(datum_element - mean_values_[c]) * scale;//去均值,幅度缩放
} else {
transformed_data[top_index] = datum_element * scale;//不去均值,只幅度缩放
}
}
}
}
}
} //与上一个函数类似,只是输出变成blob
template<typename Dtype>
void DataTransformer<Dtype>::Transform(const Datum& datum,
Blob<Dtype>* transformed_blob) {
// If datum is encoded, decode and transform the cv::image.
if (datum.encoded()) {// 检查datum是否经过编码的图像,如果是则解码
#ifdef USE_OPENCV
// 先检查是不是两个属性都设置, 如果是则说明参数设置有误
CHECK(!(param_.force_color() && param_.force_gray()))
<< "cannot set both force_color and force_gray";
cv::Mat cv_img;
if (param_.force_color() || param_.force_gray()) {
// If force_color then decode in color otherwise decode in gray.
// 如果强制彩色或者强制灰度图像一个成立则使用DecodeDatumToCVMat解码
cv_img = DecodeDatumToCVMat(datum, param_.force_color());
} else {// 否则使用DecodeDatumToCVMatNative解码
cv_img = DecodeDatumToCVMatNative(datum);
}
// Transform the cv::image into blob.将cv::image 变换为 blob
return Transform(cv_img, transformed_blob);
#else
LOG(FATAL) << "Encoded datum requires OpenCV; compile with USE_OPENCV.";
#endif // USE_OPENCV
} else {// 如果没有编码则检查force_color和force_gray是否设置,如果设置则不合法,因为该选项只适合于编码后的数据
if (param_.force_color() || param_.force_gray()) {
LOG(ERROR) << "force_color and force_gray only for encoded datum";
}
} const int crop_size = param_.crop_size();
const int datum_channels = datum.channels();
const int datum_height = datum.height();
const int datum_width = datum.width(); // Check dimensions.检查维度
const int channels = transformed_blob->channels();
const int height = transformed_blob->height();
const int width = transformed_blob->width();
const int num = transformed_blob->num(); CHECK_EQ(channels, datum_channels);
CHECK_LE(height, datum_height);
CHECK_LE(width, datum_width);
CHECK_GE(num, ); if (crop_size) {
CHECK_EQ(crop_size, height);
CHECK_EQ(crop_size, width);
} else {
CHECK_EQ(datum_height, height);
CHECK_EQ(datum_width, width);
}
// 参数变换完毕,调用现有函数
Dtype* transformed_data = transformed_blob->mutable_cpu_data();
Transform(datum, transformed_data);
} //对一组datum数据进行变换
template<typename Dtype>
void DataTransformer<Dtype>::Transform(const vector<Datum> & datum_vector,
Blob<Dtype>* transformed_blob) {
const int datum_num = datum_vector.size();
const int num = transformed_blob->num();// 变换到的目标blob的形状
const int channels = transformed_blob->channels();
const int height = transformed_blob->height();
const int width = transformed_blob->width(); CHECK_GT(datum_num, ) << "There is no datum to add";
CHECK_LE(datum_num, num) <<
"The size of datum_vector must be no greater than transformed_blob->num()";
Blob<Dtype> uni_blob(, channels, height, width);// 新建一个uni_blob,里面只有一个batch。临时Blob
//依次对每一个datum进行变换,放入对应的Blob之中
for (int item_id = ; item_id < datum_num; ++item_id) {
int offset = transformed_blob->offset(item_id);
uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset);
Transform(datum_vector[item_id], &uni_blob);
}
} #ifdef USE_OPENCV
//对一组cv::Mat对象进行变换,放入Blob中
template<typename Dtype>
void DataTransformer<Dtype>::Transform(const vector<cv::Mat> & mat_vector,
Blob<Dtype>* transformed_blob) {
// 获取mat的参数
const int mat_num = mat_vector.size();
const int num = transformed_blob->num();
const int channels = transformed_blob->channels();
const int height = transformed_blob->height();
const int width = transformed_blob->width(); CHECK_GT(mat_num, ) << "There is no MAT to add";
CHECK_EQ(mat_num, num) <<
"The size of mat_vector must be equals to transformed_blob->num()";
Blob<Dtype> uni_blob(, channels, height, width);
for (int item_id = ; item_id < mat_num; ++item_id) {
int offset = transformed_blob->offset(item_id);
uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset);
Transform(mat_vector[item_id], &uni_blob);
}
}
// 对一个cv::Mat对象进行变换,放入Blob中。
// 如果是图像的话,需要减去均值乘以scale,判断是不是需要做镜像处理
// 逻辑与前面类似
template<typename Dtype>
void DataTransformer<Dtype>::Transform(const cv::Mat& cv_img,
Blob<Dtype>* transformed_blob) {
const int crop_size = param_.crop_size();
const int img_channels = cv_img.channels();
const int img_height = cv_img.rows;
const int img_width = cv_img.cols; // Check dimensions.检查维度
const int channels = transformed_blob->channels();
const int height = transformed_blob->height();
const int width = transformed_blob->width();
const int num = transformed_blob->num(); CHECK_EQ(channels, img_channels);
CHECK_LE(height, img_height);
CHECK_LE(width, img_width);
CHECK_GE(num, ); CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte"; const Dtype scale = param_.scale();
const bool do_mirror = param_.mirror() && Rand();
const bool has_mean_file = param_.has_mean_file();
const bool has_mean_values = mean_values_.size() > ; CHECK_GT(img_channels, );
CHECK_GE(img_height, crop_size);
CHECK_GE(img_width, crop_size); Dtype* mean = NULL;
if (has_mean_file) {
CHECK_EQ(img_channels, data_mean_.channels());
CHECK_EQ(img_height, data_mean_.height());
CHECK_EQ(img_width, data_mean_.width());
mean = data_mean_.mutable_cpu_data();
}
if (has_mean_values) {
CHECK(mean_values_.size() == || mean_values_.size() == img_channels) <<
"Specify either 1 mean_value or as many as channels: " << img_channels;
if (img_channels > && mean_values_.size() == ) {
// Replicate the mean_value for simplicity 复制均值便于操作
for (int c = ; c < img_channels; ++c) {
mean_values_.push_back(mean_values_[]);
}
}
} int h_off = ;
int w_off = ;
cv::Mat cv_cropped_img = cv_img;
if (crop_size) {
CHECK_EQ(crop_size, height);
CHECK_EQ(crop_size, width);
// We only do random crop when we do training.只有训练阶段才随机切块
if (phase_ == TRAIN) {
h_off = Rand(img_height - crop_size + );
w_off = Rand(img_width - crop_size + );
} else {
h_off = (img_height - crop_size) / ;
w_off = (img_width - crop_size) / ;
}
cv::Rect roi(w_off, h_off, crop_size, crop_size);
cv_cropped_img = cv_img(roi);
} else {
CHECK_EQ(img_height, height);
CHECK_EQ(img_width, width);
} CHECK(cv_cropped_img.data); Dtype* transformed_data = transformed_blob->mutable_cpu_data();
int top_index;
for (int h = ; h < height; ++h) {
const uchar* ptr = cv_cropped_img.ptr<uchar>(h);
int img_index = ;
for (int w = ; w < width; ++w) {
for (int c = ; c < img_channels; ++c) {
if (do_mirror) {
top_index = (c * height + h) * width + (width - - w);
} else {
top_index = (c * height + h) * width + w;
}
// int top_index = (c * height + h) * width + w;
Dtype pixel = static_cast<Dtype>(ptr[img_index++]);
if (has_mean_file) {
int mean_index = (c * img_height + h_off + h) * img_width + w_off + w;
transformed_data[top_index] =
(pixel - mean[mean_index]) * scale;
} else {
if (has_mean_values) {
transformed_data[top_index] =
(pixel - mean_values_[c]) * scale;
} else {
transformed_data[top_index] = pixel * scale;
}
}
}
}
}
}
#endif // USE_OPENCV //输入输出都是Blob
template<typename Dtype>
void DataTransformer<Dtype>::Transform(Blob<Dtype>* input_blob,
Blob<Dtype>* transformed_blob) {
const int crop_size = param_.crop_size();
const int input_num = input_blob->num();
const int input_channels = input_blob->channels();
const int input_height = input_blob->height();
const int input_width = input_blob->width(); if (transformed_blob->count() == ) {
// Initialize transformed_blob with the right shape.初始化变换后的Blob形状
if (crop_size) {
transformed_blob->Reshape(input_num, input_channels,
crop_size, crop_size);
} else {
transformed_blob->Reshape(input_num, input_channels,
input_height, input_width);
}
} const int num = transformed_blob->num();
const int channels = transformed_blob->channels();
const int height = transformed_blob->height();
const int width = transformed_blob->width();
const int size = transformed_blob->count(); CHECK_LE(input_num, num);
CHECK_EQ(input_channels, channels);
CHECK_GE(input_height, height);
CHECK_GE(input_width, width); const Dtype scale = param_.scale();
const bool do_mirror = param_.mirror() && Rand();
const bool has_mean_file = param_.has_mean_file();
const bool has_mean_values = mean_values_.size() > ; int h_off = ;
int w_off = ;
if (crop_size) {
CHECK_EQ(crop_size, height);
CHECK_EQ(crop_size, width);
// We only do random crop when we do training.只有训练阶段随机切块
if (phase_ == TRAIN) {
h_off = Rand(input_height - crop_size + );
w_off = Rand(input_width - crop_size + );
} else {
h_off = (input_height - crop_size) / ;
w_off = (input_width - crop_size) / ;
}
} else {
CHECK_EQ(input_height, height);
CHECK_EQ(input_width, width);
} Dtype* input_data = input_blob->mutable_cpu_data();
if (has_mean_file) {
CHECK_EQ(input_channels, data_mean_.channels());
CHECK_EQ(input_height, data_mean_.height());
CHECK_EQ(input_width, data_mean_.width());
for (int n = ; n < input_num; ++n) {
int offset = input_blob->offset(n);
/*
template <typename Dtype>
void caffe_sub(const int N, const Dtype* a, const Dtype* b, Dtype* y);
math_function中定义的caffe_sub目的是矩阵相减input_data(以offset开始的矩阵) = input_data(以offset开始的矩阵) - data_mean_
*/
caffe_sub(data_mean_.count(), input_data + offset,
data_mean_.cpu_data(), input_data + offset);
}
} if (has_mean_values) {
CHECK(mean_values_.size() == || mean_values_.size() == input_channels) <<
"Specify either 1 mean_value or as many as channels: " << input_channels;
if (mean_values_.size() == ) {
caffe_add_scalar(input_blob->count(), -(mean_values_[]), input_data);
} else {
for (int n = ; n < input_num; ++n) {
for (int c = ; c < input_channels; ++c) {
int offset = input_blob->offset(n, c);
// 给input_data[offset]地址开始的每一个元素加上一个-mean_values_[c]
caffe_add_scalar(input_height * input_width, -(mean_values_[c]),
input_data + offset);
}
}
}
}
// 如果什么均值都没有则直接复制
Dtype* transformed_data = transformed_blob->mutable_cpu_data(); for (int n = ; n < input_num; ++n) {
int top_index_n = n * channels;
int data_index_n = n * channels;
for (int c = ; c < channels; ++c) {
int top_index_c = (top_index_n + c) * height;
int data_index_c = (data_index_n + c) * input_height + h_off;
for (int h = ; h < height; ++h) {
int top_index_h = (top_index_c + h) * width;
int data_index_h = (data_index_c + h) * input_width + w_off;
if (do_mirror) {
int top_index_w = top_index_h + width - ;
for (int w = ; w < width; ++w) {
transformed_data[top_index_w-w] = input_data[data_index_h + w];
}
} else {
for (int w = ; w < width; ++w) {
transformed_data[top_index_h + w] = input_data[data_index_h + w];
}
}
}
}
}
if (scale != Dtype()) {
DLOG(INFO) << "Scale: " << scale;
caffe_scal(size, scale, transformed_data);
}
} //获得数据变换输出尺寸
template<typename Dtype>
vector<int> DataTransformer<Dtype>::InferBlobShape(const Datum& datum) {
if (datum.encoded()) {
#ifdef USE_OPENCV
// 如果使用OpenCV则可以用先转换为CVMat,然后推断blob的形状
CHECK(!(param_.force_color() && param_.force_gray()))
<< "cannot set both force_color and force_gray";
cv::Mat cv_img;
if (param_.force_color() || param_.force_gray()) {
// If force_color then decode in color otherwise decode in gray.
cv_img = DecodeDatumToCVMat(datum, param_.force_color());
} else {
cv_img = DecodeDatumToCVMatNative(datum);
}
// InferBlobShape using the cv::image.
return InferBlobShape(cv_img);
#else
LOG(FATAL) << "Encoded datum requires OpenCV; compile with USE_OPENCV.";
#endif // USE_OPENCV
}
// 否则直接粗暴地从datum里面获取形状的数据
const int crop_size = param_.crop_size();
const int datum_channels = datum.channels();
const int datum_height = datum.height();
const int datum_width = datum.width();
// Check dimensions.检查维度
CHECK_GT(datum_channels, );
CHECK_GE(datum_height, crop_size);
CHECK_GE(datum_width, crop_size);
// Build BlobShape. 创建BlobShape对象
vector<int> shape();
shape[] = ;
shape[] = datum_channels;
shape[] = (crop_size)? crop_size: datum_height;
shape[] = (crop_size)? crop_size: datum_width;
return shape;
} template<typename Dtype>
vector<int> DataTransformer<Dtype>::InferBlobShape(
const vector<Datum> & datum_vector) {
const int num = datum_vector.size();
CHECK_GT(num, ) << "There is no datum to in the vector";
// Use first datum in the vector to InferBlobShape.使用第一个推断
vector<int> shape = InferBlobShape(datum_vector[]);
// Adjust num to the size of the vector.
shape[] = num;
return shape;
} #ifdef USE_OPENCV
// 如果使用OpenCV
// 使用CVMat中的信息来推断形状
template<typename Dtype>
vector<int> DataTransformer<Dtype>::InferBlobShape(const cv::Mat& cv_img) {
const int crop_size = param_.crop_size();
const int img_channels = cv_img.channels();
const int img_height = cv_img.rows;
const int img_width = cv_img.cols;
// Check dimensions.
CHECK_GT(img_channels, );
CHECK_GE(img_height, crop_size);
CHECK_GE(img_width, crop_size);
// Build BlobShape.
vector<int> shape();
shape[] = ;
shape[] = img_channels;
shape[] = (crop_size)? crop_size: img_height;
shape[] = (crop_size)? crop_size: img_width;
return shape;
} template<typename Dtype>
vector<int> DataTransformer<Dtype>::InferBlobShape(
const vector<cv::Mat> & mat_vector) {
const int num = mat_vector.size();
CHECK_GT(num, ) << "There is no cv_img to in the vector";
// Use first cv_img in the vector to InferBlobShape.
vector<int> shape = InferBlobShape(mat_vector[]);
// Adjust num to the size of the vector.
shape[] = num;
return shape;
}
#endif // USE_OPENCV // 初始化随机数种子
template <typename Dtype>
void DataTransformer<Dtype>::InitRand() {
// 要么需要镜像要么训练阶段和需要crop同时满足的情况下才初始化随机数种子
const bool needs_rand = param_.mirror() ||
(phase_ == TRAIN && param_.crop_size());
if (needs_rand) {
const unsigned int rng_seed = caffe_rng_rand();// 获得随机数种子(通过熵池或者时间生成种子)
rng_.reset(new Caffe::RNG(rng_seed));//初始化随机数种子并实例化随机数生成器
} else {
rng_.reset();//否则随机数生成器设置为空
}
} // 产生从0到n的随机数
template <typename Dtype>
int DataTransformer<Dtype>::Rand(int n) {
CHECK(rng_);
CHECK_GT(n, );
caffe::rng_t* rng =
static_cast<caffe::rng_t*>(rng_->generator());
return ((*rng)() % n);
} INSTANTIATE_CLASS(DataTransformer);
/*
初始化类的宏定义
#define INSTANTIATE_CLASS(classname) \
char gInstantiationGuard##classname; \
template class classname<float>; \
template class classname<double>
*/
} // namespace caffe

摘抄参考赵永科《21天实战caffe》

同时参考http://blog.csdn.net/langb2014/article/details/51050213

【caffe I/O】数据变换器(图像的预处理部分) 代码注释的更多相关文章

  1. 【撸码caffe 五】数据层搭建

    caffe.cpp中的train函数内声明了一个类型为Solver类的智能指针solver: // Train / Finetune a model. int train() { -- shared_ ...

  2. caffe添加python数据层

    caffe添加python数据层(ImageData) 在caffe中添加自定义层时,必须要实现这四个函数,在C++中是(LayerSetUp,Reshape,Forward_cpu,Backward ...

  3. 【caffe Blob】caffe中与Blob相关的代码注释、使用举例

    首先,Blob使用的小例子(通过运行结果即可知道相关功能): #include <vector> #include <caffe/blob.hpp> #include < ...

  4. json转换数据后面参数要带ture,代码

    强大的PHP已经提供了内置函数:json_encode() 和 json_decode().很容易理解,json_encode()就是将PHP数组转换成Json.相反,json_decode()就是将 ...

  5. 使用gfortran将数据写成Grads格式的代码示例

    使用gfortran将数据写成Grads格式的代码示例: !-----'Fortran4Grads.f90' program Fortran4Grads implicit none integer,p ...

  6. c#将Excel数据导入到数据库的实现代码

    这篇文章主要介绍了c#将Excel数据导入到数据库的实现代码,有需要的朋友可以参考一下 假如Excel中的数据如下: 数据库建表如下: 其中Id为自增字段: 代码: 代码如下: using Syste ...

  7. Hadoop基础-HDFS数据清理过程之校验过程代码分析

    Hadoop基础-HDFS数据清理过程之校验过程代码分析 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 想称为一名高级大数据开发工程师,不但需要了解hadoop内部的运行机制,还需 ...

  8. MySQL 数据备份与还原的示例代码

    MySQL 数据备份与还原的示例代码 这篇文章主要介绍了MySQL 数据备份与还原的相关知识,本文通过示例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下 一.数据备份 1.使用 ...

  9. 07.深入浅出 Spring Boot - 数据访问之Mybatis(附代码下载)

    MyBatis 在Spring Boot应用非常广,非常强大的一个半自动的ORM框架. 代码下载:https://github.com/Jackson0714/study-spring-boot.gi ...

  10. POI 导入excel数据自动封装成model对象--代码分析

    上完代码后,对代码进行主要的分析: 1.主要使用反射api将数数据注入javabean对象 2.代码中的日志信息级别为debug级别 3.获取ExcelImport对象后需要调用init()方法初始化 ...

随机推荐

  1. CSSTab栏下划线跟随效果

    神奇的 ~ 选择符 对于当前 hover 的 li ,其对应伪元素的下划线的定位是 left: 100%,而对于 li:hover ~ li::before,它们的定位是 left: 0. ul li ...

  2. MySQL比较时间(datetime)大小

    获取时间返回的秒数:strtotime('2019-05-10 00:00:00') 遇到一个业务功能需要将当前时间与数据库中的会议开始.结束时间进行比较,记录一下,方便下次用. 用unix_time ...

  3. Appscan漏洞之跨站点请求伪造(CSRF)

    公司前段时间使用了Fortify扫描项目代码,在修复完这些Fortify漏洞后,最近又启用了Appscan对项目代码进行漏洞扫描,同样也是安排了本人对这些漏洞进行修复.现在,针对修复过的Appscan ...

  4. 一段让人瑟瑟发抖的ABAP代码

    昨天11月1日是万圣节,Jerry在继续忙着调研SAP Commerce Cloud里的产品主数据管理.晚上回家到SAP国外的社交媒体上一看,好热闹啊.国外的SAP从业者们纷纷以各种各样的方式庆祝万圣 ...

  5. p5.BTC-网络

    Bitcoin工作在应用层,网络层是P2P . Bitcoin网络通信的设计原则是 simple  robust ,but not efficient. 每个节点维护一个邻居节点的集合,消息传播采取 ...

  6. Python 网络爬虫的常用库汇总

    爬虫的编程语言有不少,但 Python 绝对是其中的主流之一.下面就为大家介绍下 Python 在编写网络爬虫常常用到的一些库. 请求库:实现 HTTP 请求操作 urllib:一系列用于操作URL的 ...

  7. [LeetCode]1252. Cells with Odd Values in a Matrix

    Given n and m which are the dimensions of a matrix initialized by zeros and given an array indices w ...

  8. Linux GRUB手动安装方法详解

    需要手工安装 GRUB 主要有两种情况: Linux 系统原先不是使用 GRUB 作为引导程序而现在想要使用 GRUB 来作为引导程序: MBR 中的引导程序被覆盖,需要在 MBR 中重新安装 GRU ...

  9. django框架介绍安装-自写框架

    原文链接:https://www.cnblogs.com/maple-shaw/p/8862330.html Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户 ...

  10. ServicePointManager 类

    地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.net.servicepointmanager?redirectedfrom=MSDN&am ...