19

私は OpenCV C++ インターフェイスを頻繁に使用し、Mat をプライベート リソースとして使用する多くのクラスを設計しました。

最近、明示的にクローンを呼び出さない限り、常に画像データを共有リソースとして使用する Mat クラスが気になりました。私が書いたとしてもconst Mat、画像データが後で外部から変更されないかどうかはわかりません。

したがって、カプセル化を確実にするためにクローンを作成する必要があります。しかし、Mat を明示的に複製する必要がある場合の問題は、多くの場合不要でコストがかかることです。一方、共有イメージデータの必要性は roi セレクターに由来し、次のようなものを記述できることを理解しています: Mat m_small = m_big(my_roi).

私の質問は次のとおりです。

1.) cv::Mat クラスはかなり怠惰に複製されるべきではありませんか? そのため、ユーザーは外部から Mat を共有リソース ハンドラーとして見ることはありません。SharedMat実際の共有イメージデータが必要な場合のように、ユーザーは何かと呼ばれるクラスを明示的にインスタンス化するべきではありませんか?
2.)クラスのプライベート リソースとして cv::Mat の場合、常にクローンを作成するよりも優れた戦略はありますか?


更新: 「Mat::clone() データを変更する予定がない限り、使用しないでください。」(by Vadim Pisarevsky)
このアイデアには問題があります。 このクラスがある状況を考えてみましょう:

class Res_handler{
public:
  const Mat emit_mat(){ return m_treasure; } // I argue you are compelled to clone here.
private:
  Mat m_treasure;
};

この場合にそうでないclone場合は、書くことができます

Mat m_pirate = res_handler.emit_mat(); m_pirate = Scalar(0,0,0);

と の間で共有される画像データを介して、m_treasure内部で完全なブラックアウトが発生します。:) したがって、内部の偶発的な変更を避けるために、それが必要ですres_handlerm_piratem_treasurem_treasureclone

一方、このソリューションにも欠陥があります。

const Mat m_pirate = res_handler.emit_mat(); 

も変更される可能性があるためm_treasure、 の内容がm_pirateバックグラウンドで変更され、海賊のプログラマーにとって大きな頭痛の種になります。:)

4

4 に答える 4

13

はい、これは悪い設計です。内部で共有所有権を実装しているためMat、所有権ポリシーを選択する標準的な方法、つまりスマート ポインターと互換性がありません。基本的な問題は、データと所有権は直交しており、分離する必要があるということです。

それは変更可能であるため、 a でさえ にconst Mat似てconst shared_ptr<Mat>おり、含まMatれている が変更可能であるべきだと説明する方法はありませんshared_ptr<const Mat>finalこれは、Javaの問題とよく似ています。

Matと同じインターフェイスを公開するMatが、デフォルトの共有実装の上にコピー オン ライト動作を実装するクラスをラップすることで、これらの問題を回避できると思います。

于 2012-12-04T23:36:56.407 に答える
12

[恥知らずな広告]現在、answers.opencv.org があります。これは、OpenCV 固有の質問に対する一種の StackOverflow です。

質問に移ります:

  1. いいえ、これを実装する効率的な方法は見当たりません。もしそうなら、それについて議論しましょう。

  2. はい。Mat::clone()データを変更する予定がない限り、使用しないでください。参照カウントは、データが使用されなくなったときに、データの適切な割り当て解除を処理します。

于 2012-12-05T08:10:57.557 に答える
6

OPの質問に答えるには:

はい、間違いなく!数人が指摘しているように、OpenCV では、イメージへの const 参照を記述する可能性はありません。これは確かに欠陥です。「const cv::Mat&」は、C++ プログラマーが期待するものではありません。コード全体に clone() 呼び出しをまき散らし、そもそもデータ共有の利点を失うところまで自分自身を見つけたことがよくあります。

それを効率的に行う方法についてのVadimsの質問に答えるには:

