C++開発におけるデータ構造とアルゴリズムの分離についての簡単な説明

C++開発におけるデータ構造とアルゴリズムの分離についての簡単な説明

Windows でプログラムを書いたことがある人なら、誰でも多かれ少なかれビットマップを使ったことがあると思います。ほとんどの人は、成熟した完全な DIB クラス ライブラリ (CxImage、CDIB など) をインターネットからダウンロードして使用していますが、将来簡単に拡張して使用できるように、独自のカプセル化された DIB クラス ライブラリ セットを持っている人もいます。 (GDI+ は近年強力な勢力として登場しました。スケーリング、回転、グラデーションの塗りつぶしなど、特定の処理面で比類のない速度と品質を提供します。ただし、完全な画像処理プログラムを作成する場合、これを直接使用するとアーキテクチャ設計に困難が生じます。使用する前に、アダプター モードでカプセル化することができます)。

このとき、画像処理操作が必要になった場合はどうすればよいでしょうか? OO 経験のない多くのC++プログラマー (1 年前の私のように) は、クラスにメソッドを直接追加するかもしれません。

  1. int FClamp0255 ( int nValue) { return max (0, min (0xFF, nValue));} // 0~255に飽和 
  2. クラスFCObjImage
  3. {
  4.  公共
  5. 反転 () ;
  6. AdjustRGB ( int R, int G, int B);
  7. } ;
  8. void FCObjImage::Invert() 関数
  9. {
  10.  ((GetHandle() == NULL) || (ColorBits() < 24))の場合
  11. 戻る;
  12.  int nSpan = ColorBits() / 8 ; // ピクセルあたりのバイト数 3, 4  
  13.  ( int y=0 ; y < Height() ; y++)の場合
  14. {
  15. BYTE * pPixel = GetBits(y);
  16. ( int x = 0 ; x < 幅() ; x++, pPixel += nSpan)の場合
  17. {
  18. pPixel[0] = ~pPixel[0];
  19. pPixel[1] = ~pPixel[1];
  20. pPixel[2] = ~pPixel[2];
  21. }
  22. }
  23. }
  24. void FCObjImage::AdjustRGB ( int R, int G, int B)
  25. {
  26.  ((GetHandle() == NULL) || (ColorBits() < 24))の場合
  27. 戻る;
  28.  int nSpan = ColorBits() / 8 ; // ピクセルあたりのバイト数 3, 4  
  29.  ( int y=0 ; y < Height() ; y++)の場合
  30. {
  31. BYTE * pPixel = GetBits(y);
  32. ( int x = 0 ; x < 幅() ; x++, pPixel += nSpan)の場合
  33. {
  34. pPixel[0] = FClamp0255 (pPixel[0] + B);
  35. pPixel[1] = FClamp0255 (pPixel[1] + G);
  36. pPixel[2] = FClamp0255 (pPixel[2] + R);
  37. }
  38. }
  39. }

ここでは 2 つの例を示します (それぞれ、色の反転と RGB 値の調整を実現する)。実際には、明るさ、コントラスト、彩度など、このような操作は多数あります。さて、振り返ってみてください。これらのメソッドを追加するには、どのような手順を踏むのでしょうか。おおおお、RCP (同僚の発明、フルネーム: ラピッド コピー ペースト^-^)、最初の手順は、上記のコードの一部をコピーし、インターフェイスと処理部分を変更することです。ここでのデモ コードは短く、バグと一緒にコピーされることはありませんが、時限爆弾がもう 1 つあります。ある日、上司から「長い間待つのは耐えられないので、進捗バーを追加してください…」と言われました。グローバル変数を追加したり、各関数にパラメータを追加したりすることもできますが、同じことが続きます。つまり、これらすべての処理関数のコードを変更する必要があり、内部の呪いによって、それらのいずれかを変更する必要がなくなるわけではありません。この時点で、バグはすでに攻撃の機会をうかがっています... しかし、苦難の時代はまだまだ終わりません。 1 か月後、上司から突然、地域処理機能の追加を依頼され、さらに 1 か月後...

