以vgg为backbone的简易图像检索系统

2023-05-16

        图像检索(Content-based Image Retrieval,简称CBIR)即以图搜图,基于图片语义信息,诸如颜色、纹理、布局、CNN-based高层语义等特征检索技术。该技术可分为实例和类别检索任务。前者,即给定一张物体/场景/建筑类型的待查询图片,查询出包含拍摄自不同角度、光照或有遮挡的,含有相同物体/场景/建筑的图片;后者是检索出同类别的图片。当前需求更贴合实例图像检索。

         CBIR研究在20世纪90年代早期正式开始,研究人员根据诸如纹理、颜色这样的视觉特征对图像建立索引,在这一时期大量优秀算法和图像检索系统被提出。不一一表述。时间拉到2000年后,如图2.1中所示,展示了多年来实例检索任务中的里程碑时刻,并且在图中着重标出了基于SIFT特征和CNN特征算法的提出的时间。2000年可以认为是大部分传统方法结束的时间,当时Smeulders等撰写了“早期的终结”这篇综述。三年后(2003),词袋模型(BoW)进入图像检索社区的视野,并在2004年结合了SIFT方法符被应用于图像分类任务。这后来的近10年时间里,社区见证了BoW模型的优越性,它给图像检索任务带来了各种提升。在2012年,Krizhevsky等人使用AlexNet在ILSRVC 2012上取得了当时世界上最高的识别准确率。至此之后,研究的重心开始向基于深度学习特别是卷积神经网络(CNN)的方法转移。

         下面是一个简易的图像检索代码:

        cbir.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time        :2022/7/6 11:26
# @Author      :weiz
# @ProjectName :CBIR-master
# @File        :cbir.py
# @Description :content-based image retrieval
# Copyright (C) 2021-2025 Jiangxi Institute Of Intelligent Industry Technology Innovation
import os
import cv2
from six.moves import cPickle
from scipy import spatial

from feature_extraction import *
from vgg import *


class CBIR(object):
    database_feature_path = "./database_feature"

    def read_images(self, image_folder_path):
        self.image_info_list = []
        for root, _, image_name_list in os.walk(image_folder_path, topdown=False):
            label_name = root.split('/')[-1].split('\\')[-1]
            for image_name in image_name_list:
                if image_name.split('.')[-1] in ["png", "jpg", "jpeg"]:
                    image_path = os.path.join(root, image_name)
                    self.image_info_list.append([image_path, label_name])
                else:
                    print("{} is not a picture".format(os.path.join(root, image_name)))

    def load_database(self, image_folder_path=None, is_save=False):
        self.database = []
        database_feature_path = CBIR.database_feature_path + '_' + self.feature_extraction_object.get_name()
        if os.path.exists(database_feature_path) and not image_folder_path:
            self.database = cPickle.load(open(database_feature_path, "rb", True))
        else:
            if image_folder_path:
                self.read_images(image_folder_path)
            for image_info in self.image_info_list:  # [[图片路径, 类别]...]
                image = cv2.imread(image_info[0])
                feature = self.feature_extraction_object(image)
                self.database.append({
                    'image_path': image_info[0],
                    'label': image_info[1],
                    'feature': feature
                })
            if is_save:
                cPickle.dump(self.database, open(database_feature_path, "wb", True))

        return self.database

    def __init__(self, feature_extraction_object, image_folder_path):
        self.feature_extraction_object = feature_extraction_object
        self.read_images(image_folder_path)
        # print(self.image_info_list)
        self.load_database(is_save=True)
        # print(self.database)

    def query(self, image, query_depth=3, is_show=False):
        feature_1 = self.feature_extraction_object(image)

        query = []
        for idx, value_2 in enumerate(self.database):
            image_path_2, label_2, feature_2 = value_2["image_path"], value_2["label"], value_2["feature"]
            query.append({
                "distance": self.distance(feature_1, feature_2, "d3"),
                "label": label_2,
                "image_path": image_path_2
            })
        # 如果候选深度足够,取前query_depth个
        query = sorted(query, key=lambda x: x["distance"])
        if query_depth and query_depth <= len(query):
            query = query[:query_depth]

        if is_show:
            cv2.imshow("src", image)
            for idx, q in enumerate(query):
                img = cv2.imread(q["image_path"])
                cv2.imshow("top {}".format(idx + 1), img)
            cv2.waitKey(0)
            cv2.destroyAllWindows()
        return query

    def evaluate(self, database=None, query_depth=3):
        if not database:
            database = self.database

        label_list = []
        for tmp in database:
            label_list.append(tmp["label"])
        label_list = list(set(label_list))
        results = {c: [] for c in label_list}
        for value_1 in database:
            image_path_1, label_1, feature_1 = value_1["image_path"], value_1["label"], value_1["feature"]
            query = []
            for idx, value_2 in enumerate(database):
                image_path_2, label_2, feature_2 = value_2["image_path"], value_2["label"], value_2["feature"]
                if image_path_1 == image_path_2:  # 同一图片不参与评估
                    continue
                query.append({
                    "distance": self.distance(feature_1, feature_2, "d3"),
                    "label": label_2
                })
            # 如果候选深度足够,取前query_depth个
            query = sorted(query, key=lambda x: x["distance"])
            # print(query)
            if query_depth and query_depth <= len(query):
                query = query[:query_depth]

            # 计算有多少被hit
            hit = 0
            precision = []
            for idx, q in enumerate(query):
                if q["label"] == label_1:
                    hit += 1
                    precision.append((hit / (idx + 1.)))
                # else:
                #     print("原始目标, path:{}  label:{}".format(value_1["image_path"], value_1["label"]))
                #     print("预测目标, distance:{}  label:{}".format(q["distance"], q["label"]))
            if hit == 0:
                results[label_1].append(0.)
            else:
                results[label_1].append(np.mean(precision))

        mAPs = []
        for label, Ps in results.items():
            AP = np.mean(Ps)
            print("Class {}, AP {}".format(label, AP))
            mAPs.append(AP)
        print("MAP", np.mean(mAPs))
        return results

    def distance(self, value_1, value_2, d_type="d1"):
        assert value_1.shape == value_2.shape
        if d_type == 'd1':  # 曼哈顿距离
            return np.sum(np.absolute(value_1 - value_2))
        elif d_type == 'd2':  # 欧几里得距离
            return np.sqrt(np.sum((value_1 - value_2) ** 2))
        elif d_type == 'd3':  # 余弦相似度
            return spatial.distance.cosine(value_1, value_2)