API の変更なしではありませんが、これを効率的に行うことは絶対に可能です。Qtが Qt 4 以前の明示的な共有モデル (OpenCV の現在のモデルに類似) を放棄して、現在の暗黙的な共有(書き込み時のコピー) に大成功を収めた方法を見てください。基本的に、オブジェクトを変更する、または後でオブジェクトを変更する可能性のある参照を返すすべての関数呼び出しは、それを「逆参照」する必要があります。複数の参照がある場合は、コピーを作成します。

このコストは、イメージ操作の平均コストと比較するとごくわずかです。ピクセルごとに実行する必要がある場合にのみ、法外になります。そのため、クラスを 2 つに分ける必要があります。cv::Mat と cv::Mat_ によく似ています。暗黙の共有とコピーを担当するものと、IplImage のテンプレート化された単なるラッパーです。APIがどのように見えるかの例を次に示します(わかりやすくするために過度に明示的な名前を選択しました)。

// The following makes no unnecessary copies. Only a 
// couple of atomic increments and decrements.
const cv::Image img = cv::Image("lenna.bmp").toGray().brighter(0.3).inverted();

cv::Image copy(img);// Still no deep copy.

cv::ConstImageRef<char> src = img.constRef<char>();// Still no deep copy.

// This is where the copy(detach) happens.
// "img" is left untouched
cv::MutableImageRef<char> dst = copy.ref<char>();

// The following is as efficient as it has ever been.
for(int y = 0; y<dst.height(); y++)
    for(int x = 0; x<dst.width(); x++)
        dst.at(x, y) += src.at(x, y);

抜本的な変更を加えるにはあまりにも多くの OpenCV コードが浮かんでいて、OpenCV 3 で API を変更するためのウィンドウが閉じられていることに気付きましたが、新しい改善されたインターフェイスを追加できない理由がわかりません。

于 2014-10-02T11:37:30.790 に答える
2

Vadimの答えを追加して拡張し、このトピックに関するいくつかの考えを示します。

また、cv :: Matをさまざまな方法で幅広く使用し、その利点を享受しました。

プログラミングの一般的な真実は、プロジェクトのさまざまな対立するニーズのバランスを取る必要があるということです。それらの1つは、パフォーマンスと保守性です。そして、これは「時期尚早の最適化は悪である」ということで完全に解決されました。このアプローチは素晴らしいですが、多くのプログラマーは盲目的にそれに従います。

画像処理では、パフォーマンスが最も重要です。それがなければ、多くのプロジェクトは単に実行可能ではありません。したがって、画像を処理するときに最適化するのは時期尚早ではありません。これは、ミリ秒がカウントされる数少ないフィールドの1つであり、実行するすべてのことは品質と速度の両方で測定されます。また、C#、Java、またはユーザーインターフェイスの設計から来た場合、それを理解するのは難しいかもしれませんが、この速度の向上のために、オブジェクト指向設計の確立された慣行のいくつかを犠牲にする価値があります。

OpenCVのソースコードを見ると、最適化に非常に重点が置かれていることがわかります。SSEベースの関数、NEON関数、ポインタートリック、あらゆる種類のアルゴリズムの好奇心、グラフィックプロセッサの実装、OpenCLの実装、ルックアップテーブルなどです。これは、他のタイプのプロジェクトでは、やり過ぎ、維持が難しい、または「時期尚早の最適化」と見なされます。

アプリアーキテクチャの小さな変更(cv :: Mat割り当て戦略など)は、パフォーマンスに関して非常に大きな違いを生む可能性があります。クローンを作成する代わりに、組み込みデバイスで画像を共有すると、優れたガジェットと行き止まりの概念実証の違いが生じる可能性があります。

したがって、Vadimが提案された変更を実装する効率的な方法が見当たらないと述べたとき、彼はそれらの変更のパフォーマンスペナルティが利点をカバーしないことを提案しました。

このようなプロジェクト、作成や保守が困難ですが、それは良いことです。そして通常、イメージングプロジェクトの難しい部分は、適切なアルゴリズムを作成することです。それをカプセル化することは、最後にたった1%の作業です。

于 2012-12-07T21:17:00.877 に答える