本件に関して報告があります。
自分でいろいろ試行錯誤で探ったところ、NNCとnnablaの推論結果差異に対する要因を発見しました。
その結果をまとめて投稿します。
ネットワーク:DeepLabV3plusベース(加工あり)
用途:セマンティックセグメンテーション(領域分割)
入力画像:256x256 (24-bit, ※RGB)
推論出力:256x256 (8-bit, grayscale)
※元画像は8-bitだが、ネットワークの入力層は3-channel
0.0) OpenCVフォーマットの画像をそのまま用いる方法
// データの読み込み
cv::Mat img = cv::imread("data/test.png", cv::IMREAD_COLOR);
// Nnabla部分の初期化
nbla::Context cpu_ctx{{"cpu:float"}, "CpuCachedArray", "0"};
nbla::Context ctx = cpu_ctx;
nbla::utils::nnp::Nnp nnp(ctx);
const std::string nnp_filename = "model/export_model.nnp";
nnp.add(nnp_filename);
std::string executor_name("runtime");
auto executor = nnp.get_executor(executor_name);
executor->set_batch_size(1);
nbla::CgVariablePtr x = executor->get_data_variables().at(0).variable;
float *data = x->variable()->nbla::Variable::cast_data_and_get_pointer<float>(cpu_ctx);
// OpenCV -> nnabla展開
std::memcpy(data, img.data, 3 * 256 * 256 * sizeof(uint8_t));
0.1) 読み込んだ画像の正規化方法
// ピクセル毎の正規化
for (int i = 0; i < 256 * 256; i++)
{
data[i] = (float)img.data[i] / 255.0;
}
1) OpenCVの(height, width, channel)形式からnnablaの(channel, height, width)形式に変換(上記正規化処理を含む)
uint32_t HEIGHT = 256;
uint32_t WIDTH = 256;
uint32_t CHANNELS = 3;
// 注意:本題では8-bit元画像を使用しているため、BGR/RGBの変換は行っていない
for (int hw = 0; hw < WIDTH * HEIGHT; hw++)
{
for (int c = 0; c < CHANNELS; c++)
{
data[c * WIDTH * HEIGHT + hw] = (float)img.data[hw * CHANNELS + c] / 255.0;
}
}
2) 本題では8-bitの出力画像を求めるが、出力はfloatタイプの推論結果となるため、変換が必要。
ただし、切り上げではなく切り捨て処理を行う。
// 推論を実行
std::cout << "Executing..." << std::endl;
executor->execute();
// 推論結果を取りに行く
nbla::CgVariablePtr y = executor->get_output_variables().at(0).variable;
const float *y_data = y->variable()->get_data_pointer<float>(cpu_ctx);
// 切り捨て処理を行う
uint8_t y_data_aux[256*256];
for (int i = 0; i < 256 * 256; i++)
{
y_data_aux[i] = std::floor(y_data[i] * 255);
}
// OpenCV行列に展開
cv::Mat out(256, 256, CV_8U);
memcpy(out.data, y_data_aux, 256 * 256 * sizeof(uint8_t));
// データをディスクに書き込む
std::vector<int> compression_params;
compression_params.push_back(cv::ImwriteFlags::IMWRITE_PNG_STRATEGY);
cv::imwrite("data/out.png", out, compression_params);
3) NNCで学習が完了するにあたって、自動生成されるresults.nnpではなく、
明示的に「TRAINING」タブでモデルを
右クリック→エクスポート→NNP (Neural Network Libraries file format)
で生成されるmodel.nnpフアイルで推論を行う。
(1) ~ (3) を合わせて、NNCとnnablaで同一結果を得られました(ImageJにて確認)。ご参考までに。
以上