Caffe4——计算图像均值

Caffe4——计算图像均值

均值削减是数据预处理中常见的处理方式,按照之前在学习ufldl教程PCA的一章时,对于图像介绍了两种:第一种常用的方式叫做dimension_mean(个人命名),是依据输入数据的维度,每个维度内进行削减,这个也是常见的做法;第二种叫做per_image_mean,ufldl教程上说,在natural images上训练网络时;给每个像素(这里只每个dimension)计算一个独立的均值和方差是make little sense的;这是因为图像本身具有统计不变性,即在图像的一部分的统计特性和另一部分相同。作者最后建议,如果你训练你的算法在非natural images(如mnist,或者在白背景存在单个独立的物体),其他类型的规则化是值得考虑的。但是当在natural images上训练时,per_image_mean是一个合理的默认选择。

本文中在imagenet数据集上采用的是dimension_mean的方法。

一:程序开始

make_image_mean.sh文件调用代码:

[cpp] view plaincopy

  1. EXAMPLE=examples/imagenet
  2. DATA=data/ilsvrc12
  3. TOOLS=build/tools
  4. $TOOLS/compute_image_mean $EXAMPLE/ilsvrc12_train_lmdb \
  5. $DATA/imagenet_mean.binaryproto<strong>
  6. </strong>

二:make_image_mean.cpp函数分析

输入参数:lmdb文件 均值文件imagenet_mean.binaryproto

2.1 头文件分析

[cpp] view plaincopy

  1. #include<stdint.h>//定义了几种扩展的整数类型和宏
  2. #include<algorithm>//输出数组的内容、对数组进行排序、反转数组内容、复制数组内容等操作,
  3. #include<string>
  4. #include<utility>//utility头文件定义了一个pair类型,pair类型用于存储一对数据;它也提供一些常用的便利函数、或类、或模板。大小求值、值交换:min、max和swap。
  5. #include<vector>//可以自动扩展容量的数组
  6. #include"boost/scoped_ptr.hpp"
  7. #include"gflags/gflags.h"
  8. #include"glog/logging.h"
  9. #include"caffe/proto/caffe.pb.h"
  10. #include"caffe/util/db.hpp"//引入包装好的lmdb操作函数
  11. #include"caffe/util/io.hpp"//引入opencv中的图像操作函数
  12. usingnamespacecaffe;  //引入caffe命名空间
  13. usingstd::max;//
  14. usingstd::pair;
  15. using boost::scoped_ptr;

2.2 gflags宏定义string变量

DEFINE_string(backend, "lmdb","The backend {leveldb, lmdb} containing theimages");

2.3 main函数分析

2.3.1 lmdb数据操作

[cpp] view plaincopy

  1. scoped_ptr<db::DB>db(db::GetDB(FLAGS_backend));
  2. db->Open(argv[1], db::READ);//只读的方式打开lmdb文件
  3. scoped_ptr<db::Cursor> cursor(db->NewCursor());
  4. //lmdb数据库的“光标”文件,一个光标保存一个从数据库根目录到数据库文件的路径;A cursorholds a path of (page pointer, key index) from the DB root to a position in theDB, plus other state.

2.3.4 声明中转对象变量

[cpp] view plaincopy

  1. BlobProtosum_blob;//声明blob变量;这个BlobProto在哪里定义的,没有找到;感觉应该在caffe.pb.h中定义的,因为db.cpp和io.cpp中没有找到
  2. int count = 0;
  3. // load first datum
  4. Datum datum;
  5. datum.ParseFromString(cursor->value());//这个cursor.value,感觉返回的应该是lmdb中存储的第一个键值对数据

2.3.5 给BlobProto类型变量赋值

每个blob对象,为一个4维的数组,分别为image_num*channels*height*width

[cpp] view plaincopy

  1. sum_blob.set_num(1);//设置图片的个数
  2. sum_blob.set_channels(datum.channels());
  3. sum_blob.set_height(datum.height());
  4. sum_blob.set_width(datum.width());
  5. constintdata_size = datum.channels() *datum.height() * datum.width();//每张图片的尺寸
  6. intsize_in_datum = std::max<int>(datum.data().size(),datum.float_data_size());