test_image_path = "./database/cup2"  # ./test_image  ./database/glasses
if __name__ == "__main__":
    vgg_model = VGGNet(requires_grad=False, net_type="vgg16", show_params=False)
    vgg_model.eval()

    if torch.cuda.is_available():
        vgg_model = vgg_model.cuda()

    cbir = CBIR(vgg_model, "./database")
    # cbir.evaluate(query_depth=3)
    test_image_list = os.listdir(test_image_path)
    for image_name in test_image_list[:3]:
        print(image_name)
        image_path = os.path.join(test_image_path, image_name)
        test_image = cv2.imread(image_path)
        print(cbir.query(test_image, is_show=True, query_depth=2))

        feature_extraction.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time        :2022/7/11 11:20
# @Author      :weiz
# @ProjectName :CBIR-master
# @File        :feature_extraction.py
# @Description :
# Copyright (C) 2021-2025 Jiangxi Institute Of Intelligent Industry Technology Innovation
import torch
import torch.nn as nn
from torchvision import models
from torchvision.models.vgg import VGG
import numpy as np


class VGGNet(VGG):
    cfg = {
        'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
        'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
        'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
        'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512,'M'],
    }

    ranges = {
        'vgg11': ((0, 3), (3, 6), (6, 11), (11, 16), (16, 21)),
        'vgg13': ((0, 5), (5, 10), (10, 15), (15, 20), (20, 25)),
        'vgg16': ((0, 5), (5, 10), (10, 17), (17, 24), (24, 31)),
        'vgg19': ((0, 5), (5, 10), (10, 19), (19, 28), (28, 37))
    }

    means = np.array([103.939, 116.779, 123.68]) / 255.  # mean of three channels in the order of BGR

    def net_layers(self, net_type, batch_norm=False):
        """
        构建网络层
        """
        layers = []
        in_channels = 3
        for value in VGGNet.cfg[net_type]:
            if 'M' == value:
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                conv2d = nn.Conv2d(in_channels, value, kernel_size=3, padding=1)
                if batch_norm:
                    layers += [conv2d, nn.BatchNorm2d(value), nn.ReLU(inplace=True)]
                else:
                    layers += [conv2d, nn.ReLU(inplace=True)]
                in_channels = value
        return nn.Sequential(*layers)

    def __init__(self, pretrained=True, net_type='vgg16', requires_grad=False, remove_fc=False, show_params=False):
        """
        初始化
        """
        super().__init__(self.net_layers(net_type))
        self.ranges = VGGNet.ranges[net_type]
        self.fc_ranges = ((0, 2), (2, 5), (5, 7))
        self.net_type = net_type
        # print(self.features.state_dict())

        if pretrained:
            exec("self.load_state_dict(models.%s(pretrained=True).state_dict())" % net_type)

        if not requires_grad:
            for param in super().parameters():
                param.requires_grad = False

        if remove_fc:  # 不需要全连接层,删除
            self.classifier = None
            self.avgpool = None

        if show_params:
            for name, param in self.named_parameters():
                print(name, param.size())

    def forward(self, image):
        """
        image格式需要BGR格式
        """
        # image = image[:, :, ::-1]
        image = np.transpose(image, (2, 0, 1)) / 255.
        image[0] -= VGGNet.means[0]  # reduce B's mean
        image[1] -= VGGNet.means[1]  # reduce G's mean
        image[2] -= VGGNet.means[2]  # reduce R's mean
        image = np.expand_dims(image, axis=0)

        if torch.cuda.is_available():
            inputs = torch.autograd.Variable(torch.from_numpy(image).cuda().float())
        else:
            inputs = torch.autograd.Variable(torch.from_numpy(image).float())

        # print(inputs.shape)
        # print(self.features)
        x = self.features(inputs)
        avg_pool = torch.nn.AvgPool2d((x.size(-2), x.size(-1)), stride=(x.size(-2), x.size(-1)),
                                      padding=0, ceil_mode=False, count_include_pad=True)
        feature = avg_pool(x)                  # avg.size = N * 512 * 1 * 1
        feature = feature.view(feature.size(0), -1)    # avg.size = N * 512

        feature = np.sum(feature.data.cpu().numpy(), axis=0)
        feature /= np.sum(feature)  # normalize

        return feature

    def get_name(self):
        return self.net_type

    def __coll__(self, x):
        return self.forward(x)

        数据库如下格式即可。

       数据

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以vgg为backbone的简易图像检索系统 的相关文章

  • 相机内参与外参学习记录与理解

    1内参 内参 xff1a 在小孔成像模型中有一个光点P xff0c 其将光以直线的方式穿过纸板的小孔 光心 xff09 xff0c 射到显示纸板 xff08 物理成像平面 xff09 上 xff0c 其坐标系结构如下图所示 而内参的作用即是
  • 现代颜色技术原理及应用学习记录

    感谢CRFX下面分享的书 第一章
  • 【计算机网络】常见的HTTP报文头部信息

    常见的HTTP报文头部信息 HTTP首部字段根据实际用途被分为以下4种类型 1 通用首部 通用首部字段 xff08 General Header Fields xff09 请求报文和响应报文两方都会使用的首部 Cache Control x
  • imagenet 数据集读取数据速度记录

    电脑配置是固态硬盘 xff0c i7cpu xff0c 不进行计算 xff0c 只读数据 xff0c 不做数据增广 xff0c 只做resize 只看不赞不文明 xff0c 这是大约花费两天时间测试结果 目录 xff1a 1 xff0c 最
  • c++调python踩坑日志

    目录 import array 报错 矩阵互相转换 include numpy相关vs2019配置 数组太长报错 import array 报错 参考 xff1a https blog csdn net weixin 40232401 ar
  • pytorch 半精度训练

    必坑记录 这种训练直接变成nan了结果 实验结果 前者采用正常训练 后者改进为 from torch cuda amp import autocast as autocast 实验发现从一epoch44秒 xff0c 减少为35秒 但是训练
  • earth mover‘s distances学习记录

    https zhuanlan zhihu com p 145739750 后面的感觉没讲清楚 一开始我没想清楚 xff0c 为什么可以把问题转换成线性规划问题 问题转换一下 xff0c 一个工厂有A xff0c B xff0c C三个仓库
  • A Tutorial on Energy-Based Learning(机器学习能量模型)学习记录

    1 Introduction 其中Y是标签 X是输入 基于能量的机器学习模型即是寻找这样一个函数 和输入数据越接近 能量越小 给定一个输入 最好的模型即是对应的Y的值是真实值 这个真实值的能量是最小的 此模型可以做的工作有 预测 Ranki
  • dncnn(残差网络图像去燥记录)

    一 xff0c 生成训练数据 1 xff0c 原文相关知识 we use the noisy images from a wide range of noise levels e g 0 55 to train a single DnCNN
  • win10 10016自动重启错误解决记录

    1 xff0c 没解决 1352127440 xff0c 找到 组件服务 xff0c 然后依次展开组件服务 计算机 我的电脑 DCOM配置 xff0c 找到 9CA88EE3 ACB7 47C8 AFC4 AB702511C276 xff0
  • 无偏估计、有效性、相合性

    定义 xff1a 在已知概率分布函数构造的情况下 xff0c 概率分布的一些参数未知 xff0c 如高斯分布的namda 方差 xff0c 而利用采集到的参数来对未知参数进行估计就是参数估计 比较基础的有矩估计 最大似然估计 而不同的方法对
  • Windows10下安装point-cloud-annotation-tool点云标注工具——吐血之路总结

    零 为了标注点云数据 xff0c 经过多方查找免费开源的标注软件 xff0c 根据使用要求和方便程度最终选择了这款可以在Windows下编译安装的point cloud annotation tool xff0c 基于QT和vtk和PCL进
  • ROS 中CompressedImage消息的发布与订阅

    背景 xff1a 某些情况下需要录图像数据的包 xff0c 非常占空间和带宽 xff0c 尤其对于一些工业相机图像一张好几兆 xff0c 每秒30帧的话一份钟好几个G xff0c 这时候可以选择的订阅压缩图像 xff0c 下面直接来个dem
  • 【C#】简单的串口发送

    一 核心代码 xff1a SerialPort serialPort span class token operator 61 span span class token keyword new span span class token
  • 原生OKHttp以及OKHttpUtil的使用

    Android系统提供了两种HTTP通信类 xff0c HttpURLConnection和HttpClient 尽管Google在大部分安卓版本中推荐使用HttpURLConnection xff0c 但是这个类相比HttpClient实
  • c++编译器配置错误问题clang: error: linker command failed with exit code 1 (use -v to see invocation)

    背景 xff1a ubuntu18 04系统 之前装其他程序的时候安装了一些软件 xff0c 不知道什么时候g 43 43 编译器从 usr bin c 43 43 组里面给删掉了 xff0c 默认的编译器成了clang 43 43 xff
  • apollo7.0------浅谈激光雷达运动补偿(二)--计算解析

    背景介绍 运动补偿相关介绍参考第一篇博客 xff1a apollo7 0 浅谈激光雷达运动补偿 龙性的腾飞的博客 CSDN博客 lidar运动补偿 本篇博客主要解释一下上篇博客中运动补偿的计算部分 xff0c 简单来说就是一个利用四元数球面
  • Matlab激光雷达相机联合标定经验分享

    一 背景介绍 联合标定是做多传感器融合的基础工作 xff0c 也是一个没有最好只有更好的研究方向 xff0c 相关论文也是层出不穷 xff0c 网上也有许多开源的工作 xff0c 包括Autoware的工具箱我也试过 xff0c 感觉标定效
  • 如何更改Ubuntu系统的输出为HDMI(耳机,扬声器)?

    由于需要用HDMI外接音频设备 xff0c 故想要改变电脑输出 xff0c 本人用的为Ubuntu14 04 开始在网上搜寻怎么更改 xff0c 查到需在声音设置里面更改音频输出为HDMI xff0c 可当我打开声音设置 xff0c 嗯 x
  • PPT中插入图片背景透明化小技巧

    新版的编辑器真不适应 xff0c 费劲 xff01 xff01 xff01 最近两天做开题答辩ppt xff0c 发现了ppt中处理图片背景的一个小技巧 xff0c 在此分享给大家 PPT一般会带有背景图片 xff0c 那种带浅色调logo

