4.1.2 图像特征提取与检索


3.2 图像特征提取与检索

学习目标

  • 掌握传统和深度学习图像特征提取方法
  • 理解图像检索的技术原理和实现方法
  • 学会使用深度学习模型进行图像特征提取
  • 掌握多模态图像处理和检索的技术要点

核心概念

  • 图像特征提取:从图像中提取有意义的视觉特征
  • 传统特征:SIFT、SURF、HOG等手工设计特征
  • 深度特征:CNN、ResNet、ViT等自动学习的特征
  • 图像检索:基于特征相似性的图像搜索
  • 特征向量:将图像转换为数值向量表示

环境准备 / 前置知识

  • Python 3.8+
  • 计算机视觉库:OpenCV、PIL
  • 深度学习库:PyTorch、torchvision
  • 特征处理库:scikit-learn、faiss
  • 图像处理库:librosa(音频处理)

分步实战

步骤 1:图像预处理技术

基础图像预处理

图像预处理是特征提取的第一步,直接影响后续处理效果。完整的预处理流程包括:

import cv2 import numpy as np from typing import Tuple, Optional import logging from PIL import Image class ImagePreprocessor: def __init__(self, target_size: Tuple[int, int] = (224, 224)): self.target_size = target_size self.logger = logging.getLogger(__name__) def resize_image(self, image: np.ndarray, keep_aspect_ratio: bool = True, interpolation: int = cv2.INTER_AREA) -> np.ndarray: """调整图像大小""" try: if not keep_aspect_ratio: # 直接调整到目标大小 return cv2.resize(image, self.target_size, interpolation=interpolation) else: # 保持长宽比的resize h, w = image.shape[:2] target_w, target_h = self.target_size # 计算缩放比例 scale = min(target_w / w, target_h / h) new_w = int(w * scale) new_h = int(h * scale) # 调整大小 resized = cv2.resize(image, (new_w, new_h), interpolation=interpolation) # 创建目标大小的画布 if len(image.shape) == 3: canvas = np.zeros((target_h, target_w, 3), dtype=np.uint8) else: canvas = np.zeros((target_h, target_w), dtype=np.uint8) # 计算居中位置 y_offset = (target_h - new_h) // 2 x_offset = (target_w - new_w) // 2 # 将调整后的图像放置在画布中心 canvas[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized return canvas except Exception as e: self.logger.error(f"调整图像大小失败: {e}") return image def normalize_image(self, image: np.ndarray, mean: Optional[np.ndarray] = None, std: Optional[np.ndarray] = None) -> np.ndarray: """图像归一化""" if mean is None: mean = np.array([0.485, 0.456, 0.406]) if std is None: std = np.array([0.229, 0.224, 0.225]) # 转换为float32 image = image.astype(np.float32) # 归一化到[0,1] image = image / 255.0 # 标准化 image = (image - mean) / std return image def augment_image(self, image: np.ndarray, rotation_range: float = 10, zoom_range: float = 0.1) -> np.ndarray: """数据增强""" # 随机旋转 angle = np.random.uniform(-rotation_range, rotation_range) h, w = image.shape[:2] center = (w // 2, h // 2) rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0) rotated = cv2.warpAffine(image, rotation_matrix, (w, h)) # 随机缩放 scale = np.random.uniform(1 - zoom_range, 1 + zoom_range) scaled = cv2.resize(rotated, None, fx=scale, fy=scale) return scaled # 使用示例 preprocessor = ImagePreprocessor(target_size=(224, 224)) # 生成示例图像 image = np.random.randint(0, 255, (300, 400, 3), dtype=np.uint8) # 预处理 resized = preprocessor.resize_image(image, keep_aspect_ratio=True) normalized = preprocessor.normalize_image(resized) augmented = preprocessor.augment_image(resized) print(f"原始图像形状: {image.shape}") print(f"处理后图像形状: {normalized.shape}")

步骤 2:传统图像特征提取

SIFT特征提取

SIFT(Scale-Invariant Feature Transform)是一种经典的尺度不变特征提取算法:

import cv2 import numpy as np from typing import List, Tuple class SIFTFeatureExtractor: def __init__(self, nfeatures: int = 0, nlevels: int = 4, contrastThreshold: float = 0.04, edgeThreshold: int = 10, sigma: float = 1.6): self.sift = cv2.SIFT_create( nfeatures=nfeatures, nlevels=nlevels, contrastThreshold=contrastThreshold, edgeThreshold=edgeThreshold, sigma=sigma ) self.descriptors = None self.keypoints = None def extract_features(self, image: np.ndarray) -> Tuple: """提取SIFT特征""" # 转换为灰度图像 if len(image.shape) == 3: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) else: gray = image # 检测关键点和描述符 keypoints, descriptors = self.sift.detectAndCompute(gray, None) self.keypoints = keypoints self.descriptors = descriptors return keypoints, descriptors def match_features(self, descriptors1: np.ndarray, descriptors2: np.ndarray, ratio_threshold: float = 0.75) -> List: """特征匹配""" # 创建BFMatcher bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False) # 使用ratio test进行匹配 matches = bf.knnMatch(descriptors1, descriptors2, k=2) # 应用ratio test good_matches = [] for match_pair in matches: if len(match_pair) == 2: m, n = match_pair if m.distance < ratio_threshold * n.distance: good_matches.append(m) return good_matches # 使用示例 sift_extractor = SIFTFeatureExtractor() # keypoints, descriptors = sift_extractor.extract_features(image)

HOG特征提取

HOG(Histogram of Oriented Gradients)是一种用于物体检测的特征:

import cv2 import numpy as np from typing import Tuple class HOGFeatureExtractor: def __init__(self, orientations: int = 9, pixels_per_cell: Tuple[int, int] = (8, 8), cells_per_block: Tuple[int, int] = (2, 2)): self.orientations = orientations self.pixels_per_cell = pixels_per_cell self.cells_per_block = cells_per_block # 创建HOG描述符 self.hog = cv2.HOGDescriptor( _winSize=(64, 64), _blockSize=(16, 16), _blockStride=(8, 8), _cellSize=pixels_per_cell, _nbins=orientations ) def extract_features(self, image: np.ndarray) -> np.ndarray: """提取HOG特征""" # 转换为灰度图像 if len(image.shape) == 3: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) else: gray = image # 调整图像大小 if gray.shape[:2] != (64, 64): gray = cv2.resize(gray, (64, 64)) # 提取HOG特征 features = self.hog.compute(gray) return features.flatten() # 使用示例 hog_extractor = HOGFeatureExtractor() # hog_features = hog_extractor.extract_features(image)

步骤 3:深度学习图像特征提取

ResNet特征提取

ResNet(Residual Network)是深度学习中常用的图像特征提取模型:

import torch import torch.nn as nn import torchvision.transforms as transforms from PIL import Image import numpy as np class ResNetFeatureExtractor: def __init__(self, model_name: str = 'resnet50', layer: str = 'avgpool'): # 加载预训练的ResNet模型 self.model = torch.hub.load('pytorch/vision', model_name, pretrained=True) self.layer_name = layer # 移除最后的分类层 self.model = nn.Sequential(*list(self.model.children())[:-1]) # 设置为评估模式 self.model.eval() # 图像预处理 self.transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') self.model.to(self.device) def extract_features(self, image_path: str) -> np.ndarray: """提取ResNet特征""" # 加载图像 image = Image.open(image_path).convert('RGB') # 预处理 input_tensor = self.transform(image).unsqueeze(0) input_tensor = input_tensor.to(self.device) # 提取特征 with torch.no_grad(): features = self.model(input_tensor) # 展平特征向量 features = features.squeeze().cpu().numpy() return features # 使用示例 resnet_extractor = ResNetFeatureExtractor(model_name='resnet50') # features = resnet_extractor.extract_features('/tmp/example.jpg') # print(f"ResNet特征形状: {features.shape}")

步骤 4:图像检索系统实现

基于FAISS的图像检索系统

