From f59c9b74fc27ed5f53f488cdca58ccd042dfb1ca Mon Sep 17 00:00:00 2001 From: superjom Date: Mon, 25 Dec 2017 18:47:40 +0800 Subject: [PATCH 01/29] add image --- visualdl/logic/sdk.cc | 65 ++++++++++++++++++++++++++++++++++++++ visualdl/logic/sdk.h | 35 ++++++++++++++++++++ visualdl/logic/sdk_test.cc | 25 +++++++++++++++ visualdl/storage/entry.cc | 17 ++++++++++ visualdl/storage/entry.h | 8 +++-- visualdl/storage/record.h | 8 +++++ 6 files changed, 156 insertions(+), 2 deletions(-) diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 62c46ea6d..e8a801ed3 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -47,6 +47,71 @@ template class ScalarReader; template class ScalarReader; template class ScalarReader; +void Image::StartSampling() { + step_ = writer_.AddRecord(); + num_records_ = 0; +} + +int Image::IsSampleTaken() { + num_records_++; + if (num_records_ <= num_samples_) { + return num_records_ - 1; + } + float prob = float(num_samples_) / num_records_; + float thre = (float)rand() / RAND_MAX; + if (prob < thre) { + // take this sample + int index = rand() % num_samples_; + return index; + } + return -1; +} + +void Image::FinishSampling() { + // TODO(ChunweiYan) much optimizement here. + writer_.parent()->PersistToDisk(); +} + +template +struct is_same_type { + static const bool value = false; +}; +template +struct is_same_type { + static const bool value = true; +}; + +void Image::SetSample(int index, + const std::vector& shape, + const std::vector& data) { + // production + int size = std::accumulate( + shape.begin(), shape.end(), 1., [](float a, float b) { return a * b; }); + CHECK_EQ(size, data.size()) << "image's shape not match data"; + CHECK_LT(index, num_samples_); + CHECK_LE(index, num_records_); + + // set data + Entry entry; + if (index == num_records_) { + // add one entry + entry = step_.AddData(); + } else { + entry = step_.MutableData(index); + } + entry.SetMulti(data); + + static_assert( + !is_same_type::value, + "value_t should not use int64_t field, this type is used to store shape"); + + // set meta with hack + Entry meta; + meta.set_parent(entry.parent()); + meta.entry = entry.entry; + meta.SetMulti(shape); +} + } // namespace components } // namespace visualdl diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index 66135d23d..1c8cad6c9 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -140,6 +140,41 @@ struct ScalarReader { TabletReader reader_; }; +/* + * Image component writer. + */ +struct Image { + using value_t = float; + + Image(Tablet tablet, int num_samples) : writer_(tablet) { + writer_.SetType(Tablet::Type::kImage); + writer_.SetNumSamples(num_samples); + num_samples_ = num_samples; + } + /* + * Start a sample period. + */ + void StartSampling(); + /* + * Will this sample will be taken. + */ + int IsSampleTaken(); + /* + * End a sample period. + */ + void FinishSampling(); + + void SetSample(int index, + const std::vector& shape, + const std::vector& data); + +private: + Tablet writer_; + Record step_; + int num_records_{0}; + int num_samples_{0}; +}; + } // namespace components } // namespace visualdl diff --git a/visualdl/logic/sdk_test.cc b/visualdl/logic/sdk_test.cc index 3a2b2f5ef..8529ffd6c 100644 --- a/visualdl/logic/sdk_test.cc +++ b/visualdl/logic/sdk_test.cc @@ -2,6 +2,8 @@ #include +using namespace std; + namespace visualdl { TEST(Scalar, write) { @@ -40,4 +42,27 @@ TEST(Scalar, write) { ASSERT_EQ(scalar_reader1.caption(), "customized caption"); } +TEST(Image, test) { + const auto dir = "./tmp/sdk_test.image"; + Writer writer__(dir, 1); + auto writer = writer__.AsMode("train"); + + auto tablet = writer.AddTablet("image0"); + components::Image image(tablet, 3); + + image.StartSampling(); + for (int i = 0; i < 100; i++) { + vector shape({3, 5, 5}); + vector data; + for (int j = 0; j < 3 * 5 * 5; j++) { + data.push_back(float(rand()) / RAND_MAX); + } + int index = image.IsSampleTaken(); + if (index != -1) { + image.SetSample(index, shape, data); + } + } + image.FinishSampling(); +} + } // namespace visualdl diff --git a/visualdl/storage/entry.cc b/visualdl/storage/entry.cc index 0d4fc8dd3..4e1a29c7c 100644 --- a/visualdl/storage/entry.cc +++ b/visualdl/storage/entry.cc @@ -10,6 +10,17 @@ namespace visualdl { WRITE_GUARD \ } +#define IMPL_ENTRY_SETMUL(ctype__, dtype__, field__) \ + template <> \ + void Entry::SetMulti(const std::vector& vs) { \ + entry->set_dtype(storage::DataType::dtype__); \ + entry->clear_##field__(); \ + for (auto v : vs) { \ + entry->add_##field__(v); \ + } \ + WRITE_GUARD \ + } + IMPL_ENTRY_SET_OR_ADD(Set, int, kInt32, set_i32); IMPL_ENTRY_SET_OR_ADD(Set, int64_t, kInt64, set_i64); IMPL_ENTRY_SET_OR_ADD(Set, bool, kBool, set_b); @@ -22,6 +33,12 @@ IMPL_ENTRY_SET_OR_ADD(Add, double, kDoubles, add_ds); IMPL_ENTRY_SET_OR_ADD(Add, std::string, kStrings, add_ss); IMPL_ENTRY_SET_OR_ADD(Add, bool, kBools, add_bs); +IMPL_ENTRY_SETMUL(int, kInt32, i32s); +IMPL_ENTRY_SETMUL(int64_t, kInt64, i64s); +IMPL_ENTRY_SETMUL(float, kFloat, fs); +IMPL_ENTRY_SETMUL(double, kDouble, ds); +IMPL_ENTRY_SETMUL(bool, kBool, bs); + #define IMPL_ENTRY_GET(T, fieldname__) \ template <> \ T EntryReader::Get() const { \ diff --git a/visualdl/storage/entry.h b/visualdl/storage/entry.h index 060b03827..ba9542def 100644 --- a/visualdl/storage/entry.h +++ b/visualdl/storage/entry.h @@ -19,8 +19,9 @@ struct Entry { storage::Entry* entry{nullptr}; Entry() {} - explicit Entry(storage::Entry* entry, Storage* parent) - : entry(entry), x_(parent) {} + Entry(storage::Entry* entry, Storage* parent) : entry(entry), x_(parent) {} + Entry(const Entry& other) : entry(other.entry), x_(other.x_) {} + void operator()(storage::Entry* entry, Storage* parent) { this->entry = entry; x_ = parent; @@ -32,7 +33,10 @@ struct Entry { // Add a value to repeated message field. void Add(T v); + void SetMulti(const std::vector& v); + Storage* parent() { return x_; } + void set_parent(Storage* x) { x_ = x; } private: Storage* x_; diff --git a/visualdl/storage/record.h b/visualdl/storage/record.h index 4e5fc7fd9..31fae1bb9 100644 --- a/visualdl/storage/record.h +++ b/visualdl/storage/record.h @@ -30,7 +30,9 @@ struct Record { DECL_GUARD(Record) + Record() {} Record(storage::Record* x, Storage* parent) : data_(x), x_(parent) {} + Record(const Record& other) : data_(other.data_), x_(other.x_) {} // write operations void SetTimeStamp(int64_t x) { @@ -59,6 +61,12 @@ struct Record { return Entry(data_->add_data(), parent()); } + template + Entry MutableData(int i) { + WRITE_GUARD + return Entry(data_->mutable_data(i), parent()); + } + Storage* parent() { return x_; } private: From 7a08e5e9c8330490003d1238d99531e851610080 Mon Sep 17 00:00:00 2001 From: superjom Date: Mon, 25 Dec 2017 19:56:14 +0800 Subject: [PATCH 02/29] Squashed commit of the following: commit eb6d0cce9eaeb61549593914d8f7d667a51b02ae Author: superjom Date: Mon Dec 25 19:48:28 2017 +0800 code format commit 353e1028393acab4166bf0fa14484b3d91fa07d9 Author: superjom Date: Mon Dec 25 19:47:46 2017 +0800 restore --- frontend/package.json | 5 ++++- frontend/tool/build.js | 1 - frontend/tool/entry.js | 1 - frontend/tool/webpack.config.js | 1 - frontend/tool/webpack.prod.config.js | 1 - visualdl/python/test_storage.py | 4 +++- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 5cd4c1f81..57dc9b6da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,7 +8,9 @@ "release": "cross-env NODE_ENV=production node ./tool/build.js", "build": "cross-env NODE_ENV=dev node ./tool/build.js", "dev": "cross-env NODE_ENV=dev node tool/dev-server.js", - "lint": "./node_modules/fecs/bin/fecs --rule" + "lint": "./node_modules/fecs/bin/fecs --rule", + "precommit": "npm run lint", + "prepush": "npm run lint" }, "engines": { "node": ">= 6.4.0" @@ -52,6 +54,7 @@ "html-loader": "^0.4.4", "html-webpack-plugin": "^2.28.0", "http-proxy-middleware": "^0.17.4", + "husky": "^0.14.3", "json-loader": "^0.5.4", "opn": "^5.1.0", "optimize-css-assets-webpack-plugin": "^1.3.2", diff --git a/frontend/tool/build.js b/frontend/tool/build.js index 2590b8871..d8a67b1f1 100644 --- a/frontend/tool/build.js +++ b/frontend/tool/build.js @@ -1,4 +1,3 @@ -"use strict"; const webpack = require('webpack'); const rm = require('rimraf'); const ora = require('ora'); diff --git a/frontend/tool/entry.js b/frontend/tool/entry.js index aba7919bc..78754c6a5 100644 --- a/frontend/tool/entry.js +++ b/frontend/tool/entry.js @@ -1,4 +1,3 @@ -"use strict"; const path = require('path'); const projectPath = path.resolve(__dirname, '..'); const HtmlWebpackPlugin = require('html-webpack-plugin'); diff --git a/frontend/tool/webpack.config.js b/frontend/tool/webpack.config.js index 90115251f..24764208c 100644 --- a/frontend/tool/webpack.config.js +++ b/frontend/tool/webpack.config.js @@ -1,4 +1,3 @@ -"use strict"; const webpack = require('webpack'); const path = require('path'); const projectPath = path.resolve(__dirname, '..'); diff --git a/frontend/tool/webpack.prod.config.js b/frontend/tool/webpack.prod.config.js index 3d8bbbbcb..fa73c7837 100644 --- a/frontend/tool/webpack.prod.config.js +++ b/frontend/tool/webpack.prod.config.js @@ -1,4 +1,3 @@ -"use strict"; const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const path = require('path'); diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 413ac306c..328ee74ef 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -4,13 +4,15 @@ import random import time + class StorageTest(unittest.TestCase): def setUp(self): self.dir = "./tmp/storage_test" def test_read(self): print 'test write' - self.writer = storage.StorageWriter(self.dir, sync_cycle=1).as_mode("train") + self.writer = storage.StorageWriter( + self.dir, sync_cycle=1).as_mode("train") scalar = self.writer.scalar("model/scalar/min") # scalar.set_caption("model/scalar/min") for i in range(10): From 194e379275075a0a3b6530dd00ef0ff067af5725 Mon Sep 17 00:00:00 2001 From: superjom Date: Mon, 25 Dec 2017 20:48:09 +0800 Subject: [PATCH 03/29] image component ready --- visualdl/logic/sdk.cc | 34 ++++++++++++++++++++++------------ visualdl/logic/sdk.h | 31 ++++++++++++++++++++++++++++++- visualdl/logic/sdk_test.cc | 38 ++++++++++++++++++++++++++------------ visualdl/storage/entry.cc | 1 + 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index e8a801ed3..13229fae6 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -49,6 +49,10 @@ template class ScalarReader; void Image::StartSampling() { step_ = writer_.AddRecord(); + // resize record + for (int i = 0; i < num_samples_; i++) { + step_.AddData(); + } num_records_ = 0; } @@ -58,8 +62,8 @@ int Image::IsSampleTaken() { return num_records_ - 1; } float prob = float(num_samples_) / num_records_; - float thre = (float)rand() / RAND_MAX; - if (prob < thre) { + float randv = (float)rand() / RAND_MAX; + if (randv < prob) { // take this sample int index = rand() % num_samples_; return index; @@ -82,7 +86,7 @@ struct is_same_type { }; void Image::SetSample(int index, - const std::vector& shape, + const std::vector& shape, const std::vector& data) { // production int size = std::accumulate( @@ -92,26 +96,32 @@ void Image::SetSample(int index, CHECK_LE(index, num_records_); // set data - Entry entry; - if (index == num_records_) { - // add one entry - entry = step_.AddData(); - } else { - entry = step_.MutableData(index); - } + auto entry = step_.MutableData(index); entry.SetMulti(data); static_assert( - !is_same_type::value, + !is_same_type::value, "value_t should not use int64_t field, this type is used to store shape"); // set meta with hack - Entry meta; + Entry meta; meta.set_parent(entry.parent()); meta.entry = entry.entry; meta.SetMulti(shape); } +std::vector ImageReader::data(int step, int index) { + auto record = reader_.record(step); + auto entry = record.data(index); + return entry.GetMulti(); +} + +std::vector ImageReader::shape(int step, int index) { + auto record = reader_.record(step); + auto entry = record.data(index); + return entry.GetMulti(); +} + } // namespace components } // namespace visualdl diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index 1c8cad6c9..53cbf69af 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -145,12 +145,16 @@ struct ScalarReader { */ struct Image { using value_t = float; + using shape_t = int64_t; Image(Tablet tablet, int num_samples) : writer_(tablet) { writer_.SetType(Tablet::Type::kImage); writer_.SetNumSamples(num_samples); num_samples_ = num_samples; } + void SetCaption(const std::string& c) { + writer_.SetCaptions(std::vector({c})); + } /* * Start a sample period. */ @@ -165,7 +169,7 @@ struct Image { void FinishSampling(); void SetSample(int index, - const std::vector& shape, + const std::vector& shape, const std::vector& data); private: @@ -175,6 +179,31 @@ struct Image { int num_samples_{0}; }; +/* + * Image reader. + */ +struct ImageReader { + using value_t = typename Image::value_t; + using shape_t = typename Image::shape_t; + + ImageReader(TabletReader tablet) : reader_(tablet) {} + + std::string caption() { + CHECK_EQ(reader_.captions().size(), 1); + return reader_.captions().front(); + } + + // number of steps. + int num_records() { return reader_.total_records(); } + + std::vector data(int step, int index); + + std::vector shape(int step, int index); + +private: + TabletReader reader_; +}; + } // namespace components } // namespace visualdl diff --git a/visualdl/logic/sdk_test.cc b/visualdl/logic/sdk_test.cc index 8529ffd6c..d056a0e15 100644 --- a/visualdl/logic/sdk_test.cc +++ b/visualdl/logic/sdk_test.cc @@ -44,25 +44,39 @@ TEST(Scalar, write) { TEST(Image, test) { const auto dir = "./tmp/sdk_test.image"; - Writer writer__(dir, 1); + Writer writer__(dir, 4); auto writer = writer__.AsMode("train"); auto tablet = writer.AddTablet("image0"); components::Image image(tablet, 3); + const int num_steps = 10; - image.StartSampling(); - for (int i = 0; i < 100; i++) { - vector shape({3, 5, 5}); - vector data; - for (int j = 0; j < 3 * 5 * 5; j++) { - data.push_back(float(rand()) / RAND_MAX); - } - int index = image.IsSampleTaken(); - if (index != -1) { - image.SetSample(index, shape, data); + LOG(INFO) << "write images"; + image.SetCaption("this is an image"); + for (int step = 0; step < num_steps; step++) { + image.StartSampling(); + for (int i = 0; i < 7; i++) { + vector shape({3, 5, 5}); + vector data; + for (int j = 0; j < 3 * 5 * 5; j++) { + data.push_back(float(rand()) / RAND_MAX); + } + int index = image.IsSampleTaken(); + if (index != -1) { + image.SetSample(index, shape, data); + } } + image.FinishSampling(); } - image.FinishSampling(); + + LOG(INFO) << "read images"; + // read it + Reader reader__(dir); + auto reader = reader__.AsMode("train"); + auto tablet2read = reader.tablet("image0"); + components::ImageReader image2read(tablet2read); + CHECK_EQ(image2read.caption(), "this is an image"); + CHECK_EQ(image2read.num_records(), num_steps); } } // namespace visualdl diff --git a/visualdl/storage/entry.cc b/visualdl/storage/entry.cc index 4e1a29c7c..2697f7cc7 100644 --- a/visualdl/storage/entry.cc +++ b/visualdl/storage/entry.cc @@ -60,6 +60,7 @@ IMPL_ENTRY_GET(bool, b); } IMPL_ENTRY_GET_MULTI(int, i32s); +IMPL_ENTRY_GET_MULTI(int64_t, i64s); IMPL_ENTRY_GET_MULTI(float, fs); IMPL_ENTRY_GET_MULTI(double, ds); IMPL_ENTRY_GET_MULTI(std::string, ss); From 41c22081c20136af9d044934ac026b5d0e8872be Mon Sep 17 00:00:00 2001 From: superjom Date: Tue, 26 Dec 2017 13:10:58 +0800 Subject: [PATCH 04/29] add image py test --- CMakeLists.txt | 2 +- visualdl/logic/CMakeLists.txt | 5 +- visualdl/logic/pybind.cc | 89 +++++++++++++++++++++------------ visualdl/logic/sdk.h | 41 ++++++++++----- visualdl/logic/sdk_test.cc | 2 +- visualdl/python/storage.py | 6 +++ visualdl/python/test_storage.py | 27 ++++++++-- visualdl/storage/CMakeLists.txt | 2 +- visualdl/storage/storage.h | 2 +- visualdl/storage/tablet.cc | 7 +++ visualdl/storage/tablet.h | 4 ++ 11 files changed, 132 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 590abdd68..83921c41c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ add_executable(vl_test ${PROJECT_SOURCE_DIR}/visualdl/utils/concurrency.h ${PROJECT_SOURCE_DIR}/visualdl/utils/filesystem.h ) -target_link_libraries(vl_test sdk storage entry im gtest glog protobuf gflags pthread) +target_link_libraries(vl_test sdk storage entry tablet im gtest glog protobuf gflags pthread) enable_testing () diff --git a/visualdl/logic/CMakeLists.txt b/visualdl/logic/CMakeLists.txt index f7d825444..f38c9e73b 100644 --- a/visualdl/logic/CMakeLists.txt +++ b/visualdl/logic/CMakeLists.txt @@ -1,4 +1,3 @@ -#add_library(sdk ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk.cc) add_library(im ${PROJECT_SOURCE_DIR}/visualdl/logic/im.cc) add_library(sdk ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk.cc) add_dependencies(im storage_proto) @@ -6,6 +5,6 @@ add_dependencies(sdk entry storage storage_proto) ## pybind add_library(core SHARED ${PROJECT_SOURCE_DIR}/visualdl/logic/pybind.cc) -add_dependencies(core pybind python im entry storage sdk protobuf glog) -target_link_libraries(core PRIVATE pybind entry python im storage sdk protobuf glog) +add_dependencies(core pybind python im entry tablet storage sdk protobuf glog) +target_link_libraries(core PRIVATE pybind entry python im tablet storage sdk protobuf glog) set_target_properties(core PROPERTIES PREFIX "" SUFFIX ".so") diff --git a/visualdl/logic/pybind.cc b/visualdl/logic/pybind.cc index 26369794f..69d84a1da 100644 --- a/visualdl/logic/pybind.cc +++ b/visualdl/logic/pybind.cc @@ -11,28 +11,7 @@ namespace cp = visualdl::components; PYBIND11_PLUGIN(core) { py::module m("core", "C++ core of VisualDL"); -#define ADD_SCALAR(T) \ - py::class_>(m, "ScalarReader__" #T) \ - .def("records", &cp::ScalarReader::records) \ - .def("timestamps", &cp::ScalarReader::timestamps) \ - .def("ids", &cp::ScalarReader::ids) \ - .def("caption", &cp::ScalarReader::caption); - ADD_SCALAR(int); - ADD_SCALAR(float); - ADD_SCALAR(double); - ADD_SCALAR(int64_t); -#undef ADD_SCALAR - -#define ADD_SCALAR_WRITER(T) \ - py::class_>(m, "ScalarWriter__" #T) \ - .def("set_caption", &cp::Scalar::SetCaption) \ - .def("add_record", &cp::Scalar::AddRecord); - ADD_SCALAR_WRITER(int); - ADD_SCALAR_WRITER(float); - ADD_SCALAR_WRITER(double); -#undef ADD_SCALAR_WRITER - -#define ADD_SCALAR(T) \ +#define READER_ADD_SCALAR(T) \ .def("get_scalar_" #T, [](vs::Reader& self, const std::string& tag) { \ auto tablet = self.tablet(tag); \ return vs::components::ScalarReader(std::move(tablet)); \ @@ -46,13 +25,17 @@ PYBIND11_PLUGIN(core) { .def("modes", [](vs::Reader& self) { return self.storage().modes(); }) .def("tags", &vs::Reader::tags) // clang-format off - ADD_SCALAR(float) - ADD_SCALAR(double) - ADD_SCALAR(int); -// clang-format on -#undef ADD_SCALAR + READER_ADD_SCALAR(float) + READER_ADD_SCALAR(double) + READER_ADD_SCALAR(int) + // clang-format on + .def("get_image", [](vs::Reader& self, const std::string& tag) { + auto tablet = self.tablet(tag); + return vs::components::ImageReader(self.mode(), tablet); + }); +#undef READER_ADD_SCALAR -#define ADD_SCALAR(T) \ +#define WRITER_ADD_SCALAR(T) \ .def("new_scalar_" #T, [](vs::Writer& self, const std::string& tag) { \ auto tablet = self.AddTablet(tag); \ return cp::Scalar(tablet); \ @@ -65,10 +48,50 @@ PYBIND11_PLUGIN(core) { }) .def("as_mode", &vs::Writer::AsMode) // clang-format off - ADD_SCALAR(float) - ADD_SCALAR(double) - ADD_SCALAR(int); -// clang-format on -#undef ADD_SCALAR + WRITER_ADD_SCALAR(float) + WRITER_ADD_SCALAR(double) + WRITER_ADD_SCALAR(int) + // clang-format on + .def("new_image", + [](vs::Writer& self, const std::string& tag, int num_samples) { + auto tablet = self.AddTablet(tag); + return vs::components::Image(tablet, num_samples); + }); + +//------------------- components -------------------- +#define ADD_SCALAR_READER(T) \ + py::class_>(m, "ScalarReader__" #T) \ + .def("records", &cp::ScalarReader::records) \ + .def("timestamps", &cp::ScalarReader::timestamps) \ + .def("ids", &cp::ScalarReader::ids) \ + .def("caption", &cp::ScalarReader::caption); + ADD_SCALAR_READER(int); + ADD_SCALAR_READER(float); + ADD_SCALAR_READER(double); + ADD_SCALAR_READER(int64_t); +#undef ADD_SCALAR_READER + +#define ADD_SCALAR_WRITER(T) \ + py::class_>(m, "ScalarWriter__" #T) \ + .def("set_caption", &cp::Scalar::SetCaption) \ + .def("add_record", &cp::Scalar::AddRecord); + ADD_SCALAR_WRITER(int); + ADD_SCALAR_WRITER(float); + ADD_SCALAR_WRITER(double); +#undef ADD_SCALAR_WRITER + + // clang-format on + py::class_(m, "ImageWriter") + .def("set_caption", &cp::Image::SetCaption) + .def("start_sampling", &cp::Image::StartSampling) + .def("is_sample_taken", &cp::Image::IsSampleTaken) + .def("finish_sampling", &cp::Image::FinishSampling) + .def("set_sample", &cp::Image::SetSample); + + py::class_(m, "ImageReader") + .def("caption", &cp::ImageReader::caption) + .def("num_records", &cp::ImageReader::num_records) + .def("data", &cp::ImageReader::data) + .def("shape", &cp::ImageReader::shape); } // end pybind diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index 53cbf69af..4399ca356 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -32,6 +32,7 @@ class Writer { string::TagEncode(tmp); auto res = storage_.AddTablet(tmp); res.SetCaptions(std::vector({mode_})); + res.SetTag(mode_, tag); return res; } @@ -52,6 +53,8 @@ class Reader { return tmp; } + const std::string& mode() { return mode_; } + TabletReader tablet(const std::string& tag) { auto tmp = mode_ + "/" + tag; string::TagEncode(tmp); @@ -62,7 +65,7 @@ class Reader { auto tags = reader_.all_tags(); auto it = std::remove_if(tags.begin(), tags.end(), [&](const std::string& tag) { - return !TagMatchMode(tag); + return !TagMatchMode(tag, mode_); }); tags.erase(it + 1); return tags; @@ -74,8 +77,8 @@ class Reader { CHECK(!tags.empty()); std::vector res; for (const auto& tag : tags) { - if (TagMatchMode(tag)) { - res.push_back(GenReadableTag(tag)); + if (TagMatchMode(tag, mode_)) { + res.push_back(GenReadableTag(mode_, tag)); } } return res; @@ -83,17 +86,19 @@ class Reader { StorageReader& storage() { return reader_; } -protected: - bool TagMatchMode(const std::string& tag) { - if (tag.size() <= mode_.size()) return false; - return tag.substr(0, mode_.size()) == mode_; - } - std::string GenReadableTag(const std::string& tag) { + static std::string GenReadableTag(const std::string& mode, + const std::string& tag) { auto tmp = tag; string::TagDecode(tmp); - return tmp.substr(mode_.size() + 1); // including `/` + return tmp.substr(mode.size() + 1); // including `/` + } + + static bool TagMatchMode(const std::string& tag, const std::string& mode) { + if (tag.size() <= mode.size()) return false; + return tag.substr(0, mode.size()) == mode; } +protected: private: StorageReader reader_; std::string mode_{kDefaultMode}; @@ -149,7 +154,9 @@ struct Image { Image(Tablet tablet, int num_samples) : writer_(tablet) { writer_.SetType(Tablet::Type::kImage); + // make image's tag as the default caption. writer_.SetNumSamples(num_samples); + SetCaption(tablet.reader().tag()); num_samples_ = num_samples; } void SetCaption(const std::string& c) { @@ -168,6 +175,9 @@ struct Image { */ void FinishSampling(); + /* + * Just store a tensor with nothing to do with image format. + */ void SetSample(int index, const std::vector& shape, const std::vector& data); @@ -186,11 +196,17 @@ struct ImageReader { using value_t = typename Image::value_t; using shape_t = typename Image::shape_t; - ImageReader(TabletReader tablet) : reader_(tablet) {} + ImageReader(const std::string& mode, TabletReader tablet) + : reader_(tablet), mode_{mode} {} std::string caption() { CHECK_EQ(reader_.captions().size(), 1); - return reader_.captions().front(); + auto caption = reader_.captions().front(); + if (Reader::TagMatchMode(caption, mode_)) { + return Reader::GenReadableTag(mode_, caption); + } + string::TagDecode(caption); + return caption; } // number of steps. @@ -202,6 +218,7 @@ struct ImageReader { private: TabletReader reader_; + std::string mode_; }; } // namespace components diff --git a/visualdl/logic/sdk_test.cc b/visualdl/logic/sdk_test.cc index d056a0e15..7f6550f51 100644 --- a/visualdl/logic/sdk_test.cc +++ b/visualdl/logic/sdk_test.cc @@ -74,7 +74,7 @@ TEST(Image, test) { Reader reader__(dir); auto reader = reader__.AsMode("train"); auto tablet2read = reader.tablet("image0"); - components::ImageReader image2read(tablet2read); + components::ImageReader image2read("train", tablet2read); CHECK_EQ(image2read.caption(), "this is an image"); CHECK_EQ(image2read.num_records(), num_steps); } diff --git a/visualdl/python/storage.py b/visualdl/python/storage.py index 694539a0b..17a18f8d8 100644 --- a/visualdl/python/storage.py +++ b/visualdl/python/storage.py @@ -31,6 +31,9 @@ def scalar(self, tag, type='float'): } return type2scalar[type](tag) + def image(self, tag): + return self.reader.get_image(tag) + class StorageWriter(object): @@ -50,3 +53,6 @@ def scalar(self, tag, type='float'): 'int': self.writer.new_scalar_int, } return type2scalar[type](tag) + + def image(self, tag, num_samples): + return self.writer.new_image(tag, num_samples) diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 328ee74ef..7e62dcc8f 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -8,11 +8,11 @@ class StorageTest(unittest.TestCase): def setUp(self): self.dir = "./tmp/storage_test" - - def test_read(self): - print 'test write' self.writer = storage.StorageWriter( self.dir, sync_cycle=1).as_mode("train") + + def test_scalar(self): + print 'test write' scalar = self.writer.scalar("model/scalar/min") # scalar.set_caption("model/scalar/min") for i in range(10): @@ -29,6 +29,27 @@ def test_read(self): print 'records', records print 'ids', ids + def test_image(self): + tag = "layer1/layer2/image0" + image_writer = self.writer.image(tag, 10) + num_passes = 10 + num_samples = 100 + + for pass_ in xrange(num_passes): + image_writer.start_sampling() + for ins in xrange(num_samples): + index = image_writer.is_sample_taken() + if index != -1: + shape = [3, 10, 10] + data = np.random.random(shape) * 256 + data = np.ndarray.flatten(data) + image_writer.set_sample(index, shape, list(data)) + image_writer.finish_sampling() + + self.reader = storage.StorageReader(self.dir).as_mode("train") + image_reader = self.reader.image(tag) + self.assertEqual(image_reader.caption(), tag) + if __name__ == '__main__': unittest.main() diff --git a/visualdl/storage/CMakeLists.txt b/visualdl/storage/CMakeLists.txt index ec9dfbbf8..51a0b415d 100644 --- a/visualdl/storage/CMakeLists.txt +++ b/visualdl/storage/CMakeLists.txt @@ -12,4 +12,4 @@ add_library(storage storage.cc storage.h ${PROTO_SRCS} ${PROTO_HDRS}) add_dependencies(entry storage_proto im) add_dependencies(record storage_proto entry) add_dependencies(tablet storage_proto) -add_dependencies(storage storage_proto) +add_dependencies(storage storage_proto record tablet entry) diff --git a/visualdl/storage/storage.h b/visualdl/storage/storage.h index 8e36d9efb..805700148 100644 --- a/visualdl/storage/storage.h +++ b/visualdl/storage/storage.h @@ -78,7 +78,7 @@ struct Storage { * Save memory to disk. */ void PersistToDisk(const std::string& dir) { - // LOG(INFO) << "persist to disk " << dir; + LOG(INFO) << "persist to disk " << dir; CHECK(!dir.empty()) << "dir should be set."; fs::TryRecurMkdir(dir); diff --git a/visualdl/storage/tablet.cc b/visualdl/storage/tablet.cc index e69de29bb..c64273f2a 100644 --- a/visualdl/storage/tablet.cc +++ b/visualdl/storage/tablet.cc @@ -0,0 +1,7 @@ +#include "visualdl/storage/tablet.h" + +namespace visualdl { + +TabletReader Tablet::reader() { return TabletReader(*data_); } + +} // namespace visualdl diff --git a/visualdl/storage/tablet.h b/visualdl/storage/tablet.h index 1c0c9136e..ba413be20 100644 --- a/visualdl/storage/tablet.h +++ b/visualdl/storage/tablet.h @@ -10,6 +10,8 @@ namespace visualdl { +struct TabletReader; + /* * Tablet is a helper for operations on storage::Tablet. */ @@ -80,6 +82,8 @@ struct Tablet { WRITE_GUARD } + TabletReader reader(); + Storage* parent() const { return x_; } private: From de48862bb2454aae4382ebed8d5604101567424a Mon Sep 17 00:00:00 2001 From: superjom Date: Tue, 26 Dec 2017 13:17:35 +0800 Subject: [PATCH 05/29] finish image python test --- visualdl/python/test_storage.py | 10 +++++++++- visualdl/storage/storage.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 7e62dcc8f..b9fa551a9 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -34,13 +34,13 @@ def test_image(self): image_writer = self.writer.image(tag, 10) num_passes = 10 num_samples = 100 + shape = [3, 10, 10] for pass_ in xrange(num_passes): image_writer.start_sampling() for ins in xrange(num_samples): index = image_writer.is_sample_taken() if index != -1: - shape = [3, 10, 10] data = np.random.random(shape) * 256 data = np.ndarray.flatten(data) image_writer.set_sample(index, shape, list(data)) @@ -49,6 +49,14 @@ def test_image(self): self.reader = storage.StorageReader(self.dir).as_mode("train") image_reader = self.reader.image(tag) self.assertEqual(image_reader.caption(), tag) + self.assertEqual(image_reader.num_records(), num_passes) + self.assertTrue(np.equal(image_reader.shape(0, 1), shape).all()) + data = image_reader.data(0, 1) + self.assertEqual(len(data), np.prod(shape)) + + image_tags = self.reader.tags("image") + self.assertTrue(image_tags) + self.assertEqual(len(image_tags), 1) if __name__ == '__main__': diff --git a/visualdl/storage/storage.h b/visualdl/storage/storage.h index 805700148..8e36d9efb 100644 --- a/visualdl/storage/storage.h +++ b/visualdl/storage/storage.h @@ -78,7 +78,7 @@ struct Storage { * Save memory to disk. */ void PersistToDisk(const std::string& dir) { - LOG(INFO) << "persist to disk " << dir; + // LOG(INFO) << "persist to disk " << dir; CHECK(!dir.empty()) << "dir should be set."; fs::TryRecurMkdir(dir); From 926e65b519ffebd3d791f5ecf5b58e9f4a5e5b5c Mon Sep 17 00:00:00 2001 From: superjom Date: Tue, 26 Dec 2017 18:15:12 +0800 Subject: [PATCH 06/29] add image server --- server/visualdl/lib.py | 105 ++++++++++++++++++++++++++++++++ server/visualdl/mock.sh | 6 ++ server/visualdl/run.sh | 6 ++ server/visualdl/storage_mock.py | 46 ++++++++++++++ server/visualdl/test.sh | 6 ++ server/visualdl/visual_dl.py | 49 +++++++++++---- visualdl/logic/pybind.cc | 2 + visualdl/logic/sdk.cc | 14 +++++ visualdl/logic/sdk.h | 14 ++--- 9 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 server/visualdl/lib.py create mode 100644 server/visualdl/mock.sh create mode 100644 server/visualdl/run.sh create mode 100644 server/visualdl/storage_mock.py create mode 100644 server/visualdl/test.sh diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py new file mode 100644 index 000000000..8f073b9ac --- /dev/null +++ b/server/visualdl/lib.py @@ -0,0 +1,105 @@ +import numpy as np +import re +import storage +from PIL import Image +from tempfile import NamedTemporaryFile + + +def get_scalar_tags(storage, mode): + result = {} + print 'modes', storage.modes() + for mode in storage.modes(): + result[mode] = {} + reader = storage.as_mode(mode) + for tag in reader.tags('scalar'): + result[mode][tag] = { + 'displayName': reader.scalar(tag).caption(), + 'description': "", + } + return result + + +def get_image_tags(storage, mode): + result = {} + + print 'modes', storage.modes() + for mode in storage.modes(): + reader = storage.as_mode(mode) + print 'tags', reader.tags('image') + result[mode] = {} + for tag in reader.tags('image'): + image = reader.image(tag) + if image.num_samples() == 1: + result[mode][tag] = { + 'displayName': reader.scalar(tag).caption(), + 'description': "", + 'samples': 1, + } + else: + for i in xrange(image.num_samples()): + result[mode][tag + '/%d' % i] = { + 'displayName': reader.scalar(tag).caption(), + 'description': "", + 'samples': 1, + } + return result + + +def get_image_tag_steps(storage, mode, tag): + # remove suffix '/x' + res = re.search(r".*/([0-9]+$)", tag) + if res: + tag = tag[:tag.rfind('/')] + + reader = storage.as_mode(mode) + image = reader.image(tag) + # TODO(ChunweiYan) make max_steps a config + max_steps = 10 + res = [] + steps = [] + if image.num_records() > max_steps: + span = int(image.num_records() / max_steps) + steps = [image.num_records() - i * span - 1 for i in xrange(max_steps)] + steps = [i for i in reversed(steps)] + steps[0] = max(steps[0], 0) + else: + steps = [i for i in xrange(image.num_records())] + + for step in steps: + res.append({ + 'wall_time': image.timestamp(step), + 'step': step, + }) + return res + + +def get_invididual_image(storage, mode, tag, index): + reader = storage.as_mode(mode) + res = re.search(r".*/([0-9]+$)", tag) + # remove suffix '/x' + if res: + offset = int(res.groups()[0]) + tag = tag[:tag.rfind('/')] + + image = reader.image(tag) + data = image.data(offset, index) + shape = image.shape(offset, index) + # print data + # print shape + data = np.array(data, dtype='uint8').reshape(shape) + tempfile = NamedTemporaryFile(mode='w+b', suffix='.png') + with Image.fromarray(data) as im: + im.save(tempfile) + tempfile.seek(0, 0) + return tempfile + + +if __name__ == '__main__': + reader = storage.StorageReader('./tmp/mock') + tags = get_image_tags(reader, 'train') + + tags = get_image_tag_steps(reader, 'train', 'layer1/layer2/image0/0') + print 'image step tags', tags + + image = get_invididual_image(reader, "train", 'layer1/layer2/image0/0', 2) + print image diff --git a/server/visualdl/mock.sh b/server/visualdl/mock.sh new file mode 100644 index 000000000..3ac2d91d3 --- /dev/null +++ b/server/visualdl/mock.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -ex + +export PYTHONPATH="/home/superjom/project/VisualDL/build/visualdl/logic:/home/superjom/project/VisualDL/visualdl/python" + +python storage_mock.py diff --git a/server/visualdl/run.sh b/server/visualdl/run.sh new file mode 100644 index 000000000..227f596c4 --- /dev/null +++ b/server/visualdl/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -ex + +export PYTHONPATH="$(pwd)/..:/home/superjom/project/VisualDL/build/visualdl/logic:/home/superjom/project/VisualDL/visualdl/python" + +python visual_dl.py --logdir ./tmp/mock --host 172.23.233.68 diff --git a/server/visualdl/storage_mock.py b/server/visualdl/storage_mock.py new file mode 100644 index 000000000..e02ef26fc --- /dev/null +++ b/server/visualdl/storage_mock.py @@ -0,0 +1,46 @@ +import storage +import numpy as np +import unittest +import random +import time + +dir = "./tmp/mock" +writer = storage.StorageWriter(dir, sync_cycle=20) + +train_writer = writer.as_mode("train") +test_writer = writer.as_mode("test") + +train_scalar = train_writer.scalar("model/scalar/min") +test_scalar = test_writer.scalar("model/scalar/min") + +train_scalar1 = train_writer.scalar("model/scalar/max") +test_scalar1 = test_writer.scalar("model/scalar/max") + +for i in range(100): + train_scalar.add_record(i, random.random()) + train_scalar1.add_record(i, random.random()) + #time.sleep(1) + if i % 10 == 0: + test_scalar.add_record(i, random.random()) + test_scalar1.add_record(i, random.random()) + +def add_image(mode): + writer_ = writer.as_mode(mode) + tag = "layer1/layer2/image0" + image_writer = writer_.image(tag, 10) + num_passes = 25 + num_samples = 100 + shape = [10, 10, 3] + + for pass_ in xrange(num_passes): + image_writer.start_sampling() + for ins in xrange(num_samples): + index = image_writer.is_sample_taken() + if index != -1: + data = np.random.random(shape) * 256 + data = np.ndarray.flatten(data) + image_writer.set_sample(index, shape, list(data)) + image_writer.finish_sampling() + +add_image("train") +add_image("test") diff --git a/server/visualdl/test.sh b/server/visualdl/test.sh new file mode 100644 index 000000000..3b4e8e444 --- /dev/null +++ b/server/visualdl/test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -ex + +export PYTHONPATH="$(pwd)/..:/home/superjom/project/VisualDL/build/visualdl/logic:/home/superjom/project/VisualDL/visualdl/python" + +python lib.py diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index 3d7ce0f31..98b1d87da 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -2,18 +2,20 @@ """ import json import os +import re import sys from optparse import OptionParser from flask import Flask, redirect from flask import request -from flask import send_from_directory +from flask import send_from_directory, send_file from flask import Response from visualdl.log import logger import visualdl.mock.data as mock_data import visualdl.mock.tags as mock_tags import storage +import lib app = Flask(__name__, static_url_path="") @@ -87,7 +89,7 @@ def logdir(): @app.route('/data/runs') def runs(): - is_debug = bool(request.args.get('debug')) + modes = storage.modes() result = gen_result(0, "", ["train", "test"]) return Response(json.dumps(result), mimetype='application/json') @@ -95,20 +97,18 @@ def runs(): @app.route("/data/plugin/scalars/tags") def tags(): is_debug = bool(request.args.get('debug')) - tag = request.args.get('tag') if is_debug: result = mock_tags.data() else: - result = {} - print 'modes', storage.modes() - for mode in storage.modes(): - result[mode] = {} - reader = storage.as_mode(mode) - for tag in reader.tags("scalar"): - result[mode][tag] = { - 'displayName': reader.scalar(tag).caption(), - 'description': "" - } + result = lib.get_scalar_tags(storage, mode) + print 'tags', result + result = gen_result(0, "", result) + return Response(json.dumps(result), mimetype='application/json') + + +@app.route("/data/plugin/images/tags") +def tags(): + result = lib.get_image_tags(storage, mode) print 'tags', result result = gen_result(0, "", result) return Response(json.dumps(result), mimetype='application/json') @@ -135,6 +135,29 @@ def scalars(): return Response(json.dumps(result), mimetype='application/json') +@app.route('/data/plugin/images/images') +def images(): + run = request.args.get('run') + tag = request.args.get('tag') + + res = lib.gen_image_tag_steps(storage, mode, tag) + + return Response(json.dumps(result), mimetype='application/json') + + +@app.route('/data/plugin/images/individualImage') +def individual_image(): + run = request.args.get('run') + tag = request.args.get('tag') # include a index + index = request.args.get('index') # index of step + offset = 0 + + imagefile = lib.get_invididual_image(storage, mode, tag) + response = send_file( + imagefile, as_attachment=True, attachment_filename='img.png') + return response + + if __name__ == '__main__': logger.info(" port=" + str(options.port)) app.run(debug=False, host=options.host, port=options.port) diff --git a/visualdl/logic/pybind.cc b/visualdl/logic/pybind.cc index 69d84a1da..1a55ec7a3 100644 --- a/visualdl/logic/pybind.cc +++ b/visualdl/logic/pybind.cc @@ -91,6 +91,8 @@ PYBIND11_PLUGIN(core) { py::class_(m, "ImageReader") .def("caption", &cp::ImageReader::caption) .def("num_records", &cp::ImageReader::num_records) + .def("num_samples", &cp::ImageReader::num_samples) + .def("timestamp", &cp::ImageReader::timestamp) .def("data", &cp::ImageReader::data) .def("shape", &cp::ImageReader::shape); diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 13229fae6..461b99ac3 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -49,6 +49,10 @@ template class ScalarReader; void Image::StartSampling() { step_ = writer_.AddRecord(); + + time_t time = std::time(nullptr); + step_.SetTimeStamp(time); + // resize record for (int i = 0; i < num_samples_; i++) { step_.AddData(); @@ -110,6 +114,16 @@ void Image::SetSample(int index, meta.SetMulti(shape); } +std::string ImageReader::caption() { + CHECK_EQ(reader_.captions().size(), 1); + auto caption = reader_.captions().front(); + if (Reader::TagMatchMode(caption, mode_)) { + return Reader::GenReadableTag(mode_, caption); + } + string::TagDecode(caption); + return caption; +} + std::vector ImageReader::data(int step, int index) { auto record = reader_.record(step); auto entry = record.data(index); diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index 4399ca356..4d0cbf6e2 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -199,19 +199,15 @@ struct ImageReader { ImageReader(const std::string& mode, TabletReader tablet) : reader_(tablet), mode_{mode} {} - std::string caption() { - CHECK_EQ(reader_.captions().size(), 1); - auto caption = reader_.captions().front(); - if (Reader::TagMatchMode(caption, mode_)) { - return Reader::GenReadableTag(mode_, caption); - } - string::TagDecode(caption); - return caption; - } + std::string caption(); // number of steps. int num_records() { return reader_.total_records(); } + int num_samples() { return reader_.num_samples(); } + + int64_t timestamp(int step) { return reader_.record(step).timestamp(); } + std::vector data(int step, int index); std::vector shape(int step, int index); From 401e139b05d96e87472fa59e0d508457ff134616 Mon Sep 17 00:00:00 2001 From: superjom Date: Wed, 27 Dec 2017 13:04:43 +0800 Subject: [PATCH 07/29] refactor image get record interface --- server/visualdl/lib.py | 50 ++++++++++++++++++--------------- server/visualdl/storage_mock.py | 2 +- visualdl/logic/pybind.cc | 23 +++++++++++---- visualdl/logic/sdk.cc | 30 ++++++++++++-------- visualdl/logic/sdk.h | 42 ++++++++++++++++++++++++--- visualdl/logic/sdk_test.cc | 2 +- visualdl/python/storage.py | 4 +-- visualdl/python/test_storage.py | 8 ++++-- 8 files changed, 111 insertions(+), 50 deletions(-) diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py index 8f073b9ac..c86fe1464 100644 --- a/server/visualdl/lib.py +++ b/server/visualdl/lib.py @@ -3,6 +3,8 @@ import storage from PIL import Image from tempfile import NamedTemporaryFile +import pprint +import urllib def get_scalar_tags(storage, mode): @@ -31,14 +33,14 @@ def get_image_tags(storage, mode): image = reader.image(tag) if image.num_samples() == 1: result[mode][tag] = { - 'displayName': reader.scalar(tag).caption(), + 'displayName': mage.caption(), 'description': "", 'samples': 1, } else: for i in xrange(image.num_samples()): result[mode][tag + '/%d' % i] = { - 'displayName': reader.scalar(tag).caption(), + 'displayName': image.caption(), 'description': "", 'samples': 1, } @@ -48,32 +50,35 @@ def get_image_tags(storage, mode): def get_image_tag_steps(storage, mode, tag): # remove suffix '/x' res = re.search(r".*/([0-9]+$)", tag) + step_index = 0 if res: tag = tag[:tag.rfind('/')] + step_index = int(res.groups()[0]) reader = storage.as_mode(mode) image = reader.image(tag) - # TODO(ChunweiYan) make max_steps a config - max_steps = 10 res = [] - steps = [] - if image.num_records() > max_steps: - span = int(image.num_records() / max_steps) - steps = [image.num_records() - i * span - 1 for i in xrange(max_steps)] - steps = [i for i in reversed(steps)] - steps[0] = max(steps[0], 0) - else: - steps = [i for i in xrange(image.num_records())] - - for step in steps: + + for i in range(image.num_samples()): + record = image.record(step_index, i) + shape = record.shape() + query = urllib.urlencode({ + 'sample': 0, + 'index': i, + 'tag': tag, + 'run': mode, + }) res.append({ - 'wall_time': image.timestamp(step), - 'step': step, + 'height': shape[0], + 'width': shape[1], + 'step': record.step_id(), + 'wall_time': image.timestamp(step_index), + 'query': query, }) return res -def get_invididual_image(storage, mode, tag, index): +def get_invididual_image(storage, mode, tag, step_index): reader = storage.as_mode(mode) res = re.search(r".*/([0-9]+$)", tag) # remove suffix '/x' @@ -82,11 +87,9 @@ def get_invididual_image(storage, mode, tag, index): tag = tag[:tag.rfind('/')] image = reader.image(tag) - data = image.data(offset, index) - shape = image.shape(offset, index) - # print data - # print shape - data = np.array(data, dtype='uint8').reshape(shape) + record = image.record(step_index, offset) + + data = np.array(record.data(), dtype='uint8').reshape(record.shape()) tempfile = NamedTemporaryFile(mode='w+b', suffix='.png') with Image.fromarray(data) as im: im.save(tempfile) @@ -99,7 +102,8 @@ def get_invididual_image(storage, mode, tag, index): tags = get_image_tags(reader, 'train') tags = get_image_tag_steps(reader, 'train', 'layer1/layer2/image0/0') - print 'image step tags', tags + print 'image step tags' + pprint.pprint(tags) image = get_invididual_image(reader, "train", 'layer1/layer2/image0/0', 2) print image diff --git a/server/visualdl/storage_mock.py b/server/visualdl/storage_mock.py index e02ef26fc..7ccf4f92d 100644 --- a/server/visualdl/storage_mock.py +++ b/server/visualdl/storage_mock.py @@ -27,7 +27,7 @@ def add_image(mode): writer_ = writer.as_mode(mode) tag = "layer1/layer2/image0" - image_writer = writer_.image(tag, 10) + image_writer = writer_.image(tag, 10, 1) num_passes = 25 num_samples = 100 shape = [10, 10, 3] diff --git a/visualdl/logic/pybind.cc b/visualdl/logic/pybind.cc index 1a55ec7a3..425b75fec 100644 --- a/visualdl/logic/pybind.cc +++ b/visualdl/logic/pybind.cc @@ -53,9 +53,12 @@ PYBIND11_PLUGIN(core) { WRITER_ADD_SCALAR(int) // clang-format on .def("new_image", - [](vs::Writer& self, const std::string& tag, int num_samples) { + [](vs::Writer& self, + const std::string& tag, + int num_samples, + int step_cycle) { auto tablet = self.AddTablet(tag); - return vs::components::Image(tablet, num_samples); + return vs::components::Image(tablet, num_samples, step_cycle); }); //------------------- components -------------------- @@ -88,12 +91,22 @@ PYBIND11_PLUGIN(core) { .def("finish_sampling", &cp::Image::FinishSampling) .def("set_sample", &cp::Image::SetSample); + py::class_(m, "ImageRecord") + // TODO(ChunweiYan) make these copyless. + .def("data", [](cp::ImageReader::ImageRecord& self) { return self.data; }) + .def("shape", + [](cp::ImageReader::ImageRecord& self) { return self.shape; }) + .def("step_id", + [](cp::ImageReader::ImageRecord& self) { return self.step_id; }); + py::class_(m, "ImageReader") .def("caption", &cp::ImageReader::caption) .def("num_records", &cp::ImageReader::num_records) .def("num_samples", &cp::ImageReader::num_samples) - .def("timestamp", &cp::ImageReader::timestamp) - .def("data", &cp::ImageReader::data) - .def("shape", &cp::ImageReader::shape); + .def("record", &cp::ImageReader::record) + .def("timestamp", &cp::ImageReader::timestamp); + + // .def("data", &cp::ImageReader::data) + // .def("shape", &cp::ImageReader::shape); } // end pybind diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 461b99ac3..06ea178f0 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -48,7 +48,11 @@ template class ScalarReader; template class ScalarReader; void Image::StartSampling() { + // TODO(ChunweiYan) big bug here, every step will be stored in protobuf + // and that might result in explosion in some scenerios, Just sampling + // some steps should be better. step_ = writer_.AddRecord(); + step_.SetId(step_id_); time_t time = std::time(nullptr); step_.SetTimeStamp(time); @@ -61,6 +65,7 @@ void Image::StartSampling() { } int Image::IsSampleTaken() { + if (!ToSampleThisStep()) return -1; num_records_++; if (num_records_ <= num_samples_) { return num_records_ - 1; @@ -76,8 +81,11 @@ int Image::IsSampleTaken() { } void Image::FinishSampling() { - // TODO(ChunweiYan) much optimizement here. - writer_.parent()->PersistToDisk(); + step_id_++; + if (ToSampleThisStep()) { + // TODO(ChunweiYan) much optimizement here. + writer_.parent()->PersistToDisk(); + } } template @@ -124,16 +132,16 @@ std::string ImageReader::caption() { return caption; } -std::vector ImageReader::data(int step, int index) { - auto record = reader_.record(step); - auto entry = record.data(index); - return entry.GetMulti(); -} +ImageReader::ImageRecord ImageReader::record(int offset, int index) { + ImageRecord res; + auto record = reader_.record(offset); + auto data_entry = record.data(index); + auto shape_entry = record.data(index); -std::vector ImageReader::shape(int step, int index) { - auto record = reader_.record(step); - auto entry = record.data(index); - return entry.GetMulti(); + res.data = data_entry.GetMulti(); + res.shape = shape_entry.GetMulti(); + res.step_id = record.id(); + return res; } } // namespace components diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index 4d0cbf6e2..cfcf312f0 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -152,12 +152,19 @@ struct Image { using value_t = float; using shape_t = int64_t; - Image(Tablet tablet, int num_samples) : writer_(tablet) { + /* + * step_cycle: store every `step_cycle` as a record. + * num_samples: how many samples to take in a step. + */ + Image(Tablet tablet, int num_samples, int step_cycle) + : writer_(tablet), num_samples_(num_samples), step_cycle_(step_cycle) { + CHECK_GT(step_cycle, 0); + CHECK_GT(num_samples, 0); + writer_.SetType(Tablet::Type::kImage); // make image's tag as the default caption. writer_.SetNumSamples(num_samples); SetCaption(tablet.reader().tag()); - num_samples_ = num_samples; } void SetCaption(const std::string& c) { writer_.SetCaptions(std::vector({c})); @@ -182,11 +189,16 @@ struct Image { const std::vector& shape, const std::vector& data); +protected: + bool ToSampleThisStep() { return step_id_ % step_cycle_ == 0; } + private: Tablet writer_; Record step_; int num_records_{0}; int num_samples_{0}; + int step_id_{0}; + int step_cycle_; }; /* @@ -196,6 +208,12 @@ struct ImageReader { using value_t = typename Image::value_t; using shape_t = typename Image::shape_t; + struct ImageRecord { + int step_id; + std::vector data; + std::vector shape; + }; + ImageReader(const std::string& mode, TabletReader tablet) : reader_(tablet), mode_{mode} {} @@ -208,9 +226,25 @@ struct ImageReader { int64_t timestamp(int step) { return reader_.record(step).timestamp(); } - std::vector data(int step, int index); + /* + * offset: offset of a step. + * index: index of a sample. + */ + ImageRecord record(int offset, int index); + + /* + * offset: offset of a step. + * index: index of a sample. + */ + std::vector data(int offset, int index); + + /* + * offset: offset of a step. + * index: index of a sample. + */ + std::vector shape(int offset, int index); - std::vector shape(int step, int index); + int stepid(int offset, int index); private: TabletReader reader_; diff --git a/visualdl/logic/sdk_test.cc b/visualdl/logic/sdk_test.cc index 7f6550f51..2b4c8e98d 100644 --- a/visualdl/logic/sdk_test.cc +++ b/visualdl/logic/sdk_test.cc @@ -48,7 +48,7 @@ TEST(Image, test) { auto writer = writer__.AsMode("train"); auto tablet = writer.AddTablet("image0"); - components::Image image(tablet, 3); + components::Image image(tablet, 3, 1); const int num_steps = 10; LOG(INFO) << "write images"; diff --git a/visualdl/python/storage.py b/visualdl/python/storage.py index 17a18f8d8..4b6bdd4c6 100644 --- a/visualdl/python/storage.py +++ b/visualdl/python/storage.py @@ -54,5 +54,5 @@ def scalar(self, tag, type='float'): } return type2scalar[type](tag) - def image(self, tag, num_samples): - return self.writer.new_image(tag, num_samples) + def image(self, tag, num_samples, step_cycle): + return self.writer.new_image(tag, num_samples, step_cycle) diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index b9fa551a9..9a49a1103 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -31,7 +31,7 @@ def test_scalar(self): def test_image(self): tag = "layer1/layer2/image0" - image_writer = self.writer.image(tag, 10) + image_writer = self.writer.image(tag, 10, 1) num_passes = 10 num_samples = 100 shape = [3, 10, 10] @@ -50,8 +50,10 @@ def test_image(self): image_reader = self.reader.image(tag) self.assertEqual(image_reader.caption(), tag) self.assertEqual(image_reader.num_records(), num_passes) - self.assertTrue(np.equal(image_reader.shape(0, 1), shape).all()) - data = image_reader.data(0, 1) + + image_record = image_reader.record(0, 1) + self.assertTrue(np.equal(image_record.shape(), shape).all()) + data = image_record.data() self.assertEqual(len(data), np.prod(shape)) image_tags = self.reader.tags("image") From b632c19f2c70bd44ccbd8e594ae02eab8ebf8d04 Mon Sep 17 00:00:00 2001 From: superjom Date: Wed, 27 Dec 2017 14:32:51 +0800 Subject: [PATCH 08/29] image service ready fix most bugs waiting for frontend to fix interfaces --- server/visualdl/lib.py | 24 +++++++++++++++++++----- server/visualdl/run.sh | 4 +++- server/visualdl/visual_dl.py | 34 +++++++++++++++------------------- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py index c86fe1464..e22cd07a7 100644 --- a/server/visualdl/lib.py +++ b/server/visualdl/lib.py @@ -21,6 +21,18 @@ def get_scalar_tags(storage, mode): return result +def get_scalar(storage, mode, tag): + reader = storage.as_mode(mode) + scalar = reader.scalar(tag) + + records = scalar.records() + ids = scalar.ids() + timestamps = scalar.timestamps() + + result = zip(timestamps, ids, records) + return result + + def get_image_tags(storage, mode): result = {} @@ -31,16 +43,17 @@ def get_image_tags(storage, mode): result[mode] = {} for tag in reader.tags('image'): image = reader.image(tag) - if image.num_samples() == 1: + if image.num_samples() <= 1: result[mode][tag] = { - 'displayName': mage.caption(), + 'displayName': image.caption(), 'description': "", 'samples': 1, } else: for i in xrange(image.num_samples()): - result[mode][tag + '/%d' % i] = { - 'displayName': image.caption(), + caption = tag + '/%d' % i + result[mode][caption] = { + 'displayName': caption, 'description': "", 'samples': 1, } @@ -51,6 +64,7 @@ def get_image_tag_steps(storage, mode, tag): # remove suffix '/x' res = re.search(r".*/([0-9]+$)", tag) step_index = 0 + origin_tag = tag if res: tag = tag[:tag.rfind('/')] step_index = int(res.groups()[0]) @@ -65,7 +79,7 @@ def get_image_tag_steps(storage, mode, tag): query = urllib.urlencode({ 'sample': 0, 'index': i, - 'tag': tag, + 'tag': origin_tag, 'run': mode, }) res.append({ diff --git a/server/visualdl/run.sh b/server/visualdl/run.sh index 227f596c4..b67a4d339 100644 --- a/server/visualdl/run.sh +++ b/server/visualdl/run.sh @@ -2,5 +2,7 @@ set -ex export PYTHONPATH="$(pwd)/..:/home/superjom/project/VisualDL/build/visualdl/logic:/home/superjom/project/VisualDL/visualdl/python" +export FLASK_APP=visual_dl.py +export FLASK_DEBUG=1 -python visual_dl.py --logdir ./tmp/mock --host 172.23.233.68 +python visual_dl.py --logdir ./tmp/mock --host 172.23.233.68 --port 8041 diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index 98b1d87da..953b02035 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -95,7 +95,8 @@ def runs(): @app.route("/data/plugin/scalars/tags") -def tags(): +def scalar_tags(): + mode = request.args.get('mode') is_debug = bool(request.args.get('debug')) if is_debug: result = mock_tags.data() @@ -107,7 +108,8 @@ def tags(): @app.route("/data/plugin/images/tags") -def tags(): +def image_tags(): + mode = request.args.get('mode') result = lib.get_image_tags(storage, mode) print 'tags', result result = gen_result(0, "", result) @@ -120,39 +122,33 @@ def scalars(): tag = request.args.get('tag') is_debug = bool(request.args.get('debug')) if is_debug: - result = gen_result(0, "", mock_data.sequence_data()) + result = mock_data.sequence_data() else: - reader = storage.as_mode(run) - scalar = reader.scalar(tag) - - records = scalar.records() - ids = scalar.ids() - timestamps = scalar.timestamps() - - result = zip(timestamps, ids, records) - result = gen_result(0, "", result) + result = lib.get_scalar(storage, run, tag) + result = gen_result(0, "", result) return Response(json.dumps(result), mimetype='application/json') @app.route('/data/plugin/images/images') def images(): - run = request.args.get('run') - tag = request.args.get('tag') + mode = request.args.get('run') + #tag = request.args.get('tag') + tag = request.args.get('displayName') - res = lib.gen_image_tag_steps(storage, mode, tag) + result = lib.get_image_tag_steps(storage, mode, tag) return Response(json.dumps(result), mimetype='application/json') @app.route('/data/plugin/images/individualImage') def individual_image(): - run = request.args.get('run') + mode = request.args.get('run') tag = request.args.get('tag') # include a index - index = request.args.get('index') # index of step + step_index = request.args.get('index') # index of step offset = 0 - imagefile = lib.get_invididual_image(storage, mode, tag) + imagefile = lib.get_invididual_image(storage, mode, tag, step_) response = send_file( imagefile, as_attachment=True, attachment_filename='img.png') return response @@ -160,4 +156,4 @@ def individual_image(): if __name__ == '__main__': logger.info(" port=" + str(options.port)) - app.run(debug=False, host=options.host, port=options.port) + app.run(debug=True, host=options.host, port=options.port) From d5ade7d4c0499126f20bd949073c77d82181960d Mon Sep 17 00:00:00 2001 From: superjom Date: Wed, 27 Dec 2017 14:34:53 +0800 Subject: [PATCH 09/29] code format --- server/visualdl/visual_dl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index 953b02035..f27a9bce1 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -122,7 +122,7 @@ def scalars(): tag = request.args.get('tag') is_debug = bool(request.args.get('debug')) if is_debug: - result = mock_data.sequence_data() + result = mock_data.sequence_data() else: result = lib.get_scalar(storage, run, tag) From fe5579f424c5b66a46106c686e51462ae55d623c Mon Sep 17 00:00:00 2001 From: superjom Date: Wed, 27 Dec 2017 14:35:24 +0800 Subject: [PATCH 10/29] code format --- server/visualdl/visual_dl.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index f27a9bce1..24228edcf 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -1,5 +1,3 @@ -""" entry point of visual_dl -""" import json import os import re From bb3ac6fc0a06d1e5ce675cbc4ba7fd89354f1be4 Mon Sep 17 00:00:00 2001 From: superjom Date: Wed, 27 Dec 2017 14:58:15 +0800 Subject: [PATCH 11/29] apply isort --- server/visualdl/lib.py | 12 +++++++----- server/visualdl/storage_mock.py | 8 +++++--- server/visualdl/visual_dl.py | 12 +++++------- visualdl/python/test_storage.py | 8 +++++--- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py index e22cd07a7..115177b24 100644 --- a/server/visualdl/lib.py +++ b/server/visualdl/lib.py @@ -1,10 +1,12 @@ -import numpy as np -import re -import storage -from PIL import Image -from tempfile import NamedTemporaryFile import pprint +import re import urllib +from tempfile import NamedTemporaryFile + +import numpy as np +from PIL import Image + +import storage def get_scalar_tags(storage, mode): diff --git a/server/visualdl/storage_mock.py b/server/visualdl/storage_mock.py index 7ccf4f92d..1192f92e4 100644 --- a/server/visualdl/storage_mock.py +++ b/server/visualdl/storage_mock.py @@ -1,8 +1,10 @@ -import storage -import numpy as np -import unittest import random import time +import unittest + +import numpy as np + +import storage dir = "./tmp/mock" writer = storage.StorageWriter(dir, sync_cycle=20) diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index 24228edcf..6620bef82 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -4,16 +4,14 @@ import sys from optparse import OptionParser -from flask import Flask, redirect -from flask import request -from flask import send_from_directory, send_file -from flask import Response +from flask import (Flask, Response, redirect, request, send_file, + send_from_directory) -from visualdl.log import logger +import lib +import storage import visualdl.mock.data as mock_data import visualdl.mock.tags as mock_tags -import storage -import lib +from visualdl.log import logger app = Flask(__name__, static_url_path="") diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 9a49a1103..05ae2f24c 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -1,8 +1,10 @@ -import storage -import numpy as np -import unittest import random import time +import unittest + +import numpy as np + +import storage class StorageTest(unittest.TestCase): From 7f500f456874354527521dff739ce4ee8bed4db1 Mon Sep 17 00:00:00 2001 From: superjom Date: Wed, 27 Dec 2017 20:32:31 +0800 Subject: [PATCH 12/29] fix image just a trick to store int8 in protobuf should limit the maximum image shape latter --- server/visualdl/lib.py | 11 +++++----- server/visualdl/run.sh | 2 +- server/visualdl/storage_mock.py | 11 +++++----- server/visualdl/visual_dl.py | 5 +++-- visualdl/logic/im.cc | 1 + visualdl/logic/sdk.cc | 18 +++++++++++----- visualdl/logic/sdk.h | 2 +- visualdl/storage/entry.cc | 25 +++++++++++++++++++++- visualdl/storage/storage.proto | 37 ++++++++++++++++++--------------- 9 files changed, 75 insertions(+), 37 deletions(-) diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py index 115177b24..b7817eb64 100644 --- a/server/visualdl/lib.py +++ b/server/visualdl/lib.py @@ -65,22 +65,23 @@ def get_image_tags(storage, mode): def get_image_tag_steps(storage, mode, tag): # remove suffix '/x' res = re.search(r".*/([0-9]+$)", tag) - step_index = 0 + sample_index = 0 origin_tag = tag if res: tag = tag[:tag.rfind('/')] - step_index = int(res.groups()[0]) + sample_index = int(res.groups()[0]) reader = storage.as_mode(mode) image = reader.image(tag) res = [] - for i in range(image.num_samples()): - record = image.record(step_index, i) + + for step_index in range(image.num_records()): + record = image.record(step_index, sample_index) shape = record.shape() query = urllib.urlencode({ 'sample': 0, - 'index': i, + 'index': step_index, 'tag': origin_tag, 'run': mode, }) diff --git a/server/visualdl/run.sh b/server/visualdl/run.sh index b67a4d339..5f62f0eb2 100644 --- a/server/visualdl/run.sh +++ b/server/visualdl/run.sh @@ -5,4 +5,4 @@ export PYTHONPATH="$(pwd)/..:/home/superjom/project/VisualDL/build/visualdl/logi export FLASK_APP=visual_dl.py export FLASK_DEBUG=1 -python visual_dl.py --logdir ./tmp/mock --host 172.23.233.68 --port 8041 +python visual_dl.py --logdir ./tmp/mock --host 172.23.233.68 --port 8043 diff --git a/server/visualdl/storage_mock.py b/server/visualdl/storage_mock.py index 1192f92e4..963fa439a 100644 --- a/server/visualdl/storage_mock.py +++ b/server/visualdl/storage_mock.py @@ -29,14 +29,15 @@ def add_image(mode): writer_ = writer.as_mode(mode) tag = "layer1/layer2/image0" - image_writer = writer_.image(tag, 10, 1) - num_passes = 25 - num_samples = 100 - shape = [10, 10, 3] + # TODO check step_cycle + num_samples = 10 + num_passes = 10 + image_writer = writer_.image(tag, num_samples, 1) + shape = [400, 400, 3] for pass_ in xrange(num_passes): image_writer.start_sampling() - for ins in xrange(num_samples): + for ins in xrange(2*num_samples): index = image_writer.is_sample_taken() if index != -1: data = np.random.random(shape) * 256 diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index fd1446464..b28a496dc 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -135,6 +135,7 @@ def images(): tag = request.args.get('displayName') result = lib.get_image_tag_steps(storage, mode, tag) + result = gen_result(0, "", result) return Response(json.dumps(result), mimetype='application/json') @@ -143,10 +144,10 @@ def images(): def individual_image(): mode = request.args.get('run') tag = request.args.get('tag') # include a index - step_index = request.args.get('index') # index of step + step_index = int(request.args.get('index')) # index of step offset = 0 - imagefile = lib.get_invididual_image(storage, mode, tag, step_) + imagefile = lib.get_invididual_image(storage, mode, tag, step_index) response = send_file( imagefile, as_attachment=True, attachment_filename='img.png') return response diff --git a/visualdl/logic/im.cc b/visualdl/logic/im.cc index a2ec1f22e..6162c0e0f 100644 --- a/visualdl/logic/im.cc +++ b/visualdl/logic/im.cc @@ -37,6 +37,7 @@ template class SimpleWriteSyncGuard>; template class SimpleWriteSyncGuard>; template class SimpleWriteSyncGuard>; template class SimpleWriteSyncGuard>; +template class SimpleWriteSyncGuard>>; template class SimpleWriteSyncGuard>; } // namespace visualdl diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 06ea178f0..0eacb2fec 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -108,8 +108,13 @@ void Image::SetSample(int index, CHECK_LE(index, num_records_); // set data - auto entry = step_.MutableData(index); - entry.SetMulti(data); + auto entry = step_.MutableData>(index); + // trick to store int8 to protobuf + std::vector data_str(data.size()); + for (int i = 0; i < data.size(); i++) { + data_str[i] = data[i]; + } + entry.Set(data_str); static_assert( !is_same_type::value, @@ -135,10 +140,13 @@ std::string ImageReader::caption() { ImageReader::ImageRecord ImageReader::record(int offset, int index) { ImageRecord res; auto record = reader_.record(offset); - auto data_entry = record.data(index); + auto data_entry = record.data>(index); auto shape_entry = record.data(index); - - res.data = data_entry.GetMulti(); + auto data_str = data_entry.Get(); + std::transform(data_str.begin(), + data_str.end(), + std::back_inserter(res.data), + [](char i) { return (int)((unsigned char)i); }); res.shape = shape_entry.GetMulti(); res.step_id = record.id(); return res; diff --git a/visualdl/logic/sdk.h b/visualdl/logic/sdk.h index cfcf312f0..bf3ccda64 100644 --- a/visualdl/logic/sdk.h +++ b/visualdl/logic/sdk.h @@ -210,7 +210,7 @@ struct ImageReader { struct ImageRecord { int step_id; - std::vector data; + std::vector data; std::vector shape; }; diff --git a/visualdl/storage/entry.cc b/visualdl/storage/entry.cc index 2697f7cc7..5cf85c020 100644 --- a/visualdl/storage/entry.cc +++ b/visualdl/storage/entry.cc @@ -21,7 +21,22 @@ namespace visualdl { WRITE_GUARD \ } +template <> +void Entry>::Set(std::vector v) { + entry->set_dtype(storage::DataType::kBytes); + entry->set_y(std::string(v.begin(), v.end())); + WRITE_GUARD +} + +template <> +void Entry>::Add(std::vector v) { + entry->set_dtype(storage::DataType::kBytess); + *entry->add_ys() = std::string(v.begin(), v.end()); + WRITE_GUARD +} + IMPL_ENTRY_SET_OR_ADD(Set, int, kInt32, set_i32); +IMPL_ENTRY_SET_OR_ADD(Set, std::string, kString, set_s); IMPL_ENTRY_SET_OR_ADD(Set, int64_t, kInt64, set_i64); IMPL_ENTRY_SET_OR_ADD(Set, bool, kBool, set_b); IMPL_ENTRY_SET_OR_ADD(Set, float, kFloat, set_f); @@ -42,7 +57,7 @@ IMPL_ENTRY_SETMUL(bool, kBool, bs); #define IMPL_ENTRY_GET(T, fieldname__) \ template <> \ T EntryReader::Get() const { \ - data_.fieldname__(); \ + return data_.fieldname__(); \ } IMPL_ENTRY_GET(int, i32); @@ -52,6 +67,12 @@ IMPL_ENTRY_GET(double, d); IMPL_ENTRY_GET(std::string, s); IMPL_ENTRY_GET(bool, b); +template <> +std::vector EntryReader>::Get() const { + const auto& y = data_.y(); + return std::vector(y.begin(), y.end()); +} + #define IMPL_ENTRY_GET_MULTI(T, fieldname__) \ template <> \ std::vector EntryReader::GetMulti() const { \ @@ -70,10 +91,12 @@ template class Entry; template class Entry; template class Entry; template class Entry; +template class Entry>; template class EntryReader; template class EntryReader; template class EntryReader; template class EntryReader; +template class EntryReader>; } // namespace visualdl diff --git a/visualdl/storage/storage.proto b/visualdl/storage/storage.proto index 381ee9110..7ae68f29b 100644 --- a/visualdl/storage/storage.proto +++ b/visualdl/storage/storage.proto @@ -9,15 +9,16 @@ enum DataType { kDouble = 3; kString = 4; kBool = 5; + kBytes = 6; // entrys - kInt64s = 6; - kFloats = 7; - kDoubles = 8; - kStrings = 9; - kInt32s = 10; - kBools = 11; - - kUnknown = 12; + kInt64s = 7; + kFloats = 8; + kDoubles = 9; + kStrings = 10; + kInt32s = 11; + kBools = 12; + kBytess = 13; + kUnknown = 14; } // A data array, which type is `type`. @@ -29,16 +30,18 @@ message Entry { int32 i32 = 2; int64 i64 = 3; string s = 4; - float f = 5; - double d = 6; - bool b = 7; + bytes y = 5; + float f = 6; + double d = 7; + bool b = 8; // array - repeated int64 i64s = 8; - repeated float fs = 9; - repeated double ds = 10; - repeated int32 i32s = 11; - repeated string ss = 12; - repeated bool bs = 13; + repeated int64 i64s = 9; + repeated float fs = 10; + repeated double ds = 11; + repeated int32 i32s = 12; + repeated string ss = 13; + repeated bool bs = 14; + repeated bytes ys = 15; } /* From 917e41fdf29b9fe42312da113c7b66ed815c5957 Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 10:36:32 +0800 Subject: [PATCH 13/29] update some test --- server/visualdl/lib.py | 1 - server/visualdl/storage_mock.py | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py index b7817eb64..6d3a1ba68 100644 --- a/server/visualdl/lib.py +++ b/server/visualdl/lib.py @@ -119,7 +119,6 @@ def get_invididual_image(storage, mode, tag, step_index): tags = get_image_tags(reader, 'train') tags = get_image_tag_steps(reader, 'train', 'layer1/layer2/image0/0') - print 'image step tags' pprint.pprint(tags) image = get_invididual_image(reader, "train", 'layer1/layer2/image0/0', 2) diff --git a/server/visualdl/storage_mock.py b/server/visualdl/storage_mock.py index 963fa439a..a7bdd5d37 100644 --- a/server/visualdl/storage_mock.py +++ b/server/visualdl/storage_mock.py @@ -21,7 +21,6 @@ for i in range(100): train_scalar.add_record(i, random.random()) train_scalar1.add_record(i, random.random()) - #time.sleep(1) if i % 10 == 0: test_scalar.add_record(i, random.random()) test_scalar1.add_record(i, random.random()) @@ -31,13 +30,14 @@ def add_image(mode): tag = "layer1/layer2/image0" # TODO check step_cycle num_samples = 10 - num_passes = 10 + num_passes = 20 image_writer = writer_.image(tag, num_samples, 1) - shape = [400, 400, 3] + shape = [50, 50, 3] for pass_ in xrange(num_passes): image_writer.start_sampling() for ins in xrange(2*num_samples): + print '.', index = image_writer.is_sample_taken() if index != -1: data = np.random.random(shape) * 256 From cc252d3dd6756af80115e808fd93e5a75629c0e4 Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 10:41:11 +0800 Subject: [PATCH 14/29] code format --- server/visualdl/visual_dl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index b28a496dc..f34fd8b0d 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -16,11 +16,12 @@ import graph app = Flask(__name__, static_url_path="") +# set static expires in a short time to reduce browser's memory usage. +app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 30 def option_parser(): """ - :return: """ parser = OptionParser(usage="usage: visual_dl visual_dl.py "\ @@ -152,6 +153,7 @@ def individual_image(): imagefile, as_attachment=True, attachment_filename='img.png') return response + @app.route('/data/plugin/graphs/graph') def graph(): model_json = graph.load_model("") From 2e3eca35f61df1a53bc8dbd3bfa4b3ab00648804 Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 13:53:00 +0800 Subject: [PATCH 15/29] add server test --- server/visualdl/lib.py | 49 +++++++++++++------------- server/visualdl/storage_mock.py | 61 ++++++++++++++++----------------- server/visualdl/test.sh | 6 ---- server/visualdl/visual_dl.py | 11 +++--- tests.sh | 12 ++++++- 5 files changed, 70 insertions(+), 69 deletions(-) delete mode 100644 server/visualdl/test.sh diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py index 6d3a1ba68..efdf1648c 100644 --- a/server/visualdl/lib.py +++ b/server/visualdl/lib.py @@ -9,17 +9,22 @@ import storage +def get_modes(storage): + return storage.modes() + + def get_scalar_tags(storage, mode): result = {} - print 'modes', storage.modes() for mode in storage.modes(): - result[mode] = {} reader = storage.as_mode(mode) - for tag in reader.tags('scalar'): - result[mode][tag] = { - 'displayName': reader.scalar(tag).caption(), - 'description': "", - } + tags = reader.tags('scalar') + if tags: + result[mode] = {} + for tag in tags: + result[mode][tag] = { + 'displayName': reader.scalar(tag).caption(), + 'description': "", + } return result @@ -35,27 +40,20 @@ def get_scalar(storage, mode, tag): return result -def get_image_tags(storage, mode): +def get_image_tags(storage): result = {} - print 'modes', storage.modes() for mode in storage.modes(): reader = storage.as_mode(mode) - print 'tags', reader.tags('image') - result[mode] = {} - for tag in reader.tags('image'): - image = reader.image(tag) - if image.num_samples() <= 1: - result[mode][tag] = { - 'displayName': image.caption(), - 'description': "", - 'samples': 1, - } - else: - for i in xrange(image.num_samples()): - caption = tag + '/%d' % i - result[mode][caption] = { - 'displayName': caption, + tags = reader.tags('image') + if tags: + result[mode] = {} + for tag in tags: + image = reader.image(tag) + for i in xrange(max(1, image.num_samples())): + tag = image.caption() if image.num_samples() <= 1 else '%s/%d'%(tag, i) + result[mode][tag] = { + 'displayName': tag, 'description': "", 'samples': 1, } @@ -75,7 +73,6 @@ def get_image_tag_steps(storage, mode, tag): image = reader.image(tag) res = [] - for step_index in range(image.num_records()): record = image.record(step_index, sample_index) shape = record.shape() @@ -116,7 +113,7 @@ def get_invididual_image(storage, mode, tag, step_index): if __name__ == '__main__': reader = storage.StorageReader('./tmp/mock') - tags = get_image_tags(reader, 'train') + tags = get_image_tags(reader) tags = get_image_tag_steps(reader, 'train', 'layer1/layer2/image0/0') pprint.pprint(tags) diff --git a/server/visualdl/storage_mock.py b/server/visualdl/storage_mock.py index a7bdd5d37..67277b0c5 100644 --- a/server/visualdl/storage_mock.py +++ b/server/visualdl/storage_mock.py @@ -4,46 +4,45 @@ import numpy as np -import storage -dir = "./tmp/mock" -writer = storage.StorageWriter(dir, sync_cycle=20) - -train_writer = writer.as_mode("train") -test_writer = writer.as_mode("test") - -train_scalar = train_writer.scalar("model/scalar/min") -test_scalar = test_writer.scalar("model/scalar/min") - -train_scalar1 = train_writer.scalar("model/scalar/max") -test_scalar1 = test_writer.scalar("model/scalar/max") - -for i in range(100): - train_scalar.add_record(i, random.random()) - train_scalar1.add_record(i, random.random()) - if i % 10 == 0: - test_scalar.add_record(i, random.random()) - test_scalar1.add_record(i, random.random()) - -def add_image(mode): +def add_scalar(writer, mode, tag, num_steps, skip): + my_writer = writer.as_mode(mode) + scalar = my_writer.scalar(tag) + for i in range(num_steps): + if i % skip == 0: + scalar.add_record(i, random.random()) + + +def add_image(writer, + mode, + tag, + num_samples, + num_passes, + step_cycle, + shape=[50, 50, 3]): writer_ = writer.as_mode(mode) - tag = "layer1/layer2/image0" - # TODO check step_cycle - num_samples = 10 - num_passes = 20 - image_writer = writer_.image(tag, num_samples, 1) - shape = [50, 50, 3] + image_writer = writer_.image(tag, num_samples, step_cycle) for pass_ in xrange(num_passes): image_writer.start_sampling() - for ins in xrange(2*num_samples): + for ins in xrange(2 * num_samples): print '.', - index = image_writer.is_sample_taken() + index = image_writer.is_sample_taken() if index != -1: data = np.random.random(shape) * 256 data = np.ndarray.flatten(data) image_writer.set_sample(index, shape, list(data)) image_writer.finish_sampling() -add_image("train") -add_image("test") + +if __name__ == '__main__': + add_scalar("train", "layer/scalar0/min", 1000, 1) + add_scalar("test", "layer/scalar0/min", 1000, 10) + add_scalar("valid", "layer/scalar0/min", 1000, 10) + + add_scalar("train", "layer/scalar0/max", 1000, 1) + add_scalar("test", "layer/scalar0/max", 1000, 10) + add_scalar("valid", "layer/scalar0/max", 1000, 10) + + add_image("train", "layer/image0", 7, 10, 1) + add_image("test", "layer/image0", 7, 10, 3) diff --git a/server/visualdl/test.sh b/server/visualdl/test.sh deleted file mode 100644 index 3b4e8e444..000000000 --- a/server/visualdl/test.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -ex - -export PYTHONPATH="$(pwd)/..:/home/superjom/project/VisualDL/build/visualdl/logic:/home/superjom/project/VisualDL/visualdl/python" - -python lib.py diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index f34fd8b0d..94b37bd93 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -89,7 +89,7 @@ def logdir(): @app.route('/data/runs') def runs(): modes = storage.modes() - result = gen_result(0, "", ["train", "test"]) + result = gen_result(0, "", lib.modes()) return Response(json.dumps(result), mimetype='application/json') @@ -101,16 +101,16 @@ def scalar_tags(): result = mock_tags.data() else: result = lib.get_scalar_tags(storage, mode) - print 'tags', result + print 'scalar tags (mode: %s)' % mode, result result = gen_result(0, "", result) return Response(json.dumps(result), mimetype='application/json') @app.route("/data/plugin/images/tags") def image_tags(): - mode = request.args.get('mode') - result = lib.get_image_tags(storage, mode) - print 'tags', result + mode = request.args.get('run') + result = lib.get_image_tags(storage) + print 'image tags (mode: %s)'%mode, result result = gen_result(0, "", result) return Response(json.dumps(result), mimetype='application/json') @@ -132,6 +132,7 @@ def scalars(): @app.route('/data/plugin/images/images') def images(): mode = request.args.get('run') + # TODO(ChunweiYan) update this when frontend fix the field name #tag = request.args.get('tag') tag = request.args.get('displayName') diff --git a/tests.sh b/tests.sh index 961e2f592..99700941f 100644 --- a/tests.sh +++ b/tests.sh @@ -2,7 +2,11 @@ set -ex mode=$1 -cur=$(pwd) +readonly cur=$(pwd) +readonly core_path=$cur/build/visualdl/logic +readonly python_path=$cur/visualdl/python + +export PYTHONPATH="${core_path}:${python_path}" backend_test() { cd $cur @@ -21,6 +25,11 @@ frontend_test() { npm run build } +server_test() { + cd $cur/server/visualdl + python lib_test.py +} + echo "mode" $mode if [ $mode = "backend" ]; then @@ -28,6 +37,7 @@ if [ $mode = "backend" ]; then elif [ $mode = "all" ]; then frontend_test backend_test + server_test else frontend_test fi From 944fa54fb4281392c94bef3ee41d50bd31f940fc Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 14:42:55 +0800 Subject: [PATCH 16/29] fix image bug --- server/visualdl/lib.py | 8 ++++-- server/visualdl/lib_test.py | 51 +++++++++++++++++++++++++++++++++ server/visualdl/mock.sh | 2 +- server/visualdl/storage_mock.py | 3 +- server/visualdl/visual_dl.py | 2 +- tests.sh | 2 ++ visualdl/logic/sdk.cc | 6 ++-- 7 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 server/visualdl/lib_test.py diff --git a/server/visualdl/lib.py b/server/visualdl/lib.py index efdf1648c..4f5c49b17 100644 --- a/server/visualdl/lib.py +++ b/server/visualdl/lib.py @@ -51,9 +51,9 @@ def get_image_tags(storage): for tag in tags: image = reader.image(tag) for i in xrange(max(1, image.num_samples())): - tag = image.caption() if image.num_samples() <= 1 else '%s/%d'%(tag, i) - result[mode][tag] = { - 'displayName': tag, + caption = tag if image.num_samples() <= 1 else '%s/%d'%(tag, i) + result[mode][caption] = { + 'displayName': caption, 'description': "", 'samples': 1, } @@ -61,6 +61,7 @@ def get_image_tags(storage): def get_image_tag_steps(storage, mode, tag): + print 'image_tag_steps,mode,tag:', mode, tag # remove suffix '/x' res = re.search(r".*/([0-9]+$)", tag) sample_index = 0 @@ -76,6 +77,7 @@ def get_image_tag_steps(storage, mode, tag): for step_index in range(image.num_records()): record = image.record(step_index, sample_index) shape = record.shape() + assert shape, "%s,%s" % (mode, tag) query = urllib.urlencode({ 'sample': 0, 'index': step_index, diff --git a/server/visualdl/lib_test.py b/server/visualdl/lib_test.py new file mode 100644 index 000000000..f167af099 --- /dev/null +++ b/server/visualdl/lib_test.py @@ -0,0 +1,51 @@ +import lib +import unittest +import storage +import pprint +from storage_mock import add_scalar, add_image + + +class LibTest(unittest.TestCase): + def setUp(self): + dir = "./tmp/mock" + writer = storage.StorageWriter(dir, sync_cycle=20) + + add_scalar(writer, "train", "layer/scalar0/min", 1000, 1) + add_scalar(writer, "test", "layer/scalar0/min", 1000, 10) + add_scalar(writer, "valid", "layer/scalar0/min", 1000, 10) + + add_scalar(writer, "train", "layer/scalar0/max", 1000, 1) + add_scalar(writer, "test", "layer/scalar0/max", 1000, 10) + add_scalar(writer, "valid", "layer/scalar0/max", 1000, 10) + + add_image(writer, "train", "layer/image0", 7, 10, 1) + add_image(writer, "test", "layer/image0", 7, 10, 3) + + self.reader = storage.StorageReader(dir) + + def test_modes(self): + modes = lib.get_modes(self.reader) + self.assertEqual(sorted(modes), sorted(["train", "test", "valid"])) + + def test_scalar(self): + + for mode in "train test valid".split(): + tags = lib.get_scalar_tags(self.reader, mode) + print 'scalar tags:' + pprint.pprint(tags) + self.assertEqual(len(tags), 3) + self.assertEqual(sorted(tags.keys()), sorted("train test valid".split())) + + def test_image(self): + tags = lib.get_image_tags(self.reader) + self.assertEqual(len(tags), 2) + + tags = lib.get_image_tag_steps(self.reader, 'train', 'layer/image0/0') + pprint.pprint(tags) + + image = lib.get_invididual_image(self.reader, "train", 'layer/image0/0', 2) + print image + + +if __name__ == '__main__': + unittest.main() diff --git a/server/visualdl/mock.sh b/server/visualdl/mock.sh index 3ac2d91d3..966fa6899 100644 --- a/server/visualdl/mock.sh +++ b/server/visualdl/mock.sh @@ -3,4 +3,4 @@ set -ex export PYTHONPATH="/home/superjom/project/VisualDL/build/visualdl/logic:/home/superjom/project/VisualDL/visualdl/python" -python storage_mock.py +python lib_test.py diff --git a/server/visualdl/storage_mock.py b/server/visualdl/storage_mock.py index 67277b0c5..5ba26b15f 100644 --- a/server/visualdl/storage_mock.py +++ b/server/visualdl/storage_mock.py @@ -26,11 +26,12 @@ def add_image(writer, for pass_ in xrange(num_passes): image_writer.start_sampling() for ins in xrange(2 * num_samples): - print '.', index = image_writer.is_sample_taken() if index != -1: data = np.random.random(shape) * 256 data = np.ndarray.flatten(data) + assert shape + assert len(data) > 0 image_writer.set_sample(index, shape, list(data)) image_writer.finish_sampling() diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index 94b37bd93..e90807a68 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -89,7 +89,7 @@ def logdir(): @app.route('/data/runs') def runs(): modes = storage.modes() - result = gen_result(0, "", lib.modes()) + result = gen_result(0, "", lib.get_modes()) return Response(json.dumps(result), mimetype='application/json') diff --git a/tests.sh b/tests.sh index 99700941f..92d527fae 100644 --- a/tests.sh +++ b/tests.sh @@ -26,6 +26,8 @@ frontend_test() { } server_test() { + cd $cur/server + sh build.sh cd $cur/server/visualdl python lib_test.py } diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 0eacb2fec..bb633b04b 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -48,9 +48,8 @@ template class ScalarReader; template class ScalarReader; void Image::StartSampling() { - // TODO(ChunweiYan) big bug here, every step will be stored in protobuf - // and that might result in explosion in some scenerios, Just sampling - // some steps should be better. + if (!ToSampleThisStep()) return; + step_ = writer_.AddRecord(); step_.SetId(step_id_); @@ -103,6 +102,7 @@ void Image::SetSample(int index, // production int size = std::accumulate( shape.begin(), shape.end(), 1., [](float a, float b) { return a * b; }); + CHECK_GT(size, 0); CHECK_EQ(size, data.size()) << "image's shape not match data"; CHECK_LT(index, num_samples_); CHECK_LE(index, num_records_); From bb271f1df1008a143c4ae8c99e8e91f2d10fe4bd Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 14:45:13 +0800 Subject: [PATCH 17/29] fix bash --- tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests.sh b/tests.sh index 92d527fae..b804afb84 100644 --- a/tests.sh +++ b/tests.sh @@ -27,7 +27,7 @@ frontend_test() { server_test() { cd $cur/server - sh build.sh + bash build.sh cd $cur/server/visualdl python lib_test.py } From 438a934e17092b58e93afde092f30467f4b734bd Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 16:26:16 +0800 Subject: [PATCH 18/29] add Pillow pip --- tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests.sh b/tests.sh index b804afb84..f731e65bd 100644 --- a/tests.sh +++ b/tests.sh @@ -11,6 +11,7 @@ export PYTHONPATH="${core_path}:${python_path}" backend_test() { cd $cur sudo pip install numpy + sudo pip install Pillow mkdir -p build cd build cmake .. From f3274a24578703c582a049ca6f9d75c15cebfce3 Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 16:42:26 +0800 Subject: [PATCH 19/29] add channel support --- server/visualdl/lib_test.py | 3 +++ visualdl/logic/sdk.cc | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/server/visualdl/lib_test.py b/server/visualdl/lib_test.py index f167af099..117cc2d89 100644 --- a/server/visualdl/lib_test.py +++ b/server/visualdl/lib_test.py @@ -21,6 +21,9 @@ def setUp(self): add_image(writer, "train", "layer/image0", 7, 10, 1) add_image(writer, "test", "layer/image0", 7, 10, 3) + add_image(writer, "train", "layer/image1", 7, 10, 1, shape=[30,30,2]) + add_image(writer, "test", "layer/image1", 7, 10, 1, shape=[30,30,2]) + self.reader = storage.StorageReader(dir) def test_modes(self): diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index bb633b04b..4303e7340 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -103,6 +103,10 @@ void Image::SetSample(int index, int size = std::accumulate( shape.begin(), shape.end(), 1., [](float a, float b) { return a * b; }); CHECK_GT(size, 0); + CHECK_EQ(shape.size(), 3) + << "shape should be something like (width, height, num_channel)"; + CHECK_LE(shape.back(), 3); + CHECK_GE(shape.back(), 2); CHECK_EQ(size, data.size()) << "image's shape not match data"; CHECK_LT(index, num_samples_); CHECK_LE(index, num_records_); From 1cf7781a067c820b73edbe942a4c204a3d64eec4 Mon Sep 17 00:00:00 2001 From: superjom Date: Thu, 28 Dec 2017 17:00:11 +0800 Subject: [PATCH 20/29] import eigen to cmake --- CMakeLists.txt | 1 + cmake/external/eigen.cmake | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 cmake/external/eigen.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 83921c41c..6365d0bb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ include(external/zlib) # download, build, install zlib include(external/gflags) # download, build, install gflags include(external/glog) # download, build, install glog include(external/gtest) # download, build, install gtest +include(external/eigen) # download eigen include(external/pybind11) # download pybind11 include(external/protobuf) # download, build, install protobuf include(external/python) # find python and set path diff --git a/cmake/external/eigen.cmake b/cmake/external/eigen.cmake new file mode 100644 index 000000000..f7483f6be --- /dev/null +++ b/cmake/external/eigen.cmake @@ -0,0 +1,30 @@ +INCLUDE(ExternalProject) + +SET(EIGEN_SOURCE_DIR ${THIRD_PARTY_PATH}/eigen3) + +INCLUDE_DIRECTORIES(${EIGEN_SOURCE_DIR}/src/extern_eigen3) + +ExternalProject_Add( + extern_eigen3 + ${EXTERNAL_PROJECT_LOG_ARGS} + GIT_REPOSITORY "https://github.com/RLovelett/eigen.git" + GIT_TAG "master" + PREFIX ${EIGEN_SOURCE_DIR} + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) + +if (${CMAKE_VERSION} VERSION_LESS "3.3.0") + set(dummyfile ${CMAKE_CURRENT_BINARY_DIR}/eigen3_dummy.c) + file(WRITE ${dummyfile} "const char * dummy_eigen3 = \"${dummyfile}\";") + add_library(eigen3 STATIC ${dummyfile}) +else() + add_library(eigen3 INTERFACE) +endif() + +add_dependencies(eigen3 extern_eigen3) + +LIST(APPEND external_project_dependencies eigen3) From c9ccb1264665bf95b9a4031b646bd36e8fab4794 Mon Sep 17 00:00:00 2001 From: superjom Date: Fri, 29 Dec 2017 15:13:00 +0800 Subject: [PATCH 21/29] image ready --- CMakeLists.txt | 5 +++-- visualdl/logic/CMakeLists.txt | 4 ++-- visualdl/logic/sdk.cc | 5 +++-- visualdl/logic/sdk_test.cc | 2 +- visualdl/python/test_storage.py | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6365d0bb5..42de25a5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,8 @@ include(external/python) # find python and set path include_directories(${PROJECT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${PROJECT_SOURCE_DIR}/thirdparty/local/include) +# TODO(ChunweiYan) debug, remote latter +include_directories(/home/superjom/project/VisualDL/build/third_party/eigen3/src/extern_eigen3) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/storage) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/logic) @@ -38,9 +39,9 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/python) add_executable(vl_test ${PROJECT_SOURCE_DIR}/visualdl/test.cc - ${PROJECT_SOURCE_DIR}/visualdl/storage/storage_test.cc ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk_test.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/test_concurrency.cc + ${PROJECT_SOURCE_DIR}/visualdl/utils/test_image.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/concurrency.h ${PROJECT_SOURCE_DIR}/visualdl/utils/filesystem.h ) diff --git a/visualdl/logic/CMakeLists.txt b/visualdl/logic/CMakeLists.txt index f38c9e73b..44c6579ec 100644 --- a/visualdl/logic/CMakeLists.txt +++ b/visualdl/logic/CMakeLists.txt @@ -1,10 +1,10 @@ add_library(im ${PROJECT_SOURCE_DIR}/visualdl/logic/im.cc) -add_library(sdk ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk.cc) +add_library(sdk ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/image.h) add_dependencies(im storage_proto) add_dependencies(sdk entry storage storage_proto) ## pybind add_library(core SHARED ${PROJECT_SOURCE_DIR}/visualdl/logic/pybind.cc) -add_dependencies(core pybind python im entry tablet storage sdk protobuf glog) +add_dependencies(core pybind python im entry tablet storage sdk protobuf glog eigen3) target_link_libraries(core PRIVATE pybind entry python im tablet storage sdk protobuf glog) set_target_properties(core PROPERTIES PREFIX "" SUFFIX ".so") diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 4303e7340..069ef9551 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -1,5 +1,7 @@ #include "visualdl/logic/sdk.h" +#include "visualdl/utils/image.h" + namespace visualdl { namespace components { @@ -101,7 +103,7 @@ void Image::SetSample(int index, const std::vector& data) { // production int size = std::accumulate( - shape.begin(), shape.end(), 1., [](float a, float b) { return a * b; }); + shape.begin(), shape.end(), 1., [](int a, int b) { return a * b; }); CHECK_GT(size, 0); CHECK_EQ(shape.size(), 3) << "shape should be something like (width, height, num_channel)"; @@ -111,7 +113,6 @@ void Image::SetSample(int index, CHECK_LT(index, num_samples_); CHECK_LE(index, num_records_); - // set data auto entry = step_.MutableData>(index); // trick to store int8 to protobuf std::vector data_str(data.size()); diff --git a/visualdl/logic/sdk_test.cc b/visualdl/logic/sdk_test.cc index 2b4c8e98d..b82e25437 100644 --- a/visualdl/logic/sdk_test.cc +++ b/visualdl/logic/sdk_test.cc @@ -56,7 +56,7 @@ TEST(Image, test) { for (int step = 0; step < num_steps; step++) { image.StartSampling(); for (int i = 0; i < 7; i++) { - vector shape({3, 5, 5}); + vector shape({5, 5, 3}); vector data; for (int j = 0; j < 3 * 5 * 5; j++) { data.push_back(float(rand()) / RAND_MAX); diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 05ae2f24c..b75410278 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -36,7 +36,7 @@ def test_image(self): image_writer = self.writer.image(tag, 10, 1) num_passes = 10 num_samples = 100 - shape = [3, 10, 10] + shape = [10, 10, 3] for pass_ in xrange(num_passes): image_writer.start_sampling() From 5f7399282ad487c3d1689548ffdd2dd43d373e1f Mon Sep 17 00:00:00 2001 From: superjom Date: Fri, 29 Dec 2017 15:15:14 +0800 Subject: [PATCH 22/29] restore storage_test --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42de25a5e..23e83ab88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ include(external/python) # find python and set path include_directories(${PROJECT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) # TODO(ChunweiYan) debug, remote latter -include_directories(/home/superjom/project/VisualDL/build/third_party/eigen3/src/extern_eigen3) +#include_directories(/home/superjom/project/VisualDL/build/third_party/eigen3/src/extern_eigen3) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/storage) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/logic) @@ -40,6 +40,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/python) add_executable(vl_test ${PROJECT_SOURCE_DIR}/visualdl/test.cc ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk_test.cc + ${PROJECT_SOURCE_DIR}/visualdl/storage/storage_test.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/test_concurrency.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/test_image.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/concurrency.h From 7e408c42fca85d8e25c91aae7c86d51b24ea582a Mon Sep 17 00:00:00 2001 From: superjom Date: Fri, 29 Dec 2017 15:18:40 +0800 Subject: [PATCH 23/29] Merge branch 'feature/add_image_component-eigen_support_transform' into feature/add_image_component --- CMakeLists.txt | 7 +++++-- cmake/external/eigen.cmake | 30 ++++++++++++++++++++++++++++++ visualdl/logic/CMakeLists.txt | 4 ++-- visualdl/logic/sdk.cc | 5 +++-- visualdl/logic/sdk_test.cc | 2 +- visualdl/python/test_storage.py | 2 +- 6 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 cmake/external/eigen.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 83921c41c..23e83ab88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,13 +23,15 @@ include(external/zlib) # download, build, install zlib include(external/gflags) # download, build, install gflags include(external/glog) # download, build, install glog include(external/gtest) # download, build, install gtest +include(external/eigen) # download eigen include(external/pybind11) # download pybind11 include(external/protobuf) # download, build, install protobuf include(external/python) # find python and set path include_directories(${PROJECT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${PROJECT_SOURCE_DIR}/thirdparty/local/include) +# TODO(ChunweiYan) debug, remote latter +#include_directories(/home/superjom/project/VisualDL/build/third_party/eigen3/src/extern_eigen3) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/storage) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/logic) @@ -37,9 +39,10 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/visualdl/python) add_executable(vl_test ${PROJECT_SOURCE_DIR}/visualdl/test.cc - ${PROJECT_SOURCE_DIR}/visualdl/storage/storage_test.cc ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk_test.cc + ${PROJECT_SOURCE_DIR}/visualdl/storage/storage_test.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/test_concurrency.cc + ${PROJECT_SOURCE_DIR}/visualdl/utils/test_image.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/concurrency.h ${PROJECT_SOURCE_DIR}/visualdl/utils/filesystem.h ) diff --git a/cmake/external/eigen.cmake b/cmake/external/eigen.cmake new file mode 100644 index 000000000..f7483f6be --- /dev/null +++ b/cmake/external/eigen.cmake @@ -0,0 +1,30 @@ +INCLUDE(ExternalProject) + +SET(EIGEN_SOURCE_DIR ${THIRD_PARTY_PATH}/eigen3) + +INCLUDE_DIRECTORIES(${EIGEN_SOURCE_DIR}/src/extern_eigen3) + +ExternalProject_Add( + extern_eigen3 + ${EXTERNAL_PROJECT_LOG_ARGS} + GIT_REPOSITORY "https://github.com/RLovelett/eigen.git" + GIT_TAG "master" + PREFIX ${EIGEN_SOURCE_DIR} + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) + +if (${CMAKE_VERSION} VERSION_LESS "3.3.0") + set(dummyfile ${CMAKE_CURRENT_BINARY_DIR}/eigen3_dummy.c) + file(WRITE ${dummyfile} "const char * dummy_eigen3 = \"${dummyfile}\";") + add_library(eigen3 STATIC ${dummyfile}) +else() + add_library(eigen3 INTERFACE) +endif() + +add_dependencies(eigen3 extern_eigen3) + +LIST(APPEND external_project_dependencies eigen3) diff --git a/visualdl/logic/CMakeLists.txt b/visualdl/logic/CMakeLists.txt index f38c9e73b..44c6579ec 100644 --- a/visualdl/logic/CMakeLists.txt +++ b/visualdl/logic/CMakeLists.txt @@ -1,10 +1,10 @@ add_library(im ${PROJECT_SOURCE_DIR}/visualdl/logic/im.cc) -add_library(sdk ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk.cc) +add_library(sdk ${PROJECT_SOURCE_DIR}/visualdl/logic/sdk.cc ${PROJECT_SOURCE_DIR}/visualdl/utils/image.h) add_dependencies(im storage_proto) add_dependencies(sdk entry storage storage_proto) ## pybind add_library(core SHARED ${PROJECT_SOURCE_DIR}/visualdl/logic/pybind.cc) -add_dependencies(core pybind python im entry tablet storage sdk protobuf glog) +add_dependencies(core pybind python im entry tablet storage sdk protobuf glog eigen3) target_link_libraries(core PRIVATE pybind entry python im tablet storage sdk protobuf glog) set_target_properties(core PROPERTIES PREFIX "" SUFFIX ".so") diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 4303e7340..069ef9551 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -1,5 +1,7 @@ #include "visualdl/logic/sdk.h" +#include "visualdl/utils/image.h" + namespace visualdl { namespace components { @@ -101,7 +103,7 @@ void Image::SetSample(int index, const std::vector& data) { // production int size = std::accumulate( - shape.begin(), shape.end(), 1., [](float a, float b) { return a * b; }); + shape.begin(), shape.end(), 1., [](int a, int b) { return a * b; }); CHECK_GT(size, 0); CHECK_EQ(shape.size(), 3) << "shape should be something like (width, height, num_channel)"; @@ -111,7 +113,6 @@ void Image::SetSample(int index, CHECK_LT(index, num_samples_); CHECK_LE(index, num_records_); - // set data auto entry = step_.MutableData>(index); // trick to store int8 to protobuf std::vector data_str(data.size()); diff --git a/visualdl/logic/sdk_test.cc b/visualdl/logic/sdk_test.cc index 2b4c8e98d..b82e25437 100644 --- a/visualdl/logic/sdk_test.cc +++ b/visualdl/logic/sdk_test.cc @@ -56,7 +56,7 @@ TEST(Image, test) { for (int step = 0; step < num_steps; step++) { image.StartSampling(); for (int i = 0; i < 7; i++) { - vector shape({3, 5, 5}); + vector shape({5, 5, 3}); vector data; for (int j = 0; j < 3 * 5 * 5; j++) { data.push_back(float(rand()) / RAND_MAX); diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 05ae2f24c..b75410278 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -36,7 +36,7 @@ def test_image(self): image_writer = self.writer.image(tag, 10, 1) num_passes = 10 num_samples = 100 - shape = [3, 10, 10] + shape = [10, 10, 3] for pass_ in xrange(num_passes): image_writer.start_sampling() From 34caf1a3d50cf65c0760591c67de9e15489946a2 Mon Sep 17 00:00:00 2001 From: superjom Date: Fri, 29 Dec 2017 15:40:19 +0800 Subject: [PATCH 24/29] add image --- visualdl/utils/image.h | 84 ++++++++++++++++++++++++++++++++++++ visualdl/utils/test_image.cc | 19 ++++++++ 2 files changed, 103 insertions(+) create mode 100644 visualdl/utils/image.h create mode 100644 visualdl/utils/test_image.cc diff --git a/visualdl/utils/image.h b/visualdl/utils/image.h new file mode 100644 index 000000000..cf4bd5144 --- /dev/null +++ b/visualdl/utils/image.h @@ -0,0 +1,84 @@ +#ifndef VISUALDL_UTILS_IMAGE_H +#define VISUALDL_UTILS_IMAGE_H + +#include +#include +#include + +namespace visualdl { + +using uint8_t = unsigned char; + +/* + * 2: height*width, channel + */ +template +using ImageDT = Eigen::Matrix; +using Uint8Image = ImageDT; + +/* + * hw: height*width + * depth: number of channels + */ +static void NormalizeImage(Uint8Image* image, + float* buffer, + int hw, + int depth) { + Eigen::Map values(buffer, depth, hw); + + CHECK_EQ(image->size(), hw * depth); + CHECK_EQ(image->row(0).size(), hw); + CHECK_EQ(image->col(0).size(), depth); + + std::vector infinite_pixels; + // compute min and max ignoring nonfinite pixels + float image_min = std::numeric_limits::infinity(); + float image_max = -image_min; + for (int i = 0; i < hw; i++) { + bool finite = true; + for (int j = 0; j < depth; j++) { + // if infinite, skip this pixel + if (!std::isfinite(values(j, i))) { + infinite_pixels.emplace_back(i); + finite = false; + break; + } + } + if (finite) { + for (int j = 0; j < depth; j++) { + float v = values(j, i); + image_min = std::min(image_min, v); + image_max = std::max(image_max, v); + } + } + } + + // Pick an affine transform into uint8 + const float kZeroThreshold = 1e-6; + float scale, offset; + if (image_min < 0) { + float max_val = std::max(std::abs(image_min), image_max); + scale = (max_val < kZeroThreshold ? 0.0f : 127.0f) / max_val; + } else { + scale = + (image_max < image_max < kZeroThreshold ? 0.0f : 255.0f) / image_max; + offset = 0.0f; + } + + // Transform image, turning nonfinite values to bad_color + for (int i = 0; i < depth; i++) { + auto tmp = scale * values.row(i).array() + offset; + image->row(i) = tmp.cast(); + } + + for (int pixel : infinite_pixels) { + for (int i = 0; i < depth; i++) { + // TODO(ChunweiYan) use some highlight color to represent infinite pixels. + (*image)(pixel, i) = (uint8_t)0; + } + } +} + +} // namespace visualdl + +#endif diff --git a/visualdl/utils/test_image.cc b/visualdl/utils/test_image.cc new file mode 100644 index 000000000..c2a6558dd --- /dev/null +++ b/visualdl/utils/test_image.cc @@ -0,0 +1,19 @@ +#include "visualdl/utils/image.h" + +#include + +using namespace visualdl; + +TEST(image, NormalizeImage) { + Uint8Image image(128, 3); + const int size = 128 * 3; + float arr[size]; + + for (int i = 0; i < size; i++) { + // set a strange scale + arr[i] = 234. * (rand() / RAND_MAX - 0.5); + } + + NormalizeImage(&image, arr, 3, 128); +} + From 23a4be3076e479f108f420ff2d7adb0fc9bb44a9 Mon Sep 17 00:00:00 2001 From: superjom Date: Sun, 31 Dec 2017 15:27:03 +0800 Subject: [PATCH 25/29] add image consistent --- visualdl/logic/im.cc | 2 +- visualdl/logic/pybind.cc | 5 +++- visualdl/logic/sdk.cc | 12 +++++----- visualdl/python/test_storage.py | 41 +++++++++++++++++++++++++++++++++ visualdl/storage/entry.cc | 12 +++++----- visualdl/storage/entry.h | 6 +++++ 6 files changed, 64 insertions(+), 14 deletions(-) diff --git a/visualdl/logic/im.cc b/visualdl/logic/im.cc index 6162c0e0f..ff6da2478 100644 --- a/visualdl/logic/im.cc +++ b/visualdl/logic/im.cc @@ -37,7 +37,7 @@ template class SimpleWriteSyncGuard>; template class SimpleWriteSyncGuard>; template class SimpleWriteSyncGuard>; template class SimpleWriteSyncGuard>; -template class SimpleWriteSyncGuard>>; +template class SimpleWriteSyncGuard>>; template class SimpleWriteSyncGuard>; } // namespace visualdl diff --git a/visualdl/logic/pybind.cc b/visualdl/logic/pybind.cc index 425b75fec..3fbe53eb6 100644 --- a/visualdl/logic/pybind.cc +++ b/visualdl/logic/pybind.cc @@ -93,7 +93,10 @@ PYBIND11_PLUGIN(core) { py::class_(m, "ImageRecord") // TODO(ChunweiYan) make these copyless. - .def("data", [](cp::ImageReader::ImageRecord& self) { return self.data; }) + .def("data", + [](cp::ImageReader::ImageRecord& self) { + return self.data; + }) .def("shape", [](cp::ImageReader::ImageRecord& self) { return self.shape; }) .def("step_id", diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 069ef9551..8b7f0fd34 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -113,13 +113,13 @@ void Image::SetSample(int index, CHECK_LT(index, num_samples_); CHECK_LE(index, num_records_); - auto entry = step_.MutableData>(index); + auto entry = step_.MutableData>(index); // trick to store int8 to protobuf - std::vector data_str(data.size()); + std::vector data_str(data.size()); for (int i = 0; i < data.size(); i++) { data_str[i] = data[i]; } - entry.Set(data_str); + entry.SetRaw(std::string(data_str.begin(), data_str.end())); static_assert( !is_same_type::value, @@ -145,13 +145,13 @@ std::string ImageReader::caption() { ImageReader::ImageRecord ImageReader::record(int offset, int index) { ImageRecord res; auto record = reader_.record(offset); - auto data_entry = record.data>(index); + auto data_entry = record.data>(index); auto shape_entry = record.data(index); - auto data_str = data_entry.Get(); + auto data_str = data_entry.GetRaw(); std::transform(data_str.begin(), data_str.end(), std::back_inserter(res.data), - [](char i) { return (int)((unsigned char)i); }); + [](byte_t i) { return (int)(i); }); res.shape = shape_entry.GetMulti(); res.step_id = record.id(); return res; diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index b75410278..e1d258817 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -1,6 +1,7 @@ import random import time import unittest +from PIL import Image import numpy as np @@ -62,6 +63,46 @@ def test_image(self): self.assertTrue(image_tags) self.assertEqual(len(image_tags), 1) + def test_check_image(self): + ''' + check whether the storage will keep image data consistent + ''' + print 'check image' + tag = "layer1/check/image1" + image_writer = self.writer.image(tag, 10, 1) + + image = Image.open("./dog.jpg") + shape = [image.size[1], image.size[0], 3] + origin_data = np.array(image.getdata()).flatten() + + self.reader = storage.StorageReader(self.dir).as_mode("train") + + image_writer.start_sampling() + index = image_writer.is_sample_taken() + image_writer.set_sample(index, shape, list(origin_data)) + image_writer.finish_sampling() + + # read and check whether the original image will be displayed + + image_reader = self.reader.image(tag) + image_record = image_reader.record(0, 0) + data = image_record.data() + shape = image_record.shape() + + PIL_image_shape = (shape[0]*shape[1], shape[2]) + data = np.array(data, dtype='uint8').reshape(PIL_image_shape) + print 'origin', origin_data.flatten() + print 'data', data.flatten() + image = Image.fromarray(data.reshape(shape)) + + self.assertTrue(np.equal(origin_data.reshape(PIL_image_shape), data).all()) + + + + + + + if __name__ == '__main__': unittest.main() diff --git a/visualdl/storage/entry.cc b/visualdl/storage/entry.cc index 5cf85c020..f2f2d0c64 100644 --- a/visualdl/storage/entry.cc +++ b/visualdl/storage/entry.cc @@ -22,14 +22,14 @@ namespace visualdl { } template <> -void Entry>::Set(std::vector v) { +void Entry>::Set(std::vector v) { entry->set_dtype(storage::DataType::kBytes); entry->set_y(std::string(v.begin(), v.end())); WRITE_GUARD } template <> -void Entry>::Add(std::vector v) { +void Entry>::Add(std::vector v) { entry->set_dtype(storage::DataType::kBytess); *entry->add_ys() = std::string(v.begin(), v.end()); WRITE_GUARD @@ -68,9 +68,9 @@ IMPL_ENTRY_GET(std::string, s); IMPL_ENTRY_GET(bool, b); template <> -std::vector EntryReader>::Get() const { +std::vector EntryReader>::Get() const { const auto& y = data_.y(); - return std::vector(y.begin(), y.end()); + return std::vector(y.begin(), y.end()); } #define IMPL_ENTRY_GET_MULTI(T, fieldname__) \ @@ -91,12 +91,12 @@ template class Entry; template class Entry; template class Entry; template class Entry; -template class Entry>; +template class Entry>; template class EntryReader; template class EntryReader; template class EntryReader; template class EntryReader; -template class EntryReader>; +template class EntryReader>; } // namespace visualdl diff --git a/visualdl/storage/entry.h b/visualdl/storage/entry.h index ba9542def..343fa310d 100644 --- a/visualdl/storage/entry.h +++ b/visualdl/storage/entry.h @@ -9,6 +9,8 @@ namespace visualdl { struct Storage; +using byte_t = unsigned char; + /* * Utility helper for storage::Entry. */ @@ -30,6 +32,8 @@ struct Entry { // Set a single value. void Set(T v); + void SetRaw(const std::string& bytes) { entry->set_y(bytes); } + // Add a value to repeated message field. void Add(T v); @@ -50,6 +54,8 @@ struct EntryReader { // Get repeated field. std::vector GetMulti() const; + std::string GetRaw() { return data_.y(); } + private: storage::Entry data_; }; From 23b35c70ca864348129c6745d41f57f0624413f9 Mon Sep 17 00:00:00 2001 From: superjom Date: Sun, 31 Dec 2017 16:34:20 +0800 Subject: [PATCH 26/29] add image check --- visualdl/logic/sdk.cc | 6 +++++- visualdl/python/test_storage.py | 13 ++++--------- visualdl/utils/image.h | 15 ++++++++++----- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/visualdl/logic/sdk.cc b/visualdl/logic/sdk.cc index 8b7f0fd34..7b0c74950 100644 --- a/visualdl/logic/sdk.cc +++ b/visualdl/logic/sdk.cc @@ -119,7 +119,11 @@ void Image::SetSample(int index, for (int i = 0; i < data.size(); i++) { data_str[i] = data[i]; } - entry.SetRaw(std::string(data_str.begin(), data_str.end())); + Uint8Image image(shape[2], shape[0] * shape[1]); + NormalizeImage(&image, &data[0], shape[0] * shape[1], shape[2]); + // entry.SetRaw(std::string(data_str.begin(), data_str.end())); + entry.SetRaw( + std::string(image.data(), image.data() + image.rows() * image.cols())); static_assert( !is_same_type::value, diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index e1d258817..78c0971ac 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -42,7 +42,7 @@ def test_image(self): for pass_ in xrange(num_passes): image_writer.start_sampling() for ins in xrange(num_samples): - index = image_writer.is_sample_taken() + index = image_writer.is_sample_taken() if index != -1: data = np.random.random(shape) * 256 data = np.ndarray.flatten(data) @@ -89,19 +89,14 @@ def test_check_image(self): data = image_record.data() shape = image_record.shape() - PIL_image_shape = (shape[0]*shape[1], shape[2]) + PIL_image_shape = (shape[0] * shape[1], shape[2]) data = np.array(data, dtype='uint8').reshape(PIL_image_shape) print 'origin', origin_data.flatten() print 'data', data.flatten() image = Image.fromarray(data.reshape(shape)) - self.assertTrue(np.equal(origin_data.reshape(PIL_image_shape), data).all()) - - - - - - + self.assertTrue( + np.equal(origin_data.reshape(PIL_image_shape), data).all()) if __name__ == '__main__': diff --git a/visualdl/utils/image.h b/visualdl/utils/image.h index cf4bd5144..77b44c37a 100644 --- a/visualdl/utils/image.h +++ b/visualdl/utils/image.h @@ -13,7 +13,8 @@ using uint8_t = unsigned char; * 2: height*width, channel */ template -using ImageDT = Eigen::Matrix; +using ImageDT = + Eigen::Matrix; using Uint8Image = ImageDT; /* @@ -21,10 +22,13 @@ using Uint8Image = ImageDT; * depth: number of channels */ static void NormalizeImage(Uint8Image* image, - float* buffer, + const float* buffer, int hw, int depth) { - Eigen::Map values(buffer, depth, hw); + // Both image and buffer should be used in row major. + Eigen::Map> + values(buffer, depth, hw); CHECK_EQ(image->size(), hw * depth); CHECK_EQ(image->row(0).size(), hw); @@ -60,11 +64,12 @@ static void NormalizeImage(Uint8Image* image, float max_val = std::max(std::abs(image_min), image_max); scale = (max_val < kZeroThreshold ? 0.0f : 127.0f) / max_val; } else { - scale = - (image_max < image_max < kZeroThreshold ? 0.0f : 255.0f) / image_max; + scale = (image_max < kZeroThreshold ? 0.0f : 255.0f) / image_max; offset = 0.0f; } + LOG(INFO) << "scale " << scale; + // Transform image, turning nonfinite values to bad_color for (int i = 0; i < depth; i++) { auto tmp = scale * values.row(i).array() + offset; From 1f0cca767294dc5acaa4191d0013e0142864d117 Mon Sep 17 00:00:00 2001 From: superjom Date: Mon, 1 Jan 2018 13:21:20 +0800 Subject: [PATCH 27/29] fix test --- visualdl/python/test_storage.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 78c0971ac..83e8d1244 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -94,9 +94,12 @@ def test_check_image(self): print 'origin', origin_data.flatten() print 'data', data.flatten() image = Image.fromarray(data.reshape(shape)) + # manully check the image and found that nothing wrong with the image storage. + image.show() - self.assertTrue( - np.equal(origin_data.reshape(PIL_image_shape), data).all()) + # after scale, elements are changed. + # self.assertTrue( + # np.equal(origin_data.reshape(PIL_image_shape), data).all()) if __name__ == '__main__': From 9652683d7a1e9421efe43d6e56a789e34684ec65 Mon Sep 17 00:00:00 2001 From: superjom Date: Mon, 1 Jan 2018 13:48:04 +0800 Subject: [PATCH 28/29] disable image show --- visualdl/python/test_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visualdl/python/test_storage.py b/visualdl/python/test_storage.py index 83e8d1244..078a95871 100644 --- a/visualdl/python/test_storage.py +++ b/visualdl/python/test_storage.py @@ -95,7 +95,7 @@ def test_check_image(self): print 'data', data.flatten() image = Image.fromarray(data.reshape(shape)) # manully check the image and found that nothing wrong with the image storage. - image.show() + # image.show() # after scale, elements are changed. # self.assertTrue( From c8116e33cf6ad0401ef3c8034123a2e7fb3d7bda Mon Sep 17 00:00:00 2001 From: superjom Date: Mon, 1 Jan 2018 16:17:15 +0800 Subject: [PATCH 29/29] add the dog image --- visualdl/python/dog.jpg | Bin 0 -> 15561 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 visualdl/python/dog.jpg diff --git a/visualdl/python/dog.jpg b/visualdl/python/dog.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c1df6ba3bd0fbb7fabf6a80529c4e545eeeef340 GIT binary patch literal 15561 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 p4A9rCuV*Y)8v`T*SrBmF-P)se6~TEF4|D0g