这个size()和float_data_size()有些不明白,图像数据正常应该是整形的数据(例如uint8_t),感觉这个size()应该对应的是整型数据的个数,例如一个50*50的彩色图片,最后应该是50*50*3=750个整型数来表示一幅50*50的图片;至于这个float_data_size()就不清楚了,感觉是某些图片数据使用float类型存储的,所以用float来统计数值的个数。开始感觉这个float的size应该是把int类型转换成float后,查看在float类型下的字节占用情况;但是由下面的代码来看,感觉这个size(),统计的是数据的个数也就是750,而不是占用的字节数。如果图像使用int类型存储的,那么float_data_size()=0;如果使用float类型存储的,那么datum.data.size=0。所以每次都要max操作

[cpp] view plaincopy

  1. for (inti= 0; i<size_in_datum; ++i) {
  2. sum_blob.add_data(0.);//设置初值为float型的0.0
  3. }

2.3.6利用循环和cursor读取lmdb中的数据

[cpp] view plaincopy

  1. while (cursor->valid()) {//如果cursor是有效的
  2. Datum datum;
  3. datum.ParseFromString(cursor->value());//解析cuisor.value返回的字符串值,到datum
  4. DecodeDatumNative(&datum);//感觉是把datum中字符串类型的值,变成相应的类型
  5. conststd::string& data =datum.data();//利用data来引用datum.data
  6. size_in_datum = std::max<int>(datum.data().size(),datum.float_data_size());
  7. CHECK_EQ(size_in_datum,data_size) <<"Incorrect data field size"<<size_in_datum;
  8. if (data.size() != 0) {//datum.data().size()!=0
  9. CHECK_EQ(data.size(),size_in_datum);//判断是否相等
  10. for (inti= 0; i<size_in_datum; ++i) {
  11. sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]);//对应位置的像素值相加(uin8_t类型相加),相加的结果放在sum_blob中
  12. }
  13. } else{
  14. CHECK_EQ(datum.float_data_size(), size_in_datum);
  15. for (inti= 0; i<size_in_datum; ++i) {
  16. sum_blob.set_data(i, sum_blob.data(i) +
  17. static_cast<float>(datum.float_data(i)));//对应位置的像素值相加(float类型相加)
  18. }
  19. }
  20. ++count;
  21. if (count % 10000 == 0) {
  22. LOG(INFO) <<"Processed "<<count <<" files.";
  23. }
  24. cursor->Next();//光标下移(指针),指向下一个存储在lmdb中的数据
  25. }

2.3.7 求均值

[cpp] view plaincopy

  1. for (inti= 0; i<sum_blob.data_size(); ++i) {
  2. sum_blob.set_data(i, sum_blob.data(i) / count);
  3. }

2.3.8 存储到指定文件

[cpp] view plaincopy

  1. // Write to disk
  2. if (argc == 3) {
  3. LOG(INFO) <<"Write to "<<argv[2];
  4. WriteProtoToBinaryFile(sum_blob, argv[2]);
  5. }

2.3.9 计算每个channel的均值,这个貌似没有用到吧!

[cpp] view plaincopy

  1. constint channels = sum_blob.channels();
  2. constint dim = sum_blob.height() *sum_blob.width();
  3. std::vector<float>mean_values(channels,0.0);//容量为3的数组,初始值为0.0
  4. LOG(INFO) <<"Number of channels:"<< channels;
  5. for (intc = 0; c < channels; ++c) {
  6. for (inti= 0; i< dim; ++i) {
  7. mean_values[c] += sum_blob.data(dim * c + i);
  8. }
  9. LOG(INFO) <<"mean_value channel["<< c <<"]:"<<mean_values[c]/ dim;
  10. }

三,相关文件

compute_image_mean.cpp