import faiss import numpy as np from typing import List, Tuple, Dict import pickle import os class ImageRetrievalSystem: def __init__(self, feature_dim: int = 2048, index_type: str = 'flat'): self.feature_dim = feature_dim self.index_type = index_type self.index = None self.image_paths = [] self.image_metadata = {} # 创建索引 self._create_index() def _create_index(self): """创建FAISS索引""" if self.index_type == 'flat': # 平坦索引 self.index = faiss.IndexFlatL2(self.feature_dim) elif self.index_type == 'ivf': # IVF索引(更快的搜索) quantizer = faiss.IndexFlatL2(self.feature_dim) nlist = 100 # 聚类中心数量 self.index = faiss.IndexIVFFlat(quantizer, self.feature_dim, nlist) elif self.index_type == 'hnsw': # HNSW索引(快速近似搜索) self.index = faiss.IndexHNSWFlat(self.feature_dim, 32) # M=32 else: raise ValueError(f"不支持的索引类型: {self.index_type}") def add_images(self, features: np.ndarray, image_paths: List[str], metadata: List[Dict] = None): """添加图像到检索系统""" # 转换为float32类型 features = features.astype(np.float32) # 训练索引(如果是IVF索引) if isinstance(self.index, faiss.IndexIVFFlat): self.index.train(features) # 添加特征到索引 self.index.add(features) # 保存图像路径 self.image_paths.extend(image_paths) # 保存元数据 if metadata is not None: for i, meta in enumerate(metadata): self.image_metadata[len(self.image_paths) - len(image_paths) + i] = meta def search(self, query_features: np.ndarray, top_k: int = 10) -> List[Dict]: """搜索相似图像""" if self.index is None: raise ValueError("索引未构建") # 转换为float32类型 query_features = query_features.astype(np.float32) # 归一化特征向量 query_features = query_features / np.linalg.norm(query_features) # 搜索 distances, indices = self.index.search(query_features.reshape(1, -1), top_k) # 获取结果 results = [] for idx, distance in zip(indices[0], distances[0]): if idx < len(self.image_paths): result = { 'image_path': self.image_paths[idx], 'distance': float(distance), 'similarity': 1.0 / (1.0 + float(distance)) # 转换为相似度 } # 添加元数据 if idx in self.image_metadata: result['metadata'] = self.image_metadata[idx] results.append(result) return results # 使用示例 # retrieval_system = ImageRetrievalSystem(feature_dim=2048, index_type='ivf') # # 假设有1000张图像的特征 # features = np.random.rand(1000, 2048).astype(np.float32) # image_paths = [f'/tmp/image_{i}.jpg' for i in range(1000)] # metadata = [{'id': i, 'category': f'cat_{i%10}'} for i in range(1000)] # # # 添加图像到检索系统 # retrieval_system.add_images(features, image_paths, metadata) # # # 搜索相似图像 # query_features = np.random.rand(2048).astype(np.float32) # results = retrieval_system.search(query_features, top_k=5)

本节小结

本节详细介绍了图像特征提取与检索的核心技术,涵盖了从传统方法到深度学习的完整技术栈。主要技术要点包括:

  1. 图像预处理

    • 图像尺寸调整和归一化
    • 数据增强技术
    • 图像质量控制
  2. 传统特征提取

    • SIFT特征提取与匹配
    • HOG特征提取
    • LBP纹理特征提取
    • 手工设计特征的优势和应用场景
  3. 深度学习特征提取

    • ResNet特征提取
    • Vision Transformer特征提取
    • 预训练模型的微调技术
    • 特征向量化表示
  4. 图像检索技术

    • 基于FAISS的快速检索
    • 特征索引构建
    • 相似度计算和排序
    • 大规模图像检索的优化策略

通过完整的代码示例和实现方案,本节为读者提供了图像特征提取与检索的实用技术参考,为构建多模态知识库中的图像处理模块奠定了坚实基础。

关键词:图像特征提取, 图像检索, SIFT, ResNet, FAISS, 多模态, 特征向量, 深度学习, 计算机视觉, 相似性搜索

难度:进阶

预计阅读:45分钟


作者与出处
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: Star-10b78764的小龙虾 转发
评论区 (0)
U