日常生活におけるITの非標準アプリケーション:解析、知覚ハッシュ、画像比較=コスト最適化

この記事では、1年前に私にもたらされた1つの興味深い問題に対する異常な解決策に関する興味深い話を共有したいと思います。 記事に記載されているすべてのものは、まず第一に、「ただの楽しみのために」、純粋な学問的関心から作られました...

それは一年前でした。ただ自由時間と何か役に立つことをしたいという願望がありました。 明らかに、特定の知的飢えと、何か新しい興味深い仕事の深刻な不足がありました...したがって、まったく必要のない場所に自転車を貼り付けようとする試み...



1.挑戦



ある取引および購入企業では、調達の最適化の問題はかなり深刻でした。 同社は数十社の主要サプライヤーを抱えていましたが、同時に、多くのサプライヤーが20〜30%を超える商品を抱えており、価格は全員で異なっていました。 残念ながら、ほとんどの商品は「古いメモリから」購入されました。たとえば、グループAの商品はサプライヤーXから供給され、グループBの商品はサプライヤーYから供給されるという事実に慣れています。 明確にするために、例を示します。



 サプライヤーX:
製品A1-11ルーブル。
製品A2-10ルーブル。
製品B1-10ルーブル。
製品B2-11ルーブル。

サプライヤーY:
製品A1-10ルーブル。
製品A2-11ルーブル。
製品B1-11ルーブル。
製品B2-10.5ルーブル。


この表から、A1とB2は「Y」から購入し、A2とB2は「X」から購入する方が収益性が高いことが明らかです。 しかし、命名法の4つのアイテムと2つのサプライヤの例では簡単で簡単です。アイテムが合計で数万のアイテムで、サプライヤが数十の場合はどうでしょうか。

問題は基本的なタスクのように思えますが、多くの簡単な計算が必要であり、ボリュームは手で持ち上げられないため、PCの肩にすばやくシフトしてメリットを享受する必要がありますか? すべての命名法と価格を持つ単一のベースがあれば、すべてがそうでしょう。 しかし、悲しいかな、これはユートピアです...基地はごみ捨て場の通常のひどい状態にあり、毎日この山のゴミを捨てた人だけがどうにかそれを理解することができました。 価格はさらに悪く、通常はさまざまなカタログ、価格、サイトに分散しています。 なんらかの方法でカタログ化してまとめようとすると、手動による介入なしでは非常に難しいことが明らかになります。 1つのサプライヤーはメーカーの記事を使用し、もう1つのサプライヤーは自分の記事を使用し、3番目のサプライヤーはそれらを価格表にまったく表示しません。 名前は実際にはどこにも一致しません。たとえば、同じ製品を「三角形の絵のある花瓶」、「花瓶A-563」、「絵のある花瓶」、最後に「花瓶」と呼ぶことができます。

そのため、名前で言うと、接続もまったく役に立ちませんでした。



2.クレイジーなアイデア



そして、クレイジーなアイデアが浮上しました。 製品の詳細は、ほぼすべての場所に写真があり、さらに、サプライヤーが非常に頻繁に1か所で、またはお互いから写真を撮るようなものでした。 「BUT」が1つありました。写真のサイズは自然に異なり、比率も異なる場合がありました。 したがって、「額」の比較は役に立ちません。 しかし、結局のところ、Yandexは何らかの形で類似の画像を見つけますか? 試してみませんか?!? 解決しました!



文学を勉強し、グーグル、記事を読んで、類似の画像の検索がどのように機能するかについてのアイデアを与えました。 つまり、イメージのハッシュが構築され、次にハッシュが比較および比較されます。 主な違いはハッシュ関数にあります。 実装と計算の非常に複雑さの両方において、最も単純で最も速いのは、「平均値による」知覚的ハッシュです。

簡単に言えば、操作の原理:画像を16x16などの正方形に絞り込んでから、カラー画像からグレースケールの画像を取得し、正方形のポイントで平均色値を計算し、2回目のパスで0または1を入れますこのポイントの色は平均よりも小さいか、または大きいです。 結果の0と1のシーケンスは、探しているハッシュです。



プロトタイプを迅速に作成するために、画像処理にImageMagickを使用することが決定され、phpがスクリプト言語として選択されました。

テストの材料として、Windowsの標準セットから「Chrysanthemum.jpg」という画像を取得しました。 最初にtest1.jpgを呼び出したファイルは、ソースファイルを取得し、プロポーションを歪ませて、test2.jpgという名前で保存しました。 3番目のサンプルとして、「Desert.jpg」を取得し、test3.jpgという名前を付けました。 ここにあります:



テスト用の画像










次に、前処理のためにImageMagickを呼び出します。

E:\ImageMagick\convert.exe E:\Article\img\test1.jpg -resize 16x16! -colorspace gray E:\Article\img\_test1.jpg E:\ImageMagick\convert.exe E:\Article\img\test2.jpg -resize 16x16! -colorspace gray E:\Article\img\_test2.jpg E:\ImageMagick\convert.exe E:\Article\img\test3.jpg -resize 16x16! -colorspace gray E:\Article\img\_test3.jpg
      
      







取得するもの:

画像16x16グレースケール










