従来の機械学習アルゴリズムの分析 (opencv 実装)

序文

このテキストは主に、従来の機械学習アルゴリズムの一部にすぎない、従来の機械学習におけるいくつかの小さなアルゴリズムとアイデアを分析します。より伝統的な機械学習アルゴリズムについては、私の他のブログを参照してください。リンク 1: PCA 主成分分析
リンク2
: Canny エッジ検出アルゴリズム
リンク 3: K-Means クラスタリング アルゴリズム
リンク 4: SIFT アルゴリズム分析

1.opencv

  • OpenCV は、http://opencv.org から入手できるオープン ソースのコンピューター ビジョン ライブラリです。
  • OpenCV ライブラリは C および C++ で書かれており、Windows、Linux、Mac OS X、およびその他のシステム上で実行できます。同時に、Python、Java、Matlab、その他の言語のインターフェイスの開発や、Android や iOS へのライブラリのインポートによるモバイル デバイス向けアプリケーションの開発にも積極的に取り組んでいます。
  • OpenCV は、リアルタイム アプリケーションの開発に重点を置き、効率的なコンピューティングを実現するように設計されています。これは C++ で書かれており、マルチスレッドを活用するために徹底的に最適化されています。
  • OpenCV の目標の 1 つは、高度なビジョン アプリケーションを迅速に作成できるようにする、使いやすいコンピューター ビジョン インターフェイスを提供することです。
  • OpenCV ライブラリには、工業製品の品質検査、医療画像処理、セキュリティ分野、対話型操作、カメラ キャリブレーション、両眼視機能、ロボット工学など、コンピュータ ビジョンのさまざまな分野から派生した 500 以上の関数が含まれています。

opencv の基礎となる実装は C++ で記述されているため、C++ の最大の利点はその効率の高さにあり、同じタイプの関数が異なるライブラリでは異なる速度で実行されます。

opencv ピットの BGR
読み込み画像の opencv のチャンネル配置は主流の RGB ではなく BGR です!覚えて!


#opencv で読み込む行列は BGR なので、RGB に変換したい場合はimg4 = cv2.imread('1.jpg')
img4 = cv2.cvtColor(img4,cv2.COLOR_BGR2RGB)のように変換できます。

注意点

  1. opencv によって読み取られたカラー画像が BGR 順序で保存されることを除いて、カラー画像で読み取られた他のすべての画像ライブラリは RGB で保存されます。
  2. PIL によって読み取られる画像が img クラスであることを除いて、他のライブラリによって読み取られる画像はすべて numpy 行列内にあります。
  3. 主要な画像ライブラリの中で最高の OpenCv は、速度にしても画像操作の包括性にしても、圧倒的な存在であり、結局のところ、巨大な cv に特化したライブラリです。

以下は一連の実験データ、3120*4160 の画像、100 回の実行にかかる時間です。
ここに画像の説明を挿入

2. 線形回帰

線形回帰とは何ですか?
例えば、商品の利益が2元、5元、10元の場合、それぞれ4元、10元、20元となり、商品の利益と販売価格の関係は一致すると容易に結論付けることができます。直線: y= 2x. 上記の単純な線形回帰方程式では、「2」を回帰係数と呼びます、つまり、傾きが回帰係数です。回帰係数は、利益 (y) と、それに対応する商品の販売価格の 1 単位の変化 (x) との関係を示します。

ここに画像の説明を挿入

線形回帰は、全体的にどの直線がこれらの離散点に「最も近い」かを示します。平均の概念と似ています。

最小二乗法

  • 重要なポイントは、誤差の二乗和を最小化する
  • 一連のデータ点 (xi,yi) (i=1,...,m) があると仮定すると、フィッティング関数 h(x) によって得られる推定量は h(xi) になります。
  • 残差:ri = h(xi) – yi
  • 私たちが追求したい目標は、残差の二乗和を最小化することです。

これから、最小二乗法の定義を書くことができます。
ここに画像の説明を挿入

これは制約のない最適化問題であり、k と b の偏導関数がそれぞれ計算され、偏導関数が 0 に設定されて極値が取得されます。
ここに画像の説明を挿入

3. ランサック

  • ランダムサンプリングの一貫性
  • 知らせ、RANSACはアイデアです、既知のモデルのパラメーターを解決するためのフレームワーク。特定の問題に限定されるわけではありません。コンピュータ ビジョンの問題、統計数学の問題、さらには経済学の分野におけるモデル パラメータ推定の問題さえも可能です。
  • これは、外れ値を含む一連の観測データ内の数学的モデルのパラメーターを推定するための反復手法です。RANSAC は、特定の確率で妥当な結果を生成するという意味で非決定論的アルゴリズムであり、反復を増やすことで確率を高めることができます。
  • RANSAC の基本的な前提は、「内部グループ」データはモデル パラメーターのいくつかのセットを通じてそのデータ分布を説明できるのに対し、「外れ値」データはモデリングに適さないデータであるということです。データは、極度のノイズや測定値の誤った解釈、データに関する誤った仮定などによる外れ値を指すノイズの影響を受ける可能性があります。RANSAC は、(通常は小規模な) 内群セットが与えられた場合、データ モデルを最もよく説明する、またはデータ モデルに最もよく適合するパラメータを推定できる手順が存在すると仮定します。