戻ってコードをもう一度見てみますか?そうです、赤いコードを除いて、他のすべてはまったく同じなので、これらのアルゴリズムを分離することは可能ですか?標準ライブラリの qsort や windows でよく使用されるコールバック メソッドをすぐに思い浮かべるかもしれません。では、これを実装してみましょう:

  1. voidピクセル反転 ( BYTE * pPixel)
  2. {
  3. pPixel[0] = ~pPixel[0];
  4. pPixel[1] = ~pPixel[1];
  5. pPixel[2] = ~pPixel[2];
  6. }
  7. void FCObjImage::PixelProcess ( void (__cdecl*PixelProc)( BYTE * pPixel))
  8. {
  9.  ((GetHandle() == NULL) || (ColorBits() < 24))の場合
  10. 戻る;
  11.  int nSpan = ColorBits() / 8 ; // ピクセルあたりのバイト数 3, 4  
  12.  ( int y=0 ; y < Height() ; y++)の場合
  13. {
  14. BYTE * pPixel = GetBits(y);
  15. ( int x = 0 ; x < 幅() ; x++, pPixel += nSpan)の場合
  16. {
  17. ピクセルプロセス (pPixel) ;
  18. }
  19. }
  20. }
  21. void FCObjImage::Invert() 関数
  22. {
  23. ピクセル処理 (Pixel_Invert) ;
  24. }

まあ、それは良いことです。アルゴリズムが 1 つの関数にまで削減され、問題は解決したようです。 Invert の処理はうまくいっていますが、AdjustRGB の処理で問題が発生しました。RGB の 3 つの調整パラメータを渡すにはどうすればよいでしょうか?グローバル変数/メンバー変数を追加することで、インターフェイスにパラメーターが 1 つだけになりますか?これは一つの方法ですが、クラスメソッドの数が増えるとプログラムの可読性や保守性が急激に低下し、変更前よりも影響が悪くなります。

では、高度な抽象化と優れたインターフェースを実現するにはどうすればよいでしょうか?私たちは OO (オブジェクト指向) を現場に招き、その実装について話をしてもらいました。次の派生関係を設計します。

  1. クラスFCSinglePixelProcessBase
  2. {
  3.  公共
  4. バーチャル  void ProcessPixel ( int x, int y, BYTE * pPixel) PURE ;
  5. } ;
  6. クラスFCPixelInvert:パブリックFCSinglePixelProcessBase
  7. {
  8.  公共
  9. バーチャル  void ProcessPixel ( int x, int y, BYTE * pPixel) ;
  10. } ;
  11. void FCPixelInvert::ProcessPixel ( int x, int y, BYTE * pPixel)
  12. {
  13. pPixel[0] = ~pPixel[0]; pPixel[1] = ~pPixel[1]; pPixel[2] = ~pPixel[2];
  14. }
  15. クラスFCPixelAdjustRGB :パブリックFCSinglePixelProcessBase
  16. {
  17.  公共
  18. FCPixelAdjustRGB ( int DeltaR、 int DeltaG、 int DeltaB) ;
  19. バーチャル  void ProcessPixel ( int x, int y, BYTE * pPixel) ;
  20.  保護されています:
  21. 整数m_iDeltaR、m_iDeltaG、m_iDeltaB;
  22. } ;
  23. void FCPixelAdjustRGB::ProcessPixel ( int x, int y, BYTE * pPixel)
  24. {
  25. pPixel[0] = FClamp0255 (pPixel[0] + m_iDeltaB);
  26. pPixel[1] = FClamp0255 (pPixel[1] + m_iDeltaG);
  27. pPixel[2] = FClamp0255 (pPixel[2] + m_iDeltaR);
  28. }

