テッセラクト。 認識システムのエラーを認識する



Tesseractは、Googleが開発したフリーウェアのテキスト認識ソフトウェアです。 プロジェクトの説明によると、「Tesseractはおそらく利用可能な最も正確なオープンソースOCRエンジンです。」 そして、PVS-Studio静的アナライザーがこのプロジェクトのエラーを認識できるかどうか試してみましょう。



テッセラクト



Tesseractは、1980年代半ばから1990年代半ばにヒューレットパッカードによって開発されたフリーテキスト認識コンピュータープログラムで、10年間「棚に横たわって」います。 2006年8月、Googleはそれを購入し、Apache 2.0ライセンスの下でソースコードを開いて開発を続けました。 現時点では、プログラムはすでにUTF-8で動作しており、追加のモジュールを使用して言語サポート(ロシア語を含む)が実行されます。 [ウィキペディアからの説明 ]



プロジェクトのソースコードは、Google Codeウェブサイトで入手できます: https : //code.google.com/p/tesseract-ocr/



ソースコードの量は約16メガバイトです。



検証結果



PVS-Studioレポートを見たときに気付いたコードスニペットを次に示します。 たぶん私は何かを見逃した。 したがって、Tesseractの作成者が自分でチェックを行うことをお勧めします。 試用版は7日間有効です。これは、このような小規模なプロジェクトには十分です。 さて、彼らは定期的にツールを使用してタイプミスを見つけるかどうかを決定します。



いつものように、思い出させてください。 静的分析の本質は 、1回限りのチェックではなく、定期的な使用です。



悪い分裂