RANSAC は単なるアイデアであることに注意してください。これは、既知のモデル パラメーターを見つけるためのフレームワークです。既知のモデルに制限はありません。任意のモデルにすることができますが、モデルには y = ax + b などのパラメーターがあります。a と b がどの程度等しいかは、RANSAC が解決する必要がある問題、つまり、y = ax + b、y = kx + c、または z = ax + by + c であるかどうかです。RANSAC は気にしません。そのメソッドに従っている限り、パラメーターを見つけることができます。例えば、象を冷蔵庫に入れるには3つのステップが必要ですが、このときの冷蔵庫はブラックボックスに相当するため、入れられるかどうかは関係なく、この3つのステップだけを気にします。したがって、象を冷蔵庫に入れようと飛行機を冷蔵庫に入れようと、RANSAC は気にせず、RANSAC が気にするのはこれら 3 つのステップのみであり、物体が異なるからといってこれら 3 つのステップが異なることはありません。

RANSAC と最小二乗法

  • 実際の運用におけるデータには、特定の偏差が存在することがよくあります。
  • たとえば、2 つの変数 X と Y の間に線形関係 (Y=aX+b) があることがわかっており、パラメーター a と b の特定の値を決定したいとします。実験を通じて、X と Y の一連のテスト値を取得できます。理論的には、2 つの未知数の方程式を確認するには 2 セットの値のみが必要ですが、系統誤差により、ランダムに 2 点を取得して計算された a と b の値は同じではありません。私たちが望んでいるのは、最終的に計算された理論モデルがテスト値との誤差を最小にすることです。
  • 最小二乗法: パラメータ a と b の偏導関数が 0 である場合の最小平均二乗誤差の値を計算します。実際、多くの場合、最小二乗法は線形回帰と同義です。
  • 残念ながら、最小二乗法は小さな誤差にのみ適しています。
  • モデルが決定され、最大反復回数が許容される場合、RANSAC は常に最適な解を見つけることができます。(80% の誤差を含むデータセットの場合、RANSAC は直接最小二乗法をはるかに上回ります。)
  • 画像の画素数が多いため、最小二乗法は計算量が多く、計算速度が遅くなります。

ここに画像の説明を挿入
上の図を見ると、フィッティングに最小二乗法を使用すると赤い線が得られますが、これは明らかに期待された効果から大きく外れています。これは、最小二乗法はノイズ ポイントの影響を受けにくいため、最小二乗法は小さな誤差にのみ適しており、RANSAC 法を使用して RANSAC ステップ RANSAC アルゴリズム入力に対する妥当な解を得ることができ
ます

  1. 一連の観測データ (多くの場合、大きなノイズや無効な点が含まれます)
  2. 観測データを説明するパラメトリック モデル、たとえば y=ax+b (つまり、モデルは既知です)
  3. いくつかの信頼できる議論
  1. データ内のいくつかの点をランダムに選択し、それらを内グループとして設定します
  2. 内側のグループに適したモデルを計算します。例: y=ax+b ->y=2x+3 y=4x+5
  3. 今選択されていない他の点を構築したばかりのモデルに取り込み、それが内部群であるかどうかを計算します。例: hi=2xi+3->ri
  4. 内集団の数を書き留めます
  5. 上記の手順を繰り返します
  6. どの計算に最も多くの内群が含まれるかを比較し、最も多くの内群を含む時間内に構築されたモデルが必要なソリューションになります。

注: 異なる問題に対応する数学的モデルは異なるため、モデル パラメーターを計算するときの方法も異なる必要があります。RANSAC の役割はモデル パラメーターを計算することではありません。(これは、数学的モデルを知る必要があるという点で ransac の欠点につながります)
ここにはいくつかの問題があります。

  1. 最初にランダムに選択するポイントの数 (n)
  2. そして何回繰り返すか(k)

RANSACのパラメータ決定

  • 各点が真の内群である確率が w であると仮定します。
    w = 内群の数/(内群の数 + 外群の数)
  • 通常、w が何であるかはわかりません。w^n は選択された n 点がすべて内群である確率、1-w^n は選択された n 点の少なくとも 1 つが内群ではない確率です、(1 − w n) k は、アルゴリズムを k 回実行した後の成功確率が p であると仮定して、k 回繰り返した後にすべての n 点がグループ内に含まれるわけではない確率です。 1 − p = (1 − w n)
    k p =
    1 − (1 − w n) k
  • 抽出回数 K は P 逆算 K=log(1-P)/log(1-w^n) で求めることができます。
  • したがって、高い確率で成功したい場合は、次のようにします。
  • n が一定の場合、k が大きいほど p も大きくなり、w が一定の場合、n が大きいほど、より大きな k が必要になります。
  • 通常、w は不明であるため、n には小さい値を選択することをお勧めします。