次に、イメージクラスを次のように変更します。

  1. #include "PixelProcessor.h"  
  2. クラスFCObjImage
  3. {
  4.  公共
  5. void PixelHandler (FCSinglePixelProcessBase & PixelProcessor、FCObjProgress * 進行状況 = NULL);
  6. } ;
  7. void FCObjImage::PixelHandler (FCSinglePixelProcessBase & PixelProcessor、FCObjProgress * 進行状況)
  8. {
  9.  (GetHandle() == NULL)の場合
  10. 戻る;
  11.  int nSpan = ColorBits() / 8 ; // ピクセルあたりのバイト数 3, 4  
  12.  ( int y=0 ; y < Height() ; y++)の場合
  13. {
  14. BYTE * pPixel = GetBits(y);
  15. ( int x = 0 ; x < 幅() ; x++, pPixel += nSpan)の場合
  16. {
  17. PixelProcessor.ProcessPixel(x, y, pPixel);
  18. }
  19. (進捗状況がNULLではない場合
  20. progress->SetProgress(y * 100 / Height());
  21. }
  22. }
  23. void FCObjImage::Invert (FCObjProgress * 進行状況)
  24. {
  25. PixelHandler (FCPixelInvert()、進行状況);
  26. }
  27. void FCObjImage::AdjustRGB ( int R, int G, int B, FCObjProgress * 進行状況)
  28. {
  29. PixelHandler (FCPixelAdjustRGB (R,G,B)、進行状況) ;
  30. }

(上記は単なる基本的なフレームワークであり、構築時に RECT パラメータを渡すことで、領域処理パラメータを簡単に追加できます。)

オブジェクトは本当に素晴らしいものです。オブジェクトは、外の世界へのシンプルなインターフェースを提供することができ、また、オブジェクト自体に多くの追加情報をカプセル化することができます。

さて、結果をテストしましょう。画像の奇数行を黒に、偶数行を白に設定する操作を追加します。

  1. クラスFCPixelTest:パブリックFCSinglePixelProcessBase
  2. {
  3.  公共
  4. バーチャル  void ProcessPixel ( int x, int y, BYTE * pPixel) ;
  5. } ;
  6. void FCPixelTest::ProcessPixel ( int x, int y, BYTE * pPixel)
  7. {
  8.  (y % 2) pPixel[0]=pPixel[1]=pPixel[2] = 0の場合;
  9.  // 奇数行 
  10.  それ以外   
  11. pPixel[0]=pPixel[1]=pPixel[2] = 0xFF;
  12. // 偶数行 
  13. }

次に、次の呼び出しを行います。

  1. PixelHandler (FCPixelTest()、進行状況);

なんと調和がとれていて美しいのでしょう。アルゴリズム設計者は、プログレスバーや領域をどのようにサポートするかを考える必要はなく、独自のアルゴリズムを記述するだけで済みます。よくデザインされたAKのような感じで、弾(オブジェクト)を追加し続けることができます^-^

この時点で、作業は完了しているはずです。まだ質問がありますか?

待ってください、急がないでください。何かがおかしいのです。このアルゴリズムを追加した後、コンパイルになぜこんなに時間がかかるのでしょうか?

問題は、目立たないことにあります。

  1. #include "PixelProcessor.h"  

画像は、画像処理の最も低レベルのオブジェクトです。プロジェクト内のすべてのファイルは、これを直接的または間接的に含みます。したがって、image.h 自体とそれに含まれる .h ファイルを変更すると、ほぼプロジェクト全体がビルドされます。これはもちろん耐え難いことです。解決策は、「前方宣言」を使用することです。PixelHandler インターフェイスでは、その参照のみが必要なためです (つまり、私 (インターフェイス) は、渡されたクラスの内部構造を知る必要はなく、32 (64) のメモリ アドレスのみを渡す必要があります)。

したがって、私たちは

  1. #include "PixelProcessor.h"  

次と置き換えます:

  1. class FCSinglePixelProcessBase; // 外部クラスの前方宣言 

次に、PixelProcessor.h を .cpp ファイルに含めます。この方法では、PixelProcessor.h への変更は .cpp ファイルの再コンパイルのみにつながるため、コンパイル時間が大幅に節約されます。

要約:

1) 可能であれば、プログラミング時に「コードのコピー」について考えないでください。結局のところ、OO は抽象化とコードの再利用のために生まれました。