[cpp] view plaincopy

  1. #include <stdint.h>
  2. #include <algorithm>
  3. #include <string>
  4. #include <utility>
  5. #include <vector>
  6. #include "boost/scoped_ptr.hpp"
  7. #include "gflags/gflags.h"
  8. #include "glog/logging.h"
  9. #include "caffe/proto/caffe.pb.h"
  10. #include "caffe/util/db.hpp"
  11. #include "caffe/util/io.hpp"
  12. using namespace caffe;  // NOLINT(build/namespaces)
  13. using std::max;
  14. using std::pair;
  15. using boost::scoped_ptr;
  16. DEFINE_string(backend, "lmdb",
  17. "The backend {leveldb, lmdb} containing the images");
  18. int main(int argc, char** argv) {
  19. ::google::InitGoogleLogging(argv[0]);
  20. #ifndef GFLAGS_GFLAGS_H_
  21. namespace gflags = google;
  22. #endif
  23. gflags::SetUsageMessage("Compute the mean_image of a set of images given by"
  24. " a leveldb/lmdb\n"
  25. "Usage:\n"
  26. "    compute_image_mean [FLAGS] INPUT_DB [OUTPUT_FILE]\n");
  27. gflags::ParseCommandLineFlags(&argc, &argv, true);
  28. if (argc < 2 || argc > 3) {
  29. gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/compute_image_mean");
  30. return 1;
  31. }
  32. scoped_ptr<db::DB> db(db::GetDB(FLAGS_backend));
  33. db->Open(argv[1], db::READ);
  34. scoped_ptr<db::Cursor> cursor(db->NewCursor());
  35. BlobProto sum_blob;
  36. int count = 0;
  37. // load first datum
  38. Datum datum;
  39. datum.ParseFromString(cursor->value());
  40. if (DecodeDatumNative(&datum)) {
  41. LOG(INFO) << "Decoding Datum";
  42. }
  43. sum_blob.set_num(1);
  44. sum_blob.set_channels(datum.channels());
  45. sum_blob.set_height(datum.height());
  46. sum_blob.set_width(datum.width());
  47. const int data_size = datum.channels() * datum.height() * datum.width();
  48. int size_in_datum = std::max<int>(datum.data().size(),datum.float_data_size());
  49. for (int i = 0; i < size_in_datum; ++i) {
  50. sum_blob.add_data(0.);//设置初值为float型的0.0
  51. }
  52. LOG(INFO) << "Starting Iteration";
  53. while (cursor->valid()) {//如果cursor是有效的
  54. Datum datum;
  55. datum.ParseFromString(cursor->value());//解析cuisor.value返回的字符串值,到datum
  56. DecodeDatumNative(&datum);
  57. const std::string& data = datum.data();//利用data来引用datum.data
  58. size_in_datum = std::max<int>(datum.data().size(),datum.float_data_size());
  59. CHECK_EQ(size_in_datum, data_size) << "Incorrect data field size " <<size_in_datum;
  60. if (data.size() != 0) {
  61. CHECK_EQ(data.size(), size_in_datum);
  62. for (int i = 0; i < size_in_datum; ++i) {
  63. sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]);
  64. }
  65. } else {
  66. CHECK_EQ(datum.float_data_size(), size_in_datum);
  67. for (int i = 0; i < size_in_datum; ++i) {
  68. sum_blob.set_data(i, sum_blob.data(i) +
  69. static_cast<float>(datum.float_data(i)));
  70. }
  71. }
  72. ++count;
  73. if (count % 10000 == 0) {
  74. LOG(INFO) << "Processed " << count << " files.";
  75. }
  76. cursor->Next();
  77. }
  78. if (count % 10000 != 0) {
  79. LOG(INFO) << "Processed " << count << " files.";
  80. }
  81. for (int i = 0; i < sum_blob.data_size(); ++i) {
  82. sum_blob.set_data(i, sum_blob.data(i) / count);
  83. }
  84. // Write to disk
  85. if (argc == 3) {
  86. LOG(INFO) << "Write to " << argv[2];
  87. WriteProtoToBinaryFile(sum_blob, argv[2]);
  88. }
  89. const int channels = sum_blob.channels();
  90. const int dim = sum_blob.height() * sum_blob.width();
  91. std::vector<float> mean_values(channels, 0.0);
  92. LOG(INFO) << "Number of channels: " << channels;
  93. for (int c = 0; c < channels; ++c) {
  94. for (int i = 0; i < dim; ++i) {
  95. mean_values[c] += sum_blob.data(dim * c + i);
  96. }
  97. LOG(INFO) << "mean_value channel [" << c << "]:" << mean_values[c] / dim;
  98. }
  99. return 0;
  100. }

四:以上代码注释为个人理解,如有遗漏,错误还望大家多多交流,指正,以便共同学习,进步!!

转载请标明出处:http://blog.csdn.net/whiteinblue/article/details/45540301

时间: 2024-08-30 02:42:14

Caffe4——计算图像均值的相关文章

利用matlab求图像均值和方差的几种方法