RANSAC の
長所と短所 利点:

  1. モデルパラメータをロバストに推定できます。たとえば、多数の外れ値を含むデータセットからパラメータを高精度に推定できます。

欠点:

  1. パラメーターを計算する反復回数に上限はありません。反復回数の上限が設定されている場合、得られる結果が最適な結果ではない可能性があり、さらには誤った結果が得られる可能性があります。
  2. RANSAC では信頼できるモデルを取得できる確率は一定であり、その確率は反復回数に比例します。
  3. 問題に関連するしきい値を設定する必要があります。
  4. RANSAC は特定のデータ セットから 1 つのモデルのみを推定できます。2 つ (またはそれ以上) のモデルがある場合、RANSAC は別のモデルを見つけることができません。
  5. 数学的モデルを知る必要がある

コード

import numpy as np
import scipy as sp
import scipy.linalg as sl
 
def ransac(data, model, n, k, t, d, debug = False, return_all = False):
    """
    输入:
        data - 样本点
        model - 假设模型:事先自己确定
        n - 生成模型所需的最少样本点
        k - 最大迭代次数
        t - 阈值:作为判断点满足模型的条件
        d - 拟合较好时,需要的样本点最少的个数,当做阈值看待
    输出:
        bestfit - 最优拟合解(返回nil,如果未找到)
    
    iterations = 0
    bestfit = nil #后面更新
    besterr = something really large #后期更新besterr = thiserr
    while iterations < k 
    {
        maybeinliers = 从样本中随机选取n个,不一定全是局内点,甚至全部为局外点
        maybemodel = n个maybeinliers 拟合出来的可能符合要求的模型
        alsoinliers = emptyset #满足误差要求的样本点,开始置空
        for (每一个不是maybeinliers的样本点)
        {
            if 满足maybemodel即error < t
                将点加入alsoinliers 
        }
        if (alsoinliers样本点数目 > d) 
        {
            %有了较好的模型,测试模型符合度
            bettermodel = 利用所有的maybeinliers 和 alsoinliers 重新生成更好的模型
            thiserr = 所有的maybeinliers 和 alsoinliers 样本点的误差度量
            if thiserr < besterr
            {
                bestfit = bettermodel
                besterr = thiserr
            }
        }
        iterations++
    }
    return bestfit
    """
    iterations = 0
    bestfit = None
    besterr = np.inf #设置默认值
    best_inlier_idxs = None
    while iterations < k:
        maybe_idxs, test_idxs = random_partition(n, data.shape[0])
        print ('test_idxs = ', test_idxs)
        maybe_inliers = data[maybe_idxs, :] #获取size(maybe_idxs)行数据(Xi,Yi)
        test_points = data[test_idxs] #若干行(Xi,Yi)数据点
        maybemodel = model.fit(maybe_inliers) #拟合模型
        test_err = model.get_error(test_points, maybemodel) #计算误差:平方和最小
        print('test_err = ', test_err <t)
        also_idxs = test_idxs[test_err < t]
        print ('also_idxs = ', also_idxs)
        also_inliers = data[also_idxs,:]
        if debug:
            print ('test_err.min()',test_err.min())
            print ('test_err.max()',test_err.max())
            print ('numpy.mean(test_err)',numpy.mean(test_err))
            print ('iteration %d:len(alsoinliers) = %d' %(iterations, len(also_inliers)) )
        # if len(also_inliers > d):
        print('d = ', d)
        if (len(also_inliers) > d):
            betterdata = np.concatenate( (maybe_inliers, also_inliers) ) #样本连接
            bettermodel = model.fit(betterdata)
            better_errs = model.get_error(betterdata, bettermodel)
            thiserr = np.mean(better_errs) #平均误差作为新的误差
            if thiserr < besterr:
                bestfit = bettermodel
                besterr = thiserr
                best_inlier_idxs = np.concatenate( (maybe_idxs, also_idxs) ) #更新局内点,将新点加入
        iterations += 1
    if bestfit is None:
        raise ValueError("did't meet fit acceptance criteria")
    if return_all:
        return bestfit,{
    
    'inliers':best_inlier_idxs}
    else:
        return bestfit
 
 
def random_partition(n, n_data):
    """return n random rows of data and the other len(data) - n rows"""
    all_idxs = np.arange(n_data) #获取n_data下标索引
    np.random.shuffle(all_idxs) #打乱下标索引
    idxs1 = all_idxs[:n]
    idxs2 = all_idxs[n:]
    return idxs1, idxs2
 
