2017-07-20 19 views
2

私は10x10ピクセルにスライスしたい大型の2400x1300 pngファイルを持っていますイメージを注文して別のファイルに保存します。私は複数の方法を見つけましたが、この大きなファイルサイズに対してどのファイルが最も効果的かはわかりません。パフォーマンスが重要な要素です。ここで10x10イメージ(opencv)に大きなイメージをスライスする効率的方法

は私にとって最も有望に見えたソリューションの1つです:

// source image 
IplImage *source = cvLoadImage("2400x1300.png", 1); 
int roiSize = 10; 
for(int j = 0; j < source->width/roiSize; ++j) { 
for(int i = 0; i < source->height/roiSize; ++i) {  
    cvSetImageROI(source, cvRect(i*roiSize, j*roiSize, roiSize, roiSize)); 

    // cropped image 
    IplImage *cropSource = cvCreateImage(cvGetSize(source), source->depth, source->nChannels); 

    // copy 
    cvCopy(source, cropSource, NULL); 

    // save 10x10 image as png 

    // always reset the ROI 
    cvResetImageROI(source); 
} 

}

+2

createImage + copyではなくサブイメージングを使用します。あなたが古いC APIを使用しない場合、私はあなたにどのように表示するでしょう。 IplImageとC APIの場合は、自分で見つけなければなりません。 – Micka

+0

MickaはC++を使用しているので、C++ APIを使って書き直すこともできます。速くしたいなら、['cv :: parallel_for_'](http://docs.opencv.org/trunk/d7/dff/tutorial_how_to_use_OpenCV_parallel_for_.html)で使えるように書くことができます。 –

答えて

3

あなたが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秒で動作します。

関連する問題