一.求均值 % 求一副灰度图像的均值 close all; clear; clc; i=imread('d:/lena.jpg'); %载入真彩色图像 i=rgb2gray(i); %转换为灰度图 i=double(i); %将uint8型转换为double型,否则不能计算统计量 % avg1=mean(i,1); %列向量均值 % avg2=mean(i,2); %行向量均值 % avg3=mean(i); %列向量均值 [m,n]=size(i); s=0; for x=1:m for y=

OpenCV 学习(计算图像的直方图)

OpenCV 计算图像的直方图 计算图像的直方图是图像处理领域一个非常常见的基本操作. OpenCV 中提供了 calcHist 函数来计算图像直方图.不过这个函数说实话挺难用的,研究了好久才掌握了些基本的用法. calcHist 函数 C++ 的函数原型如下: void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, SparseMat& hist, int dims, const i

OpenCV2+入门系列(四):计算图像的直方图,平均灰度,灰度方差

本篇懒得排版,直接在网页html编辑器编辑 在图像处理时,我们常常需要求出图像的直方图.灰度平均值.灰度的方差,这里给出一个opencv2+自带程序,实现这些功能. 直方图 对于直方图,使用cv::calcHist函数可以求出. 原型 void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize,

计算图像相似度——《Python也可以》之一

声明:本文最初发表于赖勇浩(恋花蝶)的博客http://blog.csdn.net/lanphaday 先将两张图片转化为直方图,图像的相似度计算就转化为直方图的距离计算了,本文依照如下公式进行直方图相似度的定量度量: Sim(G,S)= 其中G,S为直方图,N 为颜色空间样点数 转换为相应的 Python 代码如下: #!/usr/bin/env python # coding=utf-8 import Image def make_regalur_image(img,size=(256,25

采用调色板保存图像,计算图像大小

例:一幅\(200\times200\)的16色图像,采用调色板保存需要\(2\times10^4\)个字节. 解释: 通常,我们保存RGB图像,保存的是256色图像,举一反三,16色图像的计算方式和RGB一样. 对于RGB图像,R(红)需要用0~255共256个数字(256色)表示,即需要用8个二进制位表示. G(绿)需要用0~255共256个数字(256色)表示,即需要用8个二进制位表示. B(蓝)需要用0~255共256个数字(256色)表示,即需要用8个二进制位表示. 那么,一共需要8*

一种计算图像曝光度(Exposure)的方法

前几天在做图像曝光度(Exposure)这个小功能时,找了半天资料都没找到,后来在stackoverflow上翻到了计算方法,方法很简单,实际测试时,发现和photoshop效果还挺接近的.下面是基于opencv的相关代码示例,strength范围可以设置为-2.0到2.0: // strength范围[-2.0, 2.0] int row = inputImg.rows; int step = inputImg.step; uchar* pInputImg = inputImg.data; u

caffe 图片数据的转换成lmdb和数据集均值(转)

转自网站: http://blog.csdn.net/muyiyushan/article/details/70578077 1.准备数据 使用dog/cat数据集,在训练项目根目录下分别建立train和val文件夹,作为训练数据和验证数据的保存位置.train和val文件夹下各有两个文件夹:dogs和cats,分别保存dog和cat的图片.dog和cat分别有1000张训练图像和400张测试图像. 写一个python脚本文件,遍历train和val两个文件夹,分别生成train.txt和val

深度学习人脸识别实验---VGG模型

特别说明:本次实验步骤大部分来源于http://blog.csdn.net/wjmishuai/article/details/50658670 1.caffe环境配置 主要参考:<深度学习 21天实战Caffe> 2.VGG人脸识别模型资料(提供论文和以及训练完的人脸模型) http://www.robots.ox.ac.uk/~vgg/software/vgg_face/ 3.LMDB数据集的获取 数据集划分 #保存图片的路径 PATH=/media/img echo "star

车牌识别(一)-车牌定位

在对车牌识别过程中,常用的方法有:基于形状.基于色调.基于纹理.基于文字特征等方法.首先基于形状,在车牌中因为车牌为形状规格的矩形,所以目的转化为寻找矩形特征,常常是利用车牌长宽比例特征.占据图像的比例等.基于色调,国内的车牌往往是蓝底白字,可以采用图像的色调或者饱和度特征,进入生成二值图,定位车牌位置.基于纹理特征自己还没有基础到.基于文字特征往往是根据文字轮廓特征进行识别,原理是基于相邻文字轮廓特征.比例进行定位车牌位置. 一.图像二值化 正如前面文章所言,首先进行获取图像二值化特征,本文采