class LinearLeastSquareModel:
    #最小二乘求线性解,用于RANSAC的输入模型    
    def __init__(self, input_columns, output_columns, debug = False):
        self.input_columns = input_columns
        self.output_columns = output_columns
        self.debug = debug
    
    def fit(self, data):
		#np.vstack按垂直方向(行顺序)堆叠数组构成一个新的数组
        A = np.vstack( [data[:,i] for i in self.input_columns] ).T #第一列Xi-->行Xi
        B = np.vstack( [data[:,i] for i in self.output_columns] ).T #第二列Yi-->行Yi
        x, resids, rank, s = sl.lstsq(A, B) #residues:残差和
        return x #返回最小平方和向量   
 
    def get_error(self, data, model):
        A = np.vstack( [data[:,i] for i in self.input_columns] ).T #第一列Xi-->行Xi
        B = np.vstack( [data[:,i] for i in self.output_columns] ).T #第二列Yi-->行Yi
        B_fit = sp.dot(A, model) #计算的y值,B_fit = model.k*A + model.b
        err_per_point = np.sum( (B - B_fit) ** 2, axis = 1 ) #sum squared error per row
        return err_per_point
 
def test():
    #生成理想数据
    n_samples = 500 #样本个数
    n_inputs = 1 #输入变量个数
    n_outputs = 1 #输出变量个数
    A_exact = 20 * np.random.random((n_samples, n_inputs))#随机生成0-20之间的500个数据:行向量
    perfect_fit = 60 * np.random.normal( size = (n_inputs, n_outputs) ) #随机线性度,即随机生成一个斜率
    B_exact = sp.dot(A_exact, perfect_fit) # y = x * k
 
    #加入高斯噪声,最小二乘能很好的处理
    A_noisy = A_exact + np.random.normal( size = A_exact.shape ) #500 * 1行向量,代表Xi
    B_noisy = B_exact + np.random.normal( size = B_exact.shape ) #500 * 1行向量,代表Yi
 
    if 1:
        #添加"局外点"
        n_outliers = 100
        all_idxs = np.arange( A_noisy.shape[0] ) #获取索引0-499
        np.random.shuffle(all_idxs) #将all_idxs打乱
        outlier_idxs = all_idxs[:n_outliers] #100个0-500的随机局外点
        A_noisy[outlier_idxs] = 20 * np.random.random( (n_outliers, n_inputs) ) #加入噪声和局外点的Xi
        B_noisy[outlier_idxs] = 50 * np.random.normal( size = (n_outliers, n_outputs)) #加入噪声和局外点的Yi
    #setup model 
    all_data = np.hstack( (A_noisy, B_noisy) ) #形式([Xi,Yi]....) shape:(500,2)500行2列
    input_columns = range(n_inputs)  #数组的第一列x:0
    output_columns = [n_inputs + i for i in range(n_outputs)] #数组最后一列y:1
    debug = False
    model = LinearLeastSquareModel(input_columns, output_columns, debug = debug) #类的实例化:用最小二乘生成已知模型
 
    linear_fit,resids,rank,s = sp.linalg.lstsq(all_data[:,input_columns], all_data[:,output_columns])
    
    #run RANSAC 算法
    ransac_fit, ransac_data = ransac(all_data, model, 50, 1000, 7e3, 300, debug = debug, return_all = True)
 
    if 1:
        import pylab
 
        sort_idxs = np.argsort(A_exact[:,0])
        A_col0_sorted = A_exact[sort_idxs] #秩为2的数组
 
        if 1:
            pylab.plot( A_noisy[:,0], B_noisy[:,0], 'k.', label = 'data' ) #散点图
            pylab.plot( A_noisy[ransac_data['inliers'], 0], B_noisy[ransac_data['inliers'], 0], 'bx', label = "RANSAC data" )
        else:
            pylab.plot( A_noisy[non_outlier_idxs,0], B_noisy[non_outlier_idxs,0], 'k.', label='noisy data' )
            pylab.plot( A_noisy[outlier_idxs,0], B_noisy[outlier_idxs,0], 'r.', label='outlier data' )
 
        pylab.plot( A_col0_sorted[:,0],
                    np.dot(A_col0_sorted,ransac_fit)[:,0],
                    label='RANSAC fit' )
        pylab.plot( A_col0_sorted[:,0],
                    np.dot(A_col0_sorted,perfect_fit)[:,0],
                    label='exact system' )
        pylab.plot( A_col0_sorted[:,0],
                    np.dot(A_col0_sorted,linear_fit)[:,0],
                    label='linear fit' )
        pylab.legend()
        pylab.show()
 
if __name__ == "__main__":
    test()

結果:
ここに画像の説明を挿入

