あなたがC++を使っているので、(お気軽者は、物事を簡単にするためにC++ OpenCVのAPIに切り替えてみましょう本当に必要な場合は、これを古いC APIに移植します)。
ここでのボトルネックは、イメージをディスクにエンコードして書き込む際のボトルネックになります。
最も簡単なアプローチは、所定のタイルの境界矩形を計算し、O(1)コスト(コピーなし)でサブ領域(ROI)を取得することです。それから、cv::imwrite
に適切なファイル名を付けて保存してください(boost::format
を使用してファイル名を生成しますが、std::stringstream
も使用できます)。
サンプルコード:
#include <opencv2/opencv.hpp>
#include <boost/format.hpp>
#include <cstdint>
#include <chrono>
#include <iostream>
void process_tile(cv::Mat const& src
, cv::Size const& roi_size
, int32_t tile_col
, int32_t tile_row)
{
cv::Rect roi_bounds(tile_col * roi_size.width
, tile_row * roi_size.height
, roi_size.width
, roi_size.height);
cv::Mat roi(src(roi_bounds));
std::string file_name(str(boost::format("tiles/tile_%03d_%03d.png")
% tile_col % tile_row));
cv::imwrite(file_name, roi);
}
void save_tiles(cv::Mat const& src, cv::Size const& roi_size)
{
CV_Assert(src.cols % roi_size.width == 0);
CV_Assert(src.rows % roi_size.height == 0);
int32_t const TILE_COLS(src.cols/roi_size.width);
int32_t const TILE_ROWS(src.rows/roi_size.height);
for (int32_t r(0); r < TILE_ROWS; ++r) {
for (int32_t c(0); c < TILE_COLS; ++c) {
process_tile(src, roi_size, c, r);
}
}
}
int main()
{
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;
// Generate some random test image...
cv::Mat src(1300, 2400, CV_8UC3);
cv::randu(src, 0, 256);
cv::Size const ROI_SIZE(10, 10); // width, height
high_resolution_clock::time_point t1 = high_resolution_clock::now();
save_tiles(src, ROI_SIZE);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(t2 - t1).count();
double t_ms(static_cast<double>(duration)/1000.0);
std::cout << "Processed in " << t_ms << " ms\n";
return 0;
}
これはここの周りに21.4秒で走る、しかし私はそれだけで利用できるCPUの〜5%を使っていることがわかります。これを並列化することでもっとうまくいくかもしれないようです。
まず、少し考えてみましょうimwrite
。 100ピクセルの画像では、PNGが使用するzlib圧縮ではそれほど大きな違いはなく、ファイルが数百バイト(今日のファイルシステムブロックサイズよりもはるかに小さい)なので、ディスクの使用にはあまり役立ちません。これは、少し速くするために不要な圧縮を無効にできることを意味します。我々は何も失っていないことから、利益の多くが、それはまだ価値がない -
std::vector<int32_t> compression_params{CV_IMWRITE_PNG_COMPRESSION , 0};
cv::imwrite(file_name, roi, compression_params);
はこれが20.7秒かかります。
これを並列化してみましょう。 OpenCVはこのための便利なツールを提供しています - cv::parallel_for_
私たちの仕事のほとんどを行います。
サンプルコード:
#include <opencv2/opencv.hpp>
#include <boost/format.hpp>
#include <cstdint>
#include <chrono>
#include <iostream>
#include <vector>
void process_tile(cv::Mat const& src
, cv::Size const& roi_size
, int32_t tile_col
, int32_t tile_row)
{
cv::Rect roi_bounds(tile_col * roi_size.width
, tile_row * roi_size.height
, roi_size.width
, roi_size.height);
cv::Mat roi(src(roi_bounds));
std::string file_name(str(boost::format("tiles/tile_%03d_%03d.png")
% tile_col % tile_row));
std::vector<int32_t> compression_params{ CV_IMWRITE_PNG_COMPRESSION , 0};
cv::imwrite(file_name, roi, compression_params);
}
class ParallelSaveTiles
: public cv::ParallelLoopBody
{
public:
ParallelSaveTiles(cv::Mat const& src, cv::Size const& roi_size)
: src(src)
, roi_size(roi_size)
{
}
virtual void operator()(cv::Range const& range) const
{
int32_t const TILE_COLS(src.cols/roi_size.width);
for (int32_t r(range.start); r < range.end; ++r) {
for (int32_t c(0); c < TILE_COLS; ++c) {
process_tile(src, roi_size, c, r);
}
}
}
private:
cv::Mat const& src;
cv::Size const& roi_size;
};
void save_tiles(cv::Mat const& src, cv::Size const& roi_size)
{
CV_Assert(src.cols % roi_size.width == 0);
CV_Assert(src.rows % roi_size.height == 0);
int32_t const TILE_ROWS(src.rows/roi_size.height);
ParallelSaveTiles parallel_impl(src, roi_size);
cv::parallel_for_(cv::Range(0, TILE_ROWS), parallel_impl);
}
int main()
{
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;
cv::Mat src(1300, 2400, CV_8UC3);
cv::randu(src, 0, 256);
cv::Size const ROI_SIZE(10, 10); // width, height
high_resolution_clock::time_point t1 = high_resolution_clock::now();
save_tiles(src, ROI_SIZE);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(t2 - t1).count();
double t_ms(static_cast<double>(duration)/1000.0);
std::cout << "Processed in " << t_ms << " ms\n";
return 0;
}
今では(Win10上のSSDへの書き込み、32ギガバイトのRAMを搭載したi7-4930K上)の周りに5.3秒で動作します。
createImage + copyではなくサブイメージングを使用します。あなたが古いC APIを使用しない場合、私はあなたにどのように表示するでしょう。 IplImageとC APIの場合は、自分で見つけなければなりません。 – Micka
MickaはC++を使用しているので、C++ APIを使って書き直すこともできます。速くしたいなら、['cv :: parallel_for_'](http://docs.opencv.org/trunk/d7/dff/tutorial_how_to_use_OpenCV_parallel_for_.html)で使えるように書くことができます。 –