• 为了保证你在浏览本网站时有着更好的体验,建议使用类似Chrome、Firefox之类的浏览器~~
    • 如果你喜欢本站的内容何不Ctrl+D收藏一下呢,与大家一起分享各种编程知识~
    • 本网站研究机器学习、计算机视觉、模式识别~当然不局限于此,生命在于折腾,何不年轻时多折腾一下

关于图像去重的一点研究

Alg admin 4周前 (11-16) 95次浏览 0个评论 扫描二维码

最近在看一些图像去重的一些方法,网上一搜就会看到 phash、dhash 和 ahash 等基于哈希方法的去重算法,这一点跟文本上的 simhash 和 minhash 有着“相似”的处理逻辑。

phash 具体处理逻辑如下所示:

  • 缩小尺寸 为了后边的步骤计算简单些
  • 简化色彩 将图片转化成灰度图像,进一步简化计算量
  • 计算 DCT 计算图片的 DCT 变换,得到 32*32 的 DCT 系数矩阵。
  • 缩小 DCT 虽然 DCT 的结果是 32*32 大小的矩阵,但我们只要保留左上角的 8*8 的矩阵,这部分呈现了图片中的最低频率。
  • 计算平均值 如同均值哈希一样,计算 DCT 的均值。
  • 计算 hash 值 根据 8*8 的 DCT 矩阵,设置 0 或 1 的 64 位的 hash 值,大于等于 DCT 均值的设为”1”,小于 DCT 均值的设为“0”。组合在一起,就构成了一个 64 位的整数,这就是这张图片的指纹。

其他的 hash 方法有着类似的处理逻辑,代码实现有很多了,google 一下出来了。

这种局部感知 hash 的方法其实存在一定的问题,如果图像经过平移,缩放等操作,使用当前这种方法可能识别不出来。
博主自己想的方案有两个方向:
(1)基于 cnn 预训练模型提取特征计算相似度
(2)基于 AE 自编码降维计算图像相似度

基于预训练模型

使用这种方法实践应该是最快的,只要调用一下预训练 model,然后获取输出即可,常见的像 vgg/resnet/mobilenet 等预训练网络,这些网络在超大的 ImageNet 上面都有过训练。本质上还是使用卷积网络不断的提取特征的过程,像 vgg16 动辄上亿的参数,真的是很大。

说到预训练在图像和 nlp 都是有着很大的意义,之前研究过 bert 预训练模型也是很强大,这个可以帮助下游任务提升效果。

目前 keras 和 tensorflow 里面都自带了很多预训练模型

from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
import numpy as np
import os
import tensorflow as tf
os.environ["CUDA_VISIBLE_DEVICES"] = "6"
gpu_options = tf.GPUOptions(allow_growth=True)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
from keras.utils import plot_model
from matplotlib import pyplot as plt

#【0】VGG16 模型,加载预训练权重,不保留顶层的三个全连接层
model = VGG16(weights='imagenet', include_top=False) 
print(model.summary())                                 # 打印模型概况
plot_model(model,to_file = 'a simple convnet.png')     # 画出模型结构图,并保存成图片

'''

Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, None, None, 3)     0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, None, None, 64)    1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, None, None, 64)    36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, None, None, 64)    0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, None, None, 128)   73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, None, None, 128)   147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, None, None, 128)   0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, None, None, 256)   295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, None, None, 256)   590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, None, None, 256)   590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, None, None, 256)   0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, None, None, 512)   1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, None, None, 512)   2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, None, None, 512)   2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, None, None, 512)   0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, None, None, 512)   2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, None, None, 512)   2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, None, None, 512)   2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, None, None, 512)   0         
=================================================================
Total params: 14,714,688
Trainable params: 14,714,688
Non-trainable params: 0
_________________________________________________________________

'''

#【1】从网上下载一张图片,保存在当前路径下
img_path = './elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224)) # 加载图片并 resize 成 224x224

#【2】显示图片
plt.imshow(img)
plt.show()

#【3】将图片转化为 4d tensor 形式
x = image.img_to_array(img)    # x.shape: (224, 224, 3)
x = np.expand_dims(x, axis=0)  # x.shape: (1, 224, 224, 3)

#【4】数据预处理
x = preprocess_input(x)       #去均值中心化,preprocess_input 函数详细功能见注释

#【5】提取特征
features = model.predict(x)
print(features.shape) #(1,7,7,512)

上面的代码是参考了https://www.jianshu.com/p/568168ad4950 这篇文章的实现,不过它加载的预训练模型不包含输出层,如果你将参数include_top=True则会发现多三层出来

model = VGG16(weights='imagenet', include_top=True)

主要是新增了 fc1 fc2 softmax ,我是使用了 fc2 作为结果的输出表征图像的特征。

如何获取 fc2 输出的结果呢,这个就是 keras 获取中间层输出哈,

from keras import backend as K

# with a Sequential model
get_3rd_layer_output = K.function([model.layers[0].input],
                                  [model.layers[3].output])
layer_output = get_3rd_layer_output([x])[0]

你想获取 fc2 层的输出可以做下修改

get_3rd_layer_output = K.function([model.layers[0].input], [model.get_layer(“fc2”).output])

最后 layer_output 就是你想要的 fc2 层输出了。

AE 自编码

未完待续,先出门,回来再补上


Deeplearn, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明关于图像去重的一点研究
喜欢 (0)
admin
关于作者:
互联网行业码农一枚/业余铲屎官/数码影音爱好者/二次元

您必须 登录 才能发表评论!