4. 画像類似度比較ハッシュアルゴリズム

類似画像検索には 3 つのハッシュ アルゴリズムがあります。

  1. 平均ハッシュアルゴリズム
  2. 差分ハッシュアルゴリズム
  3. 知覚的ハッシュアルゴリズム

ハッシュとは何ですか?

  • ハッシュ関数(またはハッシュ アルゴリズム、ハッシュ関数、英語: Hash Function)は、あらゆる種類のデータから小さなデジタル「指紋」を作成する方法です。ハッシュ関数はメッセージまたはデータを要約に圧縮します。これにより、データ量が削減され、データの形式が固定されます。この関数は、データをスクランブルして、ハッシュ値 (ハッシュ値、ハッシュ コード、ハッシュ サム、またはハッシュ) と呼ばれるフィンガープリントを再作成します。ハッシュ値は通常、ランダムな文字と数字の短い文字列で表されます。
  • ハッシュ アルゴリズムによって得られた任意の長さのバイナリ値は、より短い固定長のバイナリ値、つまりハッシュ値にマッピングされます。また、ハッシュ値は、データを一意で非常にコンパクトに数値表現したもので、平文をハッシュしてハッシュ値を取得する場合、平文内の文字を変更しただけであっても、得られるハッシュ値は違うでしょう。
  • ハッシュ アルゴリズムは、ほぼすべてのデジタル ファイルを、一見文字化けした数字と文字の文字列に変換する関数です。

暗号化関数として、ハッシュ関数には 2 つの最も重要な特性があります。

  1. 不可逆性。入力情報から出力の一見文字化けした文字列(ハッシュ値)を取得するのは非常に簡単ですが、出力文字列から入力結果を推測するのは非常に困難です。
  2. 出力値の一意性と予測不可能性。入力情報がわずかに異なる限り、ハッシュ アルゴリズムに従って得られる出力値も大きく異なります。

ハミング距離
2 つの整数間のハミング距離は、2 つの数値の対応する 2 進ビットが異なる位置の数を指します。
ここに画像の説明を挿入

平均ハッシュアルゴリズムの
ステップ

  1. スケーリング: 画像は 8*8 にスケーリングされ、構造は維持され、細部は削除されます。
  2. グレースケール: グレースケール画像に変換します。
  3. 平均: グレースケール画像内のすべてのピクセルの平均を計算します。
  4. 比較: ピクセル値が平均値より大きい場合は 1 として記録され、そうでない場合は 0 として記録され、合計 64 ビットになります。
  5. ハッシュの生成: 上記の手順で生成された 1 と 0 を順番に組み合わせると、画像のフィンガープリント (ハッシュ) になります。
  6. フィンガープリントの比較: 2 つの画像のフィンガープリントを比較し、ハミング距離、つまり 2 つの 64 ビット ハッシュ値の桁数が異なることを計算します。異なる桁数が少ないほど、画像は類似しています。

差分ハッシュ アルゴリズム
平均ハッシュ アルゴリズムと比較すると、差分ハッシュ アルゴリズムは初期段階と後期段階で基本的に同じであり、中間のハッシュのみが変更されます。
ステップ

  1. スケーリング: 画像は 8*9 にスケーリングされ、構造は維持され、細部は削除されます。
  2. グレースケール: グレースケール画像に変換します。
  3. 平均: グレースケール画像内のすべてのピクセルの平均を計算します。—いいえ、このステップは平均ハッシュとの比較のみを目的としています。
  4. 比較: ピクセル値が次のピクセル値より大きい場合は 1 として記録され、そうでない場合は 0 として記録されます。このラインは次のラインと比較しません。各ラインには 9 ピクセル、8 つの差分があり、8 ラインあり、合計 64 ビットです。
  5. ハッシュの生成: 上記の手順で生成された 1 と 0 を順番に組み合わせると、画像のフィンガープリント (ハッシュ) になります。
  6. フィンガープリントの比較: 2 つの画像のフィンガープリントを比較し、ハミング距離、つまり 2 つの 64 ビット ハッシュ値の桁数が異なることを計算します。異なる桁数が少ないほど、画像は類似しています。

知覚的ハッシュ アルゴリズム
平均値ハッシュ アルゴリズムは厳密すぎて正確さが不十分であるため、サムネイルの検索に適しています。より正確な結果を得るには、DCT (離散コサイン変換) を使用してデータ量を削減する知覚的ハッシュ アルゴリズムを選択できます。周波数。
ステップ:

  1. 画像を縮小します。32 * 32 の方が適切なサイズであり、DCT 計算に便利です。
  2. グレースケールに変換: スケーリングされた画像をグレースケールに変換します。
  3. DCT を計算する: DCT は画像を一連の比率に分割します。
  4. Reduced DCT: DCT 計算後の行列は 32 * 32 で、左上隅の 8 * 8 は予約されており、画像の最低周波数を表します。
  5. 平均値の計算: DCT を低減した後のすべてのピクセルの平均値を計算します。
  6. DCT をさらに小さくします。DCT が平均値より大きい場合は 1 として記録され、それ以外の場合は 0 として記録されます。
  7. 情報フィンガープリントを取得します。64 個の情報ビットを組み合わせます。順序は一貫性を維持するためにランダムです。
  8. 最後に、2 つの画像のフィンガープリントを比較して、ハミング距離を取得します。

