在本文中,我们将使用DJL中的预训练模型ResNet50来提取图片的特征向量,并计算图片之间的相似度。我们主要关注使用余弦相似度、欧氏距离和内积三种方法对图片特征向量进行1:1比对,以评估图片的相似性,进一步实现图片分类。
我们选用ResNet50模型,该模型在ImageNet数据集上进行了预训练,能够提取512维的特征向量来表征图片的内容。我们将用这些特征向量来计算两张图片之间的相似度。
以下是提取图片特征向量的基本代码:
@SneakyThrows
@Test
public void test1() {
// 初始化图片编码模型
ImageEncoderModel imageEncoderModel = new ImageEncoderModel();
imageEncoderModel.init("models/CLIP-ViT-B-32-IMAGE.pt", 4);
// 加载图片并转换成特征向量
Path imageFile1 = Paths.get("models/3_3.jpg");
Image img1 = OpenCVImageFactory.getInstance().fromFile(imageFile1);
Path imageFile2 = Paths.get("models/3_5.jpg");
Image img2 = OpenCVImageFactory.getInstance().fromFile(imageFile2);
float[] feature1 = imageEncoderModel.predict(img1);
float[] feature2 = imageEncoderModel.predict(img2);
// 计算相似度
float dis = FeatureComparison.dis(feature1, feature2);
logger.info("欧氏距离: " + dis);
float cos = FeatureComparison.cosineSim(feature1, feature2);
logger.info("余弦相似度: " + cos);
float dot = FeatureComparison.dot(feature1, feature2);
logger.info("内积: " + dot);
}[INFO ] - 欧氏距离: 3.9849436
[INFO ] - 余弦相似度: 0.918966
[INFO ] - 内积: 89.196815余弦相似度是通过计算两个向量夹角的余弦值来度量相似度。值越接近1,说明两个向量越相似,代表图片内容越接近:
欧式距离用于测量两个特征向量之间的距离。距离越接近0,表示图片内容越相似:
内积主要用于计算向量相似性,尤其在特征向量方向接近时,内积值会增大。数值越高表示图片越相似。
在一些游戏场景中,例如消消乐,能够自动识别相似图片对于提高游戏体验非常有帮助。以下示例展示了如何在游戏截图中找到与特定目标图像最相似的图片。
@SneakyThrows
@Test
public void test2_4() {
ImageEncoderModel imageEncoderModel = new ImageEncoderModel();
imageEncoderModel.init("models/CLIP-ViT-B-32-IMAGE.pt", 4);
// 加载目标图片特征向量
Path imageFile1 = Paths.get("models/xxl/2_4.jpg");
Image img1 = OpenCVImageFactory.getInstance().fromFile(imageFile1);
float[] feature1 = imageEncoderModel.predict(img1);
// 遍历目录下的所有图片,找到相似度大于0.9的图片
for (File file : new File("models/xxl/").listFiles()) {
Image img2 = OpenCVImageFactory.getInstance().fromFile(Paths.get(file.getAbsolutePath()));
float[] feature2 = imageEncoderModel.predict(img2);
float cos = FeatureComparison.cosineSim(feature1, feature2);
if (cos > 0.9f) {
logger.info("与" + file.getName() + "的余弦相似度: " + cos);
}
}
}[INFO ] - 与2_4.jpg的余弦相似度: 1.0
[INFO ] - 与3_1.jpg的余弦相似度: 0.9555674
[INFO ] - 与3_3.jpg的余弦相似度: 0.95913744
[INFO ] - 与4_1.jpg的余弦相似度: 0.95366186
[INFO ] - 与5_5.jpg的余弦相似度: 0.95639783可以看出,与2_4图片的余弦相似度较高的图片是3_1、3_3、4_1和5_5,符合预期。
在某些情况下,为避免误判,我们可以通过设定更高的余弦相似度阈值来减少相似度较低的匹配结果。
@SneakyThrows
@Test
public void test0_0() {
ImageEncoderModel imageEncoderModel = new ImageEncoderModel();
imageEncoderModel.init("models/CLIP-ViT-B-32-IMAGE.pt", 4);
// 加载目标图片特征向量
Path imageFile1 = Paths.get("models/xxl/0_0.jpg");
Image img1 = OpenCVImageFactory.getInstance().fromFile(imageFile1);
float[] feature1 = imageEncoderModel.predict(img1);
// 增加阈值限制,找到相似度高于0.95的图片
for (File file : new File("models/xxl/").listFiles()) {
Image img2 = OpenCVImageFactory.getInstance().fromFile(Paths.get(file.getAbsolutePath()));
float[] feature2 = imageEncoderModel.predict(img2);
float cos = FeatureComparison.cosineSim(feature1, feature2);
if (cos > 0.95f) {
logger.info("与" + file.getName() + "的余弦相似度: " + cos);
}
}
}[INFO ] - 与0_0.jpg的余弦相似度: 1.0
[INFO ] - 与0_5.jpg的余弦相似度: 0.9142534
[INFO ] - 与2_0.jpg的余弦相似度: 0.9079724通过调整阈值,可以更严格地控制相似匹配的结果,减少误差。
本文通过使用DJL和ResNet50模型对图片特征进行提取和对比,演示了如何实现图片相似度计算和分类的基本流程。余弦相似度、欧氏距离和内积在不同场景下能有效地评估图片之间的相似性,并能够根据不同阈值来提高匹配的准确性。