传统的文本搜索主要是关键字匹配,而目前图片和音乐的搜索却使用使用特征向量的方式。
向量就是使用一组数从多个维度描述特征,虽然每个维度的含义我们可能无法得知,但是模型知道就足够了;
如果您的目标是快速比较两张图片的特征值,并且需要计算量较小的算法,那么使用较轻量级的模型或特征提取方法会更适合。以下是可选方案:
MobileNet 是一个轻量级的卷积神经网络,设计用于在移动和嵌入式设备上高效运行。与 VGG16 相比,它更快且计算量更小。
MobileNet 是一种轻量级的深度神经网络模型,由 Google 在 2017 年提出。它主要用于图像分类和特征提取任务,并且相比于传统的深度神经网络模型,MobileNet 具有更小的模型尺寸和更低的计算成本,适用于移动设备等资源受限的环境。
MobileNet 的设计思想是通过深度可分离卷积(Depthwise Separable Convolution)来减少模型参数和计算量。深度可分离卷积将标准卷积分解为两个步骤:深度卷积(Depthwise Convolution)和逐点卷积(Pointwise Convolution)。深度卷积对每个输入通道进行单独卷积操作,而逐点卷积则对深度卷积的结果进行 1x1 卷积操作,以融合不同通道的信息。
MobileNet 的结构比较简单,由若干个深度可分离卷积层和激活函数层组成,最后通过全局平均池化(Global Average Pooling)将特征图转换为固定长度的特征向量。该特征向量可以用于图像分类、相似度计算等任务。
MobileNet 通过在 ImageNet 数据集上进行预训练,可以提取图像中的语义信息,并用于各种计算机视觉任务。在 TensorFlow 中,可以通过使用 tensorflow.keras.applications.MobileNet 类加载预训练的 MobileNet 模型,并在自己的任务中使用它。
这里我测试了两张一样的图片,其中一张的缩小的版本:
import tensorflow as tf
from tensorflow.keras.applications import MobileNet
from tensorflow.keras.applications.mobilenet import preprocess_input
from tensorflow.keras.preprocessing import image
import numpy as np
from PIL import Image
# 加载预训练的MobileNet模型,不包括顶部的全连接层
model = MobileNet(weights='imagenet', include_top=False, pooling='avg')
def get_image_feature_vector(img_path):
img = Image.open(img_path)
img = img.resize((224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
features = model.predict(x)
return features.flatten()
# 获取两张图片的特征向量
features1 = get_image_feature_vector('image/1.jpg')
features2 = get_image_feature_vector('image/2.jpg')
# 计算余弦相似度
similarity = np.dot(features1, features2) / (np.linalg.norm(features1) * np.linalg.norm(features2))
print("Similarity:", similarity)
# Similarity: 0.99791807
GO版本
package main
import (
"fmt"
"log"
"math"
tf "github.com/tensorflow/tensorflow/tensorflow/go"
"github.com/disintegration/imaging"
)
func main() {
// 加载预训练的 MobileNet 模型
model, err := tf.LoadSavedModel("mobilenet_saved_model", []string{"serve"}, nil)
if err != nil {
log.Fatalf("无法加载模型: %v", err)
}
defer model.Session.Close()
// 加载并预处理图像
img1, err := loadImageAndPreprocess("image/1.jpg")
if err != nil {
log.Fatalf("无法加载图像: %v", err)
}
img2, err := loadImageAndPreprocess("image/2.jpg")
if err != nil {
log.Fatalf("无法加载图像: %v", err)
}
// 获取图像的特征向量
features1, err := extractImageFeatureVector(model, img1)
if err != nil {
log.Fatalf("无法提取特征向量: %v", err)
}
features2, err := extractImageFeatureVector(model, img2)
if err != nil {
log.Fatalf("无法提取特征向量: %v", err)
}
// 计算余弦相似度
similarity := cosineSimilarity(features1, features2)
fmt.Println("Similarity:", similarity)
}
// loadImageAndPreprocess 加载图像并进行预处理
func loadImageAndPreprocess(imgPath string) (*tf.Tensor, error) {
img, err := imaging.Open(imgPath)
if err != nil {
return nil, fmt.Errorf("无法打开图像: %v", err)
}
img = imaging.Resize(img, 224, 224, imaging.Lanczos)
preprocessedImg := preprocessImage(img)
return tensorFromImage(preprocessedImg), nil
}
// preprocessImage 对图像进行预处理
func preprocessImage(img image.Image) image.Image {
// 在这里添加预处理逻辑,如归一化、标准化等
return img
}
// tensorFromImage 从图像创建 TensorFlow 张量
func tensorFromImage(img image.Image) *tf.Tensor {
bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
var pixels []float32
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
r, g, b, _ := img.At(x, y).RGBA()
pixels = append(pixels, float32(r>>8), float32(g>>8), float32(b>>8))
}
}
return tf.NewTensor(pixels)
}
// extractImageFeatureVector 提取图像的特征向量
func extractImageFeatureVector(model *tf.SavedModel, img *tf.Tensor) (*tf.Tensor, error) {
output, err := model.Session.Run(
map[tf.Output]*tf.Tensor{
model.Graph.Operation("input_1").Output(0): img,
},
[]tf.Output{
model.Graph.Operation("global_average_pooling2d/Mean").Output(0),
},
nil,
)
if err != nil {
return nil, fmt.Errorf("无法提取特征向量: %v", err)
}
return output[0], nil
}
// cosineSimilarity 计算余弦相似度
func cosineSimilarity(features1, features2 *tf.Tensor) float32 {
numerator, err := tf.MatMul(features1, features2, false, true)
if err != nil {
log.Fatalf("无法计算余弦相似度的分子: %v", err)
}
denominator := tf.Norm(features1, 2).Mul(tf.Norm(features2, 2))
similarity := numerator.Reshape([]int32{}).Value().(float32) / denominator.Reshape([]int32{}).Value().(float32)
return similarity
}
备注:MobileNet 是一种轻量级的深度卷积神经网络,设计用于在资源受限的设备上运行,适合提取图像的全局特征。
ORB (Oriented FAST and Rotated BRIEF) 是一种用于特征提取和匹配的快速算法,适用于图像的快速比对。
import cv2
def get_orb_features(img_path):
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create()
keypoints, descriptors = orb.detectAndCompute(img, None)
return descriptors
def compare_images(img_path1, img_path2):
des1 = get_orb_features(img_path1)
des2 = get_orb_features(img_path2)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
return len(matches)
# 比较两张图片
num_matches = compare_images('image/1.jpg', 'image/2.jpg')
print("Number of ORB matches:", num_matches)
#ORB Similarity: 0.22
同时给出go版本
package main
import (
"fmt"
"log"
"github.com/lazywei/go-opencv/opencv"
)
func main() {
// 读取图像
img1 := opencv.LoadImage("image/1.jpg", 0)
img2 := opencv.LoadImage("image/2.jpg", 0)
if img1 == nil || img2 == nil {
log.Fatal("无法加载图像")
}
defer img1.Release()
defer img2.Release()
// 创建 ORB 特征检测器
orb := opencv.NewORB()
defer orb.Release()
// 在图像上检测特征点和计算特征描述符
kp1, desc1 := orb.DetectAndCompute(img1, opencv.NewMat())
kp2, desc2 := orb.DetectAndCompute(img2, opencv.NewMat())
// 创建 BFMatcher
bf := opencv.NewBFMatcher()
defer bf.Release()
// 匹配特征描述符
matches := bf.KnnMatch(desc1, desc2, 2)
// 筛选最佳匹配
var goodMatches []opencv.DMatch
for _, m := range matches {
if len(m) == 2 && m[0].Distance < 0.75*m[1].Distance {
goodMatches = append(goodMatches, m[0])
}
}
// 输出相似度
similarity := float64(len(goodMatches)) / float64(len(kp1))
fmt.Println("ORB Similarity:", similarity)
}
选择具体的方法应根据实际需求和应用场景决定。如果需要在移动设备或嵌入式设备上运行,且对计算速度要求较高,可以选择 ORB。如果在服务器环境中运行,且对图像比对精度要求较高,可以选择 MobileNet。
图像感知哈希算法(如差值哈希、感知哈希)可以快速计算图像的哈希值,并比较这些哈希值以确定图像相似度。
from PIL import Image
import imagehash
def get_image_hash(img_path):
img = Image.open(img_path)
return imagehash.average_hash(img)
def calculate_similarity(hash1, hash2):
# 计算哈希值的汉明距离
hamming_distance = hash1 - hash2
# 将汉明距离转换为相似度(距离越小,相似度越高)
max_distance = len(hash1.hash) ** 2 # 64 for aHash
similarity = 1 - (hamming_distance / max_distance)
return similarity
# 获取两张图片的哈希值
hash1 = get_image_hash('image/1.jpg')
hash2 = get_image_hash('image/2.jpg')
# 计算相似度
similarity = calculate_similarity(hash1, hash2)
print("Similarity (1 is identical, 0 is completely different):", similarity)
这些方法在计算速度和资源消耗方面比 VGG16 更加高效,可以满足快速比对图片特征值的需求。根据具体应用场景选择适合的方法即可。
package main
import (
"fmt"
"log"
"github.com/corona10/goimagehash"
"github.com/disintegration/imaging"
)
func main() {
// 打开图像文件
img1, err := imaging.Open("image/1.jpg")
if err != nil {
log.Fatalf("无法打开图像: %v", err)
}
img2, err := imaging.Open("image/2.jpg")
if err != nil {
log.Fatalf("无法打开图像: %v", err)
}
// 计算图像的哈希值
hash1, err := goimagehash.AverageHash(img1)
if err != nil {
log.Fatalf("无法计算哈希值: %v", err)
}
hash2, err := goimagehash.AverageHash(img2)
if err != nil {
log.Fatalf("无法计算哈希值: %v", err)
}
// 计算汉明距离并转换为相似度
hammingDistance := hash1.Distance(hash2)
maxDistance := len(hash1) * len(hash1)
similarity := 1.0 - (float64(hammingDistance) / float64(maxDistance))
fmt.Println("Similarity (1 is identical, 0 is completely different):", similarity)
}
平均哈希 (aHash):
步骤:
转为灰度图。
3. 计算像素平均值。
4. 每个像素值与平均值比较,生成哈希值。
感知哈希 (pHash):
步骤:
转为灰度图。
3. 进行离散余弦变换 (DCT)。
4. 取左上角 8x8 的 DCT 系数,计算平均值。
5. 每个系数与平均值比较,生成哈希值。
差值哈希 (dHash):
步骤:
转为灰度图。
3. 每行比较相邻像素,生成哈希值。
我们使用图哈希以图搜图是否能实现需求呢,其实不一定,哈希实现的精度低,可以粗略的确定范围,
再使用MobileNet再次比对,实现分级的搜索,这里测试一下:
分级对比:
from PIL import Image
import imagehash
import cv2
import numpy as np
import tensorflow as tf
from tensorflow.keras.applications import MobileNet
from tensorflow.keras.applications.mobilenet import preprocess_input
from tensorflow.keras.preprocessing import image
# 加载预训练的MobileNet模型,不包括顶部的全连接层
model = MobileNet(weights='imagenet', include_top=False, pooling='avg')
def get_image_hash(img_path, hash_function=imagehash.average_hash):
img = Image.open(img_path)
return hash_function(img)
def calculate_hash_similarity(hash1, hash2):
hamming_distance = hash1 - hash2
max_distance = len(hash1.hash) ** 2
similarity = 1 - (hamming_distance / max_distance)
return similarity
def get_orb_features(img_path):
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create()
keypoints, descriptors = orb.detectAndCompute(img, None)
return keypoints, descriptors
def match_orb_features(descriptors1, descriptors2):
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(descriptors1, descriptors2)
matches = sorted(matches, key=lambda x: x.distance)
return matches
def get_image_feature_vector(img_path):
img = Image.open(img_path)
img = img.resize((224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
features = model.predict(x)
return features.flatten()
# 初步哈希筛选
hash1 = get_image_hash('image/1.jpg')
hash2 = get_image_hash('image/2.jpg')
hash_similarity = calculate_hash_similarity(hash1, hash2)
print("Hash Similarity:", hash_similarity)
if hash_similarity > 0.8: # 设置一个阈值进行初步筛选
# ORB特征匹配
keypoints1, descriptors1 = get_orb_features('image/1.jpg')
keypoints2, descriptors2 = get_orb_features('image/2.jpg')
matches = match_orb_features(descriptors1, descriptors2)
orb_similarity = len(matches) / min(len(descriptors1), len(descriptors2))
print("ORB Similarity:", orb_similarity)
# MobileNet特征比对
features1 = get_image_feature_vector('image/1.jpg')
features2 = get_image_feature_vector('image/2.jpg')
mobile_similarity = np.dot(features1, features2) / (np.linalg.norm(features1) * np.linalg.norm(features2))
print("MobileNet Similarity:", mobile_similarity)
else:
print("Images are not similar enough based on hash comparison.")
MobileNet 可以用于物体分类任务。MobileNet 在 ImageNet 数据集上进行了预训练,其中包含了 1000 种不同类别的物体。因此,你可以使用 MobileNet 模型对图像中的物体进行分类。
你可以使用 MobileNet 模型提取图像的特征向量,然后将这些特征向量输入到一个分类器中进行分类。在 TensorFlow 中,你可以通过加载 MobileNet 模型并在顶部添加一个分类器来实现这一点。这个分类器可以是一个全连接层或者其他类型的分类器,具体取决于你的应用需求。
除了在 ImageNet 上进行预训练的 MobileNet 外,你也可以针对特定的物体分类任务对 MobileNet 进行微调(fine-tuning),以适应你的数据集和任务要求。这样可以提高模型在特定物体分类任务上的性能和准确度。
import tensorflow as tf
import numpy as np
from tensorflow.keras.applications import MobileNet
from tensorflow.keras.applications.mobilenet import preprocess_input
from tensorflow.keras.applications.mobilenet import decode_predictions
from tensorflow.keras.preprocessing import image
# 加载预训练的 MobileNet 模型
model = MobileNet(weights='imagenet')
def preprocess_image(img_path):
# 加载图像并调整大小
img = image.load_img(img_path, target_size=(224, 224))
# 将图像转换为 NumPy 数组
img_array = image.img_to_array(img)
# 扩展数组的维度以匹配模型的输入要求
img_array = np.expand_dims(img_array, axis=0)
# 预处理图像
img_array = preprocess_input(img_array)
return img_array
def classify_image(img_path):
# 预处理图像
img_array = preprocess_image(img_path)
# 使用 MobileNet 模型进行分类
preds = model.predict(img_array)
# 解码预测结果(获取类别标签)
decoded_preds = decode_predictions(preds, top=1)[0]
# 返回预测的类别和概率
return decoded_preds
# 测试图像分类
img_path = './image/dog1.jpg'
predicted_class = classify_image(img_path)
print(f'Predicted Class: {predicted_class}')
# Predicted Class: [('n02099601', 'golden_retriever', 0.93234175)]
# 金毛

其中的1000种文件分类的列表,
可以在 ImageNet 官方网站上找到标签文件。
ImageNet 官方网站:ImageNet
当然还要区分照片和真实人脸,可以考虑以下几种方法:
这些方法通常会结合在一起,构成一个完整的人脸识别系统,确保门禁识别系统能够有效地区分照片和真实人脸,提高安全性和可靠性。
光线反射检测的方法通常利用摄像头捕捉的图像中的光线反射特征来区分真实人脸和静态照片。以下是一个使用 OpenCV 库来实现简单光线反射检测的 Python 代码示例:
import cv2
def detect_reflection(image_path):
# 读取图像
image = cv2.imread(image_path)
# 转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 计算灰度图像的梯度
gradient = cv2.Laplacian(gray, cv2.CV_64F).var()
# 根据梯度阈值进行判断
threshold = 100 # 调整阈值以适应不同情况
if gradient < threshold:
print("照片")
else:
print("真实人脸")
# 在这里替换为你的图像路径
image_path = "path/to/your/image.jpg"
detect_reflection(image_path)
这个代码示例使用 Laplacian 算子来计算图像的梯度,然后根据梯度的方差(变化程度)来判断是否存在光线反射。如果梯度较小,就可能是静态照片;如果梯度较大,则可能是真实人脸。你可以根据实际情况调整阈值来提高准确性。
完。