コードの実装
平均値ハッシュと差分値ハッシュの実装:

import cv2
import numpy as np
 
#均值哈希算法
def aHash(img):
    #缩放为8*8
    img=cv2.resize(img,(8,8),interpolation=cv2.INTER_CUBIC)
    #转换为灰度图
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #s为像素和初值为0,hash_str为hash值初值为''
    s=0
    hash_str=''
    #遍历累加求像素和
    for i in range(8):
        for j in range(8):
            s=s+gray[i,j]
    #求平均灰度
    avg=s/64
    #灰度大于平均值为1相反为0生成图片的hash值
    for i in range(8):
        for j in range(8):
            if  gray[i,j]>avg:
                hash_str=hash_str+'1'
            else:
                hash_str=hash_str+'0'            
    return hash_str
 
#差值算法
def dHash(img):
    #缩放8*9
    img=cv2.resize(img,(9,8),interpolation=cv2.INTER_CUBIC)
    #转换灰度图
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    hash_str=''
    #每行前一个像素大于后一个像素为1,相反为0,生成哈希
    for i in range(8):
        for j in range(8):
            if   gray[i,j]>gray[i,j+1]:
                hash_str=hash_str+'1'
            else:
                hash_str=hash_str+'0'
    return hash_str
 
#Hash值对比
def cmpHash(hash1,hash2):
    n=0
    #hash长度不同则返回-1代表传参出错
    if len(hash1)!=len(hash2):
        return -1
    #遍历判断
    for i in range(len(hash1)):
        #不相等则n计数+1,n最终为相似度
        if hash1[i]!=hash2[i]:
            n=n+1
    return n
 
img1=cv2.imread('lenna.png')
img2=cv2.imread('lenna_noise.png')
hash1= aHash(img1)
hash2= aHash(img2)
print(hash1)
print(hash2)
n=cmpHash(hash1,hash2)
print('均值哈希算法相似度:',n)
 
hash1= dHash(img1)
hash2= dHash(img2)
print(hash1)
print(hash2)
n=cmpHash(hash1,hash2)
print('差值哈希算法相似度:',n)

ここに画像の説明を挿入

3 つのアルゴリズムの比較:

  • aHash: 平均ハッシュ。より高速ですが、場合によっては精度が低くなります。
  • pHash: 知覚的ハッシュ。精度は高くなりますが、速度は劣ります
  • dHash: 差分ハッシュ。精度も高く、スピードも速いです。

素材生成

import cv2 as cv
import numpy as np
from PIL import Image
import os.path as path
from PIL import ImageEnhance


def rotate(image):
    def rotate_bound(image, angle):
        # grab the dimensions of the image and then determine the
        # center
        (h, w) = image.shape[:2]
        (cX, cY) = (w // 2, h // 2)

        # grab the rotation matrix (applying the negative of the
        # angle to rotate clockwise), then grab the sine and cosine
        # (i.e., the rotation components of the matrix)
        M = cv.getRotationMatrix2D((cX, cY), -angle, 1.0)
        cos = np.abs(M[0, 0])
        sin = np.abs(M[0, 1])

        # compute the new bounding dimensions of the image
        nW = int((h * sin) + (w * cos))
        nH = int((h * cos) + (w * sin))

        # adjust the rotation matrix to take into account translation
        M[0, 2] += (nW / 2) - cX
        M[1, 2] += (nH / 2) - cY

        # perform the actual rotation and return the image
        return cv.warpAffine(image, M, (nW, nH))

    return rotate_bound(image, 45)


def enhance_color(image):
    enh_col = ImageEnhance.Color(image)
    color = 1.5
    return enh_col.enhance(color)


def blur(image):
    # 模糊操作
    return cv.blur(image, (15, 1))


def sharp(image):
    # 锐化操作
    kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)
    return cv.filter2D(image, -1, kernel=kernel)


def contrast(image):
    def contrast_brightness_image(src1, a, g):
        """
        粗略的调节对比度和亮度
        :param src1: 图片
        :param a: 对比度
        :param g: 亮度
        :return:
        """

        # 获取shape的数值,height和width、通道
        h, w, ch = src1.shape

        # 新建全零图片数组src2,将height和width,类型设置为原图片的通道类型(色素全为零,输出为全黑图片)
        src2 = np.zeros([h, w, ch], src1.dtype)
        # addWeighted函数说明如下
        return cv.addWeighted(src1, a, src2, 1 - a, g)

    return contrast_brightness_image(image, 1.2, 1)


