功能:
計算訓練數據庫的平均圖像。
因爲平均歸一化訓練圖像會對結果有提升,所以Caffe裏面,提供了一個可選項。
使用方法:
compute_image_mean [FLAGS] INPUT_DB [OUTPUT_FILE]\n”)
參數:INPUT_DB: 數據庫
參數(可選):OUTPUT_FILE: 輸出文件名,不提供的話,不保存平均圖像blob
實現方法:
數據源:求平均圖像的方法是直接從數據庫(LevelDB或者LMDB)裏面直接讀取出來的,而不是直接用圖像數據庫裏面求出,意味着,必須先進行圖像到數據庫的轉換後,才能求平均圖像這一步。
接下來就是遍歷KV數據庫的每一個值while (cursor->valid())
將每一個數據值轉換爲Datum,datum.ParseFromString(cursor->value());
接着將Datum階碼到sum_blob
中,sum_blob
是一個num=1,channels=圖像.channel,height=圖像.height ,width=圖像.width 的blob
累加:
sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]);
最後求平均:
sum_blob.set_data(i, sum_blob.data(i) / count);
存在的問題:上述代碼只是先累加在處於數目求和,顯然,如果需要求平均的圖像的數目相當多的話,就有可能溢出(浮點溢出),
最後,如果要求簡單一點的話,也可以直接求每個通道的平均值。
源代碼://2015.06.04版本
#include <stdint.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "boost/scoped_ptr.hpp"
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/db.hpp"
#include "caffe/util/io.hpp"
using namespace caffe; // NOLINT(build/namespaces)
using std::max;
using std::pair;
using boost::scoped_ptr;
DEFINE_string(backend, "lmdb",
"The backend {leveldb, lmdb} containing the images");
int main(int argc, char** argv) {
::google::InitGoogleLogging(argv[0]);
#ifndef GFLAGS_GFLAGS_H_
namespace gflags = google;
#endif
gflags::SetUsageMessage("Compute the mean_image of a set of images given by"
" a leveldb/lmdb\n"
"Usage:\n"
" compute_image_mean [FLAGS] INPUT_DB [OUTPUT_FILE]\n");
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (argc < 2 || argc > 3) {
gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/compute_image_mean");
return 1;
}
scoped_ptr<db::DB> db(db::GetDB(FLAGS_backend));
db->Open(argv[1], db::READ);
scoped_ptr<db::Cursor> cursor(db->NewCursor());
BlobProto sum_blob;
int count = 0;
// load first datum
Datum datum;
datum.ParseFromString(cursor->value());
if (DecodeDatumNative(&datum)) {
LOG(INFO) << "Decoding Datum";
}
sum_blob.set_num(1);
sum_blob.set_channels(datum.channels());
sum_blob.set_height(datum.height());
sum_blob.set_width(datum.width());
const int data_size = datum.channels() * datum.height() * datum.width();
int size_in_datum = std::max<int>(datum.data().size(),
datum.float_data_size());
for (int i = 0; i < size_in_datum; ++i) {
sum_blob.add_data(0.);
}
LOG(INFO) << "Starting Iteration";
while (cursor->valid()) {
Datum datum;
datum.ParseFromString(cursor->value());
DecodeDatumNative(&datum);
const std::string& data = datum.data();
size_in_datum = std::max<int>(datum.data().size(),
datum.float_data_size());
CHECK_EQ(size_in_datum, data_size) << "Incorrect data field size " <<
size_in_datum;
if (data.size() != 0) {
CHECK_EQ(data.size(), size_in_datum);
for (int i = 0; i < size_in_datum; ++i) {
sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]);
}
} else {
CHECK_EQ(datum.float_data_size(), size_in_datum);
for (int i = 0; i < size_in_datum; ++i) {
sum_blob.set_data(i, sum_blob.data(i) +
static_cast<float>(datum.float_data(i)));
}
}
++count;
if (count % 10000 == 0) {
LOG(INFO) << "Processed " << count << " files.";
}
cursor->Next();
}
if (count % 10000 != 0) {
LOG(INFO) << "Processed " << count << " files.";
}
for (int i = 0; i < sum_blob.data_size(); ++i) {
sum_blob.set_data(i, sum_blob.data(i) / count);
}
// Write to disk
if (argc == 3) {
LOG(INFO) << "Write to " << argv[2];
WriteProtoToBinaryFile(sum_blob, argv[2]);
}
const int channels = sum_blob.channels();
const int dim = sum_blob.height() * sum_blob.width();
std::vector<float> mean_values(channels, 0.0);
LOG(INFO) << "Number of channels: " << channels;
for (int c = 0; c < channels; ++c) {
for (int i = 0; i < dim; ++i) {
mean_values[c] += sum_blob.data(dim * c + i);
}
LOG(INFO) << "mean_value channel [" << c << "]:" << mean_values[c] / dim;
}
return 0;
}