次に、ハッシュ自体を検討します。

 <? function getPerceptHash($imgName) { $im = imagecreatefromjpeg($imgName); //    ,     256 (16*16),     $summ = 0; for($i=0;$i<8;$i++){ for($j=0;$j<8;$j++){ $summ += imagecolorat($im, $i, $j); } } $sred = $summ/64; //               0  1 $str=''; for($i=0;$i<8;$i++){ for($j=0;$j<8;$j++){ if(imagecolorat($im, $i, $j)>=$sred){ $str .= '1'; }else{ $str .= '0'; } } } //  16- ,   $str = base_convert($str, 2, 16); return $str; } echo '_test1.jpg '.getPerceptHash("E:\Denwer3\home\imagecomparer.loc\www\Article\img\_test1.jpg").'<br>'; echo '_test2.jpg '.getPerceptHash("E:\Denwer3\home\imagecomparer.loc\www\Article\img\_test2.jpg").'<br>'; echo '_test3.jpg '.getPerceptHash("E:\Denwer3\home\imagecomparer.loc\www\Article\img\_test3.jpg").'<br>'; ?>
      
      







結果が得られます。

 _test1.jpg f9f6f0f0e0b0f000
 _test2.jpg fbf6f0f0c0b0f000
 _test3.jpg 1e1e3e3e3e3e3c00




最初のハッシュと2番目のハッシュは非常に近く、3番目のハッシュも近くにないことがわかります。 他の一連の実験も実施されましたが、この方法は有効です。



3.試行された練習



比較方法が見つかり、テストされ、テストケースで機能しました。 それは戦いでそれをチェックアウトする時間でした。 カタログのさまざまなパーサー、価格、サプライヤーのサイト、および解析自体を書くのに数日かかりました。 さらなる実験のために、すべての情報とハッシュがデータベースに追加され、イメージ自体がディスクに追加されました。 合計で、約300万件のレコードがデータベースに収集されました。

当初、この方法は機能していましたが、ベースが拡大するにつれて、結果はますます悪化し、単純にゴミ箱に捨てられました。 多数の誤検知が発生しました。 この方法は、単にサイズを変更した画像ではうまく機能しましたが、画像が色補正、トリミング、またはさらに悪い場合、透かしが適用された場合(非常に一般的です)、結果は良くありませんでした。



4. pHash



使用されたハッシュ関数が私たちに合わないことは明らかでした。もっと深刻なものが必要です。 離散コサイン変換に基づいたpHashを使用することが決定されました



アルゴリズム自体は重く、PHPで実装する試みは何も良い結果に終わりませんでした。 解決策は、入力でファイルへのパスを受け取り、ハッシュを読み取り、それを返すコンソールユーティリティをSIで作成することでした。 ライブラリが見つかりましたhttp://www.phash.org/



次に、このライブラリを使用したテストサンプルをすばやく作成します。

 #include "stdafx.h" #include <iostream> #include <windows.h> using namespace std; #ifndef _WIN32 typedef unsigned _uint64 ulong64; typedef signed _int64 long64; #else typedef unsigned long long ulong64; typedef signed long long long64; typedef unsigned char uint8_t; typedef unsigned int uint32_t; #endif typedef int (*ph_dct_imagehash)(const char* file,ulong64 &hash); int _tmain(int argc, TCHAR* argv[]) { if (argc > 1) { char* filename = (char *)malloc( 255 ); wcstombs( filename, argv[1], 255 ); ph_dct_imagehash _ph_dct_imagehash; HINSTANCE hInstLibrary = LoadLibrary(L"pHash.dll"); if (hInstLibrary) { _ph_dct_imagehash = (ph_dct_imagehash)GetProcAddress(hInstLibrary, "ph_dct_imagehash"); int err = 0; err = GetLastError(); ulong64 myhash=0; _ph_dct_imagehash(filename, myhash); std::cout << myhash << std::endl; FreeLibrary(hInstLibrary); } else { return 0; } }else{ return 0; } return 0; }
      
      







コンパイルして、出力を取得します-pHashConcole.exe



PHPから、このコンストラクトは通常のexexを介して呼び出されます。

 $pHash=exec('E:\Article\pHashConsole.exe "'.$filename.'"');
      
      







ハッシュはMySQLテーブルにまとめられ、検索結果は非常に簡単なクエリで取得できます。

 'SELECT * FROM (SELECT * , BIT_COUNT( hash ^ '.$pHash.') AS distance FROM images ORDER BY distance LIMIT 0 , 100) s1 WHERE distance < '.$precision.';'
      
      





ここで、$ pHash-同様のものを探しているハッシュ。
 $精度-精度が高いほど、結果は多くなりますが、元の結果から遠くなります。 経験的に選択されます。




pHashを使用すると、スケーリングされた画像だけでなく、碑文が適用された画像、透かし、色補正が行われた部分がカットまたはカットされた画像を見つけることができました。



5.終わりです!



これらすべての操作の結果、共通のベースを収集するために手動で分析することなく問題を解決することができました。これにより、企業の調達コストを最適化することができました。 そのような一見クレイジーなアイデアは、実現可能であるだけでなく、実際に役立つことも判明しました。そのおかげで、具体的な結果が得られました。



ここに面白い話があります...



ご清聴ありがとうございました! 終わり!



All Articles