随机推荐

  • ROS发布Float32MultiArray消息C++/Python

    在ros下发布一个字符串消息或整数消息 xff0c 网上例程不少 xff0c ROSwiki上也有教程 xff0c 有时就需要一次发送不止一个数据 xff0c 这时候就得用到数组了 xff0c C 43 43 的也好找 xff0c 不过py
  • c++中string、char *、char[]相互转换

    一 string转char 主要有三种方法可以将str转换为char 类型 xff0c 分别是 xff1a data c str copy 其中 xff0c copy 可能会报安全性错误 xff0c 自行解决即可 3 1 data 方法 s
  • char数组与char指针

    转载来源 xff1a https www cnblogs com nzbbody p 3553222 html https blog csdn net jack 20 article details 78913202 一 0 的添加 存在的
  • linux下tcpdump的使用

    简介 用简单的话来定义tcpdump xff0c 就是 xff1a dump the traffic on a network xff0c 根据使用者的定义对网络上的数据包进行截获的包分析工具 tcpdump可以将网络中传送的数据包的 头
  • khadas vim3安装ros1

    khadas vim3 按照网上的方法可以正常安装ros2 xff0c 但是按照ros1则可能会有一些奇奇怪怪的问题导致按照失败 xff0c 不过在一位群友的帮助下 xff0c 找到了解决的方法 khadas vim3 将源换为下面 xff
  • 【Android】CMake添加多个c文件

    1 准备工作 先下相关的插件 xff0c 进入setting xff0c 勾选这LLDB NDK CMake三个 xff0c 点击OK后即可下载 2 Native C 43 43 工程 简单总结一下CMake使用的操作步骤 1 新建Nati
  • 什么是字节序(端序、低端字节序、高端字节序、网络字节序)

    前言 一个内容为12 xff08 字符串 xff09 的文本文件 xff0c 它的第一个字节是什么 xff08 小端序 xff09 xff1f 如果你的回答是0x32 xff0c 那你真的应该好好理解下字节序了 如下图所示 xff0c 我这
  • APM中电机输出分析

    一 APM类分析 老规矩 xff0c 先上类图 xff08 1 xff09 如图 xff08 1 xff09 所示 xff0c AP Motors是大部分电机类的父类 xff0c 是AC AttitudeControl姿态控制类的保护型成员
  • 解决安装ROS时出现的sudo rosdep init错误问题

    解决安装ROS时出现的sudo rosdep init错误问题 目前安装ROS时输入sudo rosdep init的命令时 xff0c 可能会出现以下的错误 xff1a ERROR cannot download default sour
  • JS实现HTTP请求头-Basic Authorization

    HTTP协议中的 Authorization 请求消息头含有服务器用于验证用户代理身份的凭证 xff0c 通常会在服务器返回401 Unauthorized 状态码以及WWW Authenticate 消息头之后在后续请求中发送此消息头 A
  • C语言Post和Get方法 ,拿过去直接用

    C语言post 和get 方法的实现 我自己实现的post 和get 请求方法 xff0c 可以直接使用在单片机上 xff0c 比如ESP32 上 xff0c OPl1000 上面 xff0c 下面直接上代码 span class toke
  • QT中图表类QChart系列之(1)-基本用法,画折线图、各个类之间的关系

    参考 xff1a https www cnblogs com yunhaisoft p 5180127 html 首先要注意3点 xff1a xff08 1 xff09 在 pro文件中添加 xff1a QT 43 61 charts xf
  • STM32使用FIFO实现USART串口发送中断

    fifo就不要造轮子了 xff0c 用现成的就行了 linux内核中有目前人类写出的基于c语言的最强FIFO xff0c 请自行搜索学习 巧夺天工的kfifo xff0c kfifo精妙无比 xff0c 实在是高 xff0c 其中用到的环回
  • c++的json读取操作

    使用的开源库是nlohmann json 后续操作也都是基于该开源库操作 本地json文件如下 xff1a 34 model config 34 34 model type 34 34 paddlex 34 34 model cfg fil
  • 三维重建了解

    一 三维重建方法 1 1 传统方法 RGBD D来源结构光或者TOF xff1a 缺点 xff0c 重建范围受限 xff0c 一般不能重建大模型 xff1b 比如 xff0c kinectFusion xff0c DynamicFusion
  • docker容器常用命令

    一 常用命令 显示本地镜像 xff1a docker images 显示已经启动的容器 xff1a docker ps a 从docker hub拉取镜像 reed98 airsim v0是镜像名 xff1a docker pull ree
  • ARM学习随笔(12)定时器查询方式和中断方式

    定时器详细讲解 百度文库 点击打开链接 xff08 一 xff09 查询方式和中断方式的区别在于 xff1a 查询方式不断查询标志位然后进行处理 xff0c 而中断要编写中断服务子程序来处理中断事件 xff08 二 xff09 内部中断是指
  • vgg16网络裁剪并加载模型参数

    主要是测试下模型裁剪后转onnx的问题 删除vgg16网络全连接层 xff0c 加载预训练模型并重新保存模型参数 xff0c 将该参数用于转onnx模型格式 usr bin env python coding utf 8 64 Time 2
  • pth转onnx的三种情况

    usr bin env python coding utf 8 64 Time 2022 8 3 16 19 64 Author weiz 64 ProjectName cbir 64 File pth2onnx py 64 Descrip
  • 以vgg为backbone的简易图像检索系统

    图像检索 xff08 Content based Image Retrieval xff0c 简称CBIR xff09 即以图搜图 xff0c 基于图片语义信息 xff0c 诸如颜色 纹理 布局 CNN based高层语义等特征检索技术 该