void LanguageModel::FillConsistencyInfo(....) { .... float gap_ratio = expected_gap / actual_gap; if (gap_ratio < 1/2 || gap_ratio > 2) { consistency_info->num_inconsistent_spaces++; .... }
      
      





PVS-Studio警告:V636「1/2」式は、暗黙的に「int」型から「float」型にキャストされました。 分数部分の損失を避けるために、明示的な型キャストの使用を検討してください。 例:double A =(double)(X)/ Y;。 language_model.cpp 1163



彼らは変数 'gap_ratio'を値0.5と比較したいと考えています。 残念ながら、0.5の記述に失敗した方法が選択されました。 除算1/2は整数で、0になります。



正しいコードは次のようになります。

 if (gap_ratio < 1.0f/2 || gap_ratio > 2) {
      
      





またはこのように:

 if (gap_ratio < 0.5f || gap_ratio > 2) {
      
      





疑わしい整数除算が実行される場所は他にもあります。 おそらくそれらの中には本当に不快な間違いがあります。



チェックアウトする価値のあるコードスニペット:

タイポの比較



 uintmax_t streamtoumax(FILE* s, int base) { int d, c = 0; .... c = fgetc(s); if (c == 'x' && c == 'X') c = fgetc(s); .... }
      
      





PVS-Studio警告:V547式 'c ==' x '&& c ==' X ''は常にfalseです。 おそらく '||' ここで演算子を使用する必要があります。 scanutils.cpp 135



正しいチェックオプション:

 if (c == 'x' || c == 'X') c = fgetc(s);
      
      





未定義の動作



1つの興味深いデザインが発見されました。 私はこれを見ていません:

 void TabVector::Evaluate(....) { .... int num_deleted_boxes = 0; .... ++num_deleted_boxes = true; .... }
      
      





警告PVS-Studio:V567未定義の動作。 'num_deleted_boxes'変数は、シーケンスポイント間で2回使用されている間に変更されます。 tabvector.cpp 735



著者がこのコードで何を言いたかったのかは明らかではありません。 ほとんどの場合、このコードはタイプミスの結果です。



式の結果を予測することは不可能です。 変数 'num_deleted_boxes'は、割り当ての前後に増やすことができます。 その理由は、変数がシーケンスの 1つのポイントで 2回変化するためです。



未定義の動作につながるその他のエラーは、シフト使用に関連しています 。 例を考えてみましょう:

 void Dawg::init(....) { .... letter_mask_ = ~(~0 << flag_start_bit_); .... }
      
      





警告V610未定義の動作。 シフト演算子 '<<を確認してください。 左のオペランド「〜0」は負です。 dawg.cpp 187



式「〜0」のタイプは「int」で、値「-1」と同じです。 負の値をシフトすると、未定義の動作が発生します。 プログラムが正しく機能することは運であり、それ以上のことはありません。 「0」を無署名にすることで、問題を修正できます。

 letter_mask_ = ~(~0u << flag_start_bit_);
      
      





ただし、それだけではありません。 アナライザーは、この行に別の警告を生成します。



V629「〜0 << flag_start_bit_」式の検査を検討してください。 32ビット値のビットシフトと、それに続く64ビットタイプへの拡張。 dawg.cpp 187



実際、変数「letter_mask_」のタイプは「uinT64」です。 私の理解では、ユニットを上位32ビットに書き込む必要があるかもしれません。 この場合、作成された式は正しくありません。 低ビットでのみ動作します。



「0」を64ビット型にする必要があります。 修正されたオプション:

 letter_mask_ = ~(~0ull << flag_start_bit_);
      
      





負の数がシフトされる他のコードフラグメントのリストをリストします。

疑わしい二重割り当て



 TESSLINE* ApproximateOutline(....) { EDGEPT *edgept; .... edgept = edgesteps_to_edgepts(c_outline, edgepts); fix2(edgepts, area); edgept = poly2 (edgepts, area); // 2nd approximation. .... }
      
      





PVS-Studio警告:V519「edgept」変数には連続して2回値が割り当てられます。 おそらくこれは間違いです。 行を確認してください:76、78。polyaprx.cpp 78



別の同様のケース:

 inT32 row_words2(....) { .... this_valid = blob_box.width () >= min_width; this_valid = TRUE; .... }
      
      





PVS-Studio警告:V519「this_valid」変数には値が連続して2回割り当てられます。 おそらくこれは間違いです。 チェック行:396、397。wordseg.cpp 397



無効なクラスメンバーの初期化シーケンス



まず、「MasterTrainer」クラスを検討します。 クラスメンバー 'samples_'は、メンバー 'fontinfo_table_'の前にあることに注意してください。

 class MasterTrainer { .... TrainingSampleSet samples_; .... FontInfoTable fontinfo_table_; .... };
      
      





標準に従って、コンストラクター内のクラスメンバーの初期化の順序は、クラス内の宣言の順序で発生します。 これは、「fontinfo_table_」の初期化の前に「samples_」が初期化されることを意味します。



次に、コンストラクターについて考えます。

 MasterTrainer::MasterTrainer(NormalizationMode norm_mode, bool shape_analysis, bool replicate_samples, int debug_level) : norm_mode_(norm_mode), samples_(fontinfo_table_), junk_samples_(fontinfo_table_), verify_samples_(fontinfo_table_), charsetsize_(0), enable_shape_anaylsis_(shape_analysis), enable_replication_(replicate_samples), fragments_(NULL), prev_unichar_id_(-1), debug_level_(debug_level) { }
      
      





問題は、初期化されていない変数「fontinfo_table_」が「samples_」の初期化にも使用されることです。



フィールド 'junk_samples_'および 'verify_samples_'を初期化するこのクラスの同様の状況。



私はこのクラスで何をするのが最善かを言うとは思わない。 'fontinfo_table_'の宣言をクラスの最初に移動するだけで十分かもしれません。



条件のタイプミス



タイプミスに気づくのは簡単ではありませんが、アナライザーは疲労を知りません。

 class ScriptDetector { .... int korean_id_; int japanese_id_; int katakana_id_; int hiragana_id_; int han_id_; int hangul_id_; int latin_id_; int fraktur_id_; .... }; void ScriptDetector::detect_blob(BLOB_CHOICE_LIST* scores) { .... if (prev_id == katakana_id_) osr_->scripts_na[i][japanese_id_] += 1.0; if (prev_id == hiragana_id_) osr_->scripts_na[i][japanese_id_] += 1.0; if (prev_id == hangul_id_) osr_->scripts_na[i][korean_id_] += 1.0; if (prev_id == han_id_) osr_->scripts_na[i][korean_id_] += kHanRatioInKorean; if (prev_id == han_id_) <<<<==== osr_->scripts_na[i][japanese_id_] += kHanRatioInJapanese; .... }
      
      





PVS-Studio警告:V581互いに並んでいる「if」演算子の条件式は同一です。 行を確認してください:551、553。osdetect.cpp 553



ほとんどの場合、最新の比較は次のようになります。

 if (prev_id == japanese_id_)
      
      





不要なチェック



「新しい」演算子が返すものを確認する必要はありません。 メモリを割り当てることができない場合、例外がスローされます。 もちろん、nullポインターを返す特別な演算子 'new'を作成できますが、これは別のケース( 詳細 )です。



その結果、この関数は単純化できます:

 void SetLabel(char_32 label) { if (label32_ != NULL) { delete []label32_; } label32_ = new char_32[2]; if (label32_ != NULL) { label32_[0] = label; label32_[1] = 0; } }
      
      





PVS-Studio警告:V668メモリは「new」演算子を使用して割り当てられたため、「label32_」ポインターをnullに対してテストする意味はありません。 メモリ割り当てエラーの場合、例外が生成されます。 char_samp.h 73



「新しい」演算子が返したポインターがチェックされる101の場所もあります。 記事にそれらをリストするために、私は理由を見ません。 このためにPVS-Studioを実行する方が簡単です。



おわりに



静的分析を定期的に使用すると、愚かなエラーやタイプミスを見つけるよりも、より便利なタスクを解決するために多くの時間を節約できます。



そして、Twitterでフォローしてください: @Code_Analysis 。 興味深いC ++関連記事へのリンクを定期的に公開しています。



記事を読んで質問がありますか?
多くの場合、記事には同じ質問が寄せられます。 ここでそれらに対する回答を収集しました: PVS-StudioおよびCppCatバージョン2014に関する記事の読者からの質問への回答 。 リストをご覧ください。




All Articles