def resize(image):
    # 缩放图片
    return cv.resize(image, (0, 0), fx=1.25, fy=1)


def light(image):
    # 修改图片的亮度
    return np.uint8(np.clip((1.3 * image + 10), 0, 255))


def save_img(image, img_name, output_path=None):
    # 保存图片
    cv.imwrite(path.join(output_path, img_name), image, [int(cv.IMWRITE_JPEG_QUALITY), 70])
    pass


def show_img(image):
    cv.imshow('image', image)
    cv.waitKey(0)
    pass


def main():
    data_img_name = 'lenna.png'
    output_path = "./source"
    data_path = path.join(output_path, data_img_name)

    img = cv.imread(data_path)

    # 修改图片的亮度
    img_light = light(img)
    # 修改图片的大小
    img_resize = resize(img)
    # 修改图片的对比度
    img_contrast = contrast(img)
    # 锐化
    img_sharp = sharp(img)
    # 模糊
    img_blur = blur(img)
    # 色度增强
    img_color = enhance_color(Image.open(data_path))
    # 旋转
    img_rotate = rotate(img)
    img_rotate1 = Image.open(data_path).rotate(45)
    # 两张图片横向合并(便于对比显示)
    # tmp = np.hstack((img, img_rotate))

    save_img(img_light, "%s_light.jpg" % data_img_name.split(".")[0], output_path)
    save_img(img_resize, "%s_resize.jpg" % data_img_name.split(".")[0], output_path)
    save_img(img_contrast, "%s_contrast.jpg" % data_img_name.split(".")[0], output_path)
    save_img(img_sharp, "%s_sharp.jpg" % data_img_name.split(".")[0], output_path)
    save_img(img_blur, "%s_blur.jpg" % data_img_name.split(".")[0], output_path)
    # save_img(img_rotate, "%s_rotate.jpg" % data_img_name.split(".")[0], output_path)
    # 色度增强
    img_color.save(path.join(output_path, "%s_color.jpg" % data_img_name.split(".")[0]))
    img_rotate1.save(path.join(output_path, "%s_rotate.jpg" % data_img_name.split(".")[0]))

    show_img(img_rotate)
    pass


if __name__ == '__main__':
    main()

アルゴリズム比較コード

import cv2
import numpy as np
import time
import os.path as path


def aHash(img, width=8, high=8):
    """
    均值哈希算法
    :param img: 图像数据
    :param width: 图像缩放的宽度
    :param high: 图像缩放的高度
    :return:感知哈希序列
    """
    # 缩放为8*8
    img = cv2.resize(img, (width, high), interpolation=cv2.INTER_CUBIC)
    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # s为像素和初值为0,hash_str为hash值初值为''
    s = 0
    hash_str = ''
    # 遍历累加求像素和
    for i in range(8):
        for j in range(8):
            s = s + gray[i, j]

    # 求平均灰度
    avg = s / 64
    # 灰度大于平均值为1相反为0生成图片的hash值
    for i in range(8):
        for j in range(8):
            if gray[i, j] > avg:
                hash_str = hash_str + '1'
            else:
                hash_str = hash_str + '0'
    return hash_str


def dHash(img, width=9, high=8):
    """
    差值感知算法
    :param img:图像数据
    :param width:图像缩放后的宽度
    :param high: 图像缩放后的高度
    :return:感知哈希序列
    """
    # 缩放8*8
    img = cv2.resize(img, (width, high), interpolation=cv2.INTER_CUBIC)
    # 转换灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    hash_str = ''
    # 每行前一个像素大于后一个像素为1,反之置为0,生成感知哈希序列(string)
    for i in range(high):
        for j in range(high):
            if gray[i, j] > gray[i, j + 1]:
                hash_str = hash_str + '1'
            else:
                hash_str = hash_str + '0'
    return hash_str


def cmp_hash(hash1, hash2):
    """
    Hash值对比
    :param hash1: 感知哈希序列1
    :param hash2: 感知哈希序列2
    :return: 返回相似度
    """
    n = 0
    # hash长度不同则返回-1代表传参出错
    if len(hash1) != len(hash2):
        return -1
    # 遍历判断
    for i in range(len(hash1)):
        # 不相等则n计数+1,n最终为相似度
        if hash1[i] != hash2[i]:
            n = n + 1

    return 1 - n / len(hash2)