2) 必要でない限り、クラス メンバー変数と関数パラメーターは、可能な限りポインターまたは参照に置き換える必要があります。これにより、.h に含まれる他の .h ファイルの数が減り、それらを前方宣言に置き換えることができるため、コンパイル時間が短縮され、将来的に相互インクルードされる可能性も減ります。

3) 最後に、効率の問題についてお話ししましょう。ピクセルごとに仮想関数を呼び出すとパフォーマンスに影響が出ると言う人もいるかもしれません。これは事実ですが、実際の損失は想像よりもはるかに小さいです。実際にテストしてみましたが、1024*768 の画像にネガティブ処理を実行すると、速度の低下はわずか 5% 程度です。複雑な処理 (明るさ/コントラスト/ガンマ) を実行する場合、この低下は完全に無視できます。結局のところ、余分なコードはスタック アクセスとテーブル検索だけであり、浮動小数点除算などの時間のかかる命令ではありません。

<<:  SQL SERVER データマイニング: クラスタリングアルゴリズムとシーケンシャルクラスタリングアルゴリズムの理解

>>:  STLコンポーネントアルゴリズム

ブログ    
ブログ    

推薦する

スマートオフィス管理におけるAIの役割

スマート オフィスの概念は新しいものではありませんが、企業のオーナーや管理者が自動化の生産性の価値を...

人工知能開発の重要な要素と気候変動への影響

人工知能は世界的な流行語となり、ほぼすべての企業のデジタル変革計画に不可欠な要素となっています。 A...

AIが高速道路に参入: テクノロジーを活用してファーストフード業界を完全に変える

迅速なサービスと便利な体験で知られるファーストフード業界は、顧客体験を向上させ、業務効率を改善するた...

AIとIoTが交通管理に及ぼす6つの影響

物流と輸送は世界貿易とサプライチェーン管理にとって極めて重要であり、テクノロジーの急速な発展により、...

マスク氏のロボットショーは何百万人ものネットユーザーを魅了した!

テスラロボットが家事を始める。マスク氏は最新の動画で、テスラのロボット「オプティマス・プライム」が服...

マスクの後ろに隠れて沈黙しないでください。AIはあなたが誰であるかを知っています

この記事は公開アカウント「Reading Core Technique」(ID: AI_Discov...

人工知能の時代において、テクノロジーは中立ではない

インターネットや人工知能に代表される情報技術の台頭により、社会は第三次科学技術革命の時代を迎えていま...

研究のアイデアがない場合は、信頼できる機械学習のための革新的なアイデア1,000個をご紹介します。

I. はじめに1. まず話をしましょう約4〜5年前、私はカーネギーメロン大学(CMU)の博士課程の...

周洪義:汎用人工知能は詐欺であり、垂直分野と組み合わせる必要がある

3月23日、360テクノロジー株式会社と華泰聯合証券はIPO上場指導契約を締結した。これは360がI...

...

インテリジェントオートメーションが現代の職場に与える影響

インテリジェントオートメーションは現代の職場をさまざまな形で変えていますCOVID-19パンデミック...

...

ビジネスアナリストにとってAIが意味するもの

[[275322]]今日では、人工知能はもはや流行語ではなく、多くの環境ビジネスアナリストやその他の...

AIエンジニアリングについて知っておくべきこと

人工知能は、21 世紀の世界のテクノロジー主導型市場において最も注目されている破壊的テクノロジーです...