def pHash(img_file, width=64, high=64):
    """
    感知哈希算法
    :param img_file: 图像数据
    :param width: 图像缩放后的宽度
    :param high:图像缩放后的高度
    :return:图像感知哈希序列
    """
    # 加载并调整图片为32x32灰度图片
    img = cv2.imread(img_file, 0)
    img = cv2.resize(img, (width, high), interpolation=cv2.INTER_CUBIC)

    # 创建二维列表
    h, w = img.shape[:2]
    vis0 = np.zeros((h, w), np.float32)
    vis0[:h, :w] = img  # 填充数据

    # 二维Dct变换
    vis1 = cv2.dct(cv2.dct(vis0))
    vis1.resize(32, 32)

    # 把二维list变成一维list
    img_list = vis1.flatten()

    # 计算均值
    avg = sum(img_list) * 1. / len(img_list)
    avg_list = ['0' if i > avg else '1' for i in img_list]

    # 得到哈希值
    return ''.join(['%x' % int(''.join(avg_list[x:x + 4]), 2) for x in range(0, 32 * 32, 4)])


def hamming_dist(s1, s2):
    return 1 - sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)]) * 1. / (32 * 32 / 4)
    


def concat_info(type_str, score, time):
    temp = '%s相似度:%.2f %% -----time=%.4f ms' % (type_str, score * 100, time)
    print(temp)
    return temp


def test_diff_hash(img1_path, img2_path, loops=1000):
    img1 = cv2.imread(img1_path)
    img2 = cv2.imread(img2_path)
    start_time = time.time()

    for _ in range(loops):
        hash1 = dHash(img1)
        hash2 = dHash(img2)
        cmp_hash(hash1, hash2)

    print(">>> 执行%s次耗费的时间为%.4f s." % (loops, time.time() - start_time))


def test_aHash(img1, img2):
    time1 = time.time()
    hash1 = aHash(img1)
    hash2 = aHash(img2)
    n = cmp_hash(hash1, hash2)
    return concat_info("均值哈希算法", n, time.time() - time1) + "\n"


def test_dHash(img1, img2):
    time1 = time.time()
    hash1 = dHash(img1)
    hash2 = dHash(img2)
    n = cmp_hash(hash1, hash2)
    return concat_info("差值哈希算法", n, time.time() - time1) + "\n"


def test_pHash(img1_path, img2_path):
    time1 = time.time()
    hash1 = pHash(img1_path)
    hash2 = pHash(img2_path)
    n = hamming_dist(hash1, hash2)
    return concat_info("感知哈希算法", n, time.time() - time1) + "\n"


def deal(img1_path, img2_path):
    info = ''

    img1 = cv2.imread(img1_path)
    img2 = cv2.imread(img2_path)

    # 计算图像哈希相似度
    info = info + test_aHash(img1, img2)
    info = info + test_dHash(img1, img2)
    info = info + test_pHash(img1_path, img2_path)
    return info


def contact_path(file_name):
    output_path = "./source"
    return path.join(output_path, file_name)


def main():
    data_img_name = 'lenna.png'
    data_img_name_base = data_img_name.split(".")[0]

    base = contact_path(data_img_name)
    light = contact_path("%s_light.jpg" % data_img_name_base)
    resize = contact_path("%s_resize.jpg" % data_img_name_base)
    contrast = contact_path("%s_contrast.jpg" % data_img_name_base)
    sharp = contact_path("%s_sharp.jpg" % data_img_name_base)
    blur = contact_path("%s_blur.jpg" % data_img_name_base)
    color = contact_path("%s_color.jpg" % data_img_name_base)
    rotate = contact_path("%s_rotate.jpg" % data_img_name_base)

    # 测试算法的效率
    test_diff_hash(base, base)
    test_diff_hash(base, light)
    test_diff_hash(base, resize)
    test_diff_hash(base, contrast)
    test_diff_hash(base, sharp)
    test_diff_hash(base, blur)
    test_diff_hash(base, color)
    test_diff_hash(base, rotate)
    
    # 测试算法的精度(以base和light为例)
    deal(base, light)
    

if __name__ == '__main__':
    main()


結果:
ここに画像の説明を挿入

拡張 - 離散コサイン変換 DCT

  • 離散コサイン変換 (離散コサイン変換) は、主にデータまたは画像の圧縮に使用され、空間領域信号を周波数領域に変換でき、優れた非相関性能を備えています。
  • DCT変換自体は可逆変換であると同時に、DCT変換が対称であるため、量子化・符号化後にDCT逆変換を行うことで、受信側で元の画像情報を復元することができます。
  • DCT 変換は、現在の画像解析および圧縮分野で非常に広範囲に使用されており、一般的な JPEG 静止画像符号化や MJPEG、MPEG 動的符号化などの規格で使用されています。
    ここに画像の説明を挿入
    の、
  • F(u,v) は出力変換結果です。
  • N は元の信号のポイント数です。
  • f(i,j) は元の画像のピクセル点 (i,j) のピクセル値です。
  • c(u)、c(v) は DCT 係数です。

おすすめ

転載: blog.csdn.net/m0_63260018/article/details/131224776