IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 车辆识别笔记 -> 正文阅读

[人工智能]车辆识别笔记

车辆识别笔记

1.首先分享下学习资源(gihub上找到的)

CaptainEven/Vehicle-Car-detection-and-multilabel-classification: 使用YOLO_v3_tiny和B-CNN实现街头车辆的检测和车辆属性的多标签识别 Using yolo_v3_tiny to do vehicle or car detection and attribute’s multilabel classification or recognize (github.com)
引入资源:
先在网站上把链接复制
在这里插入图片描述

再创建文件保存的地址(尽量不要有中文路径):
在这里插入图片描述
将上图所指位置改成cmd并输入回车,此时进入命令行就是在当前目录下
在这里插入图片描述
最后在命令行中输入:
git clone https://github.com/CaptainEven/Vehicle-Car-detection-and-multilabel-classification.git

就可以将整个文件复制进当前目录下。
在这里插入图片描述

注意:复制时需要用VPN等软件翻墙,否则会报以下错误:
在这里插入图片描述

之后的文件操作只要跟着readme文件走就可以了。

2.VehicleDC.py代码理解:

(1).相关库的调用以及选择对应的device和文件路径

(其中训练模型可按自己要求进行调换)

# coding: utf-8

import os
import sys
import re
import time
import pickle
import shutil
import random
import argparse

from darknet_util import *
from darknet import Darknet
from preprocess import prep_image, process_img, inp_to_image
from dataset import color_attrs, direction_attrs, type_attrs

import torch
import torchvision
import paramiko
import cv2
import numpy as np
import PIL
from PIL import Image
from matplotlib import pyplot as plt
from matplotlib.widgets import Cursor
from matplotlib.image import AxesImage
from scipy.spatial.distance import cityblock
from tqdm import tqdm
from pylab import *

mpl.rcParams['font.sans-serif'] = ['SimHei']
    # 因为pyplot并不默认支持中文显示,所以用rcParams用来设置字体样式以正常显示中文标签,其中SimHei代表:黑体
use_cuda = True
os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'
    #os.environ 输出的是一个字典, 形式为键值对,即key + value,此处作用是从0开始排列CPU
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
    #设置当前使用的设备为GPU0,-1时禁用GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    #如果GPU能使用就使用GPU,否则使用CPU

if use_cuda:
    torch.manual_seed(0)
    #指定随机数种子(种子可以是个任意int值),用来生成伪随机数:
    torch.cuda.manual_seed_all(0)
    #为当前的GPU设置种子,若未设计随机数种子,则每次运行.py文件,得到的tensor不固定,即结果不确定,所以设计随机数固定每次运行时的tensor,方便下次复现实验结果。

print('=> device: ', device)

"""
    此处设定相关的文件路径,如模型等
    此处采取的是相对文件路径,可改绝对路径
"""

local_model_path = './checkpoints/epoch_39.pth'
local_car_cfg_path = './car.cfg'
local_car_det_weights_path = './car_540000.weights'

(2).以类的形式引用resnet18模型以及定义相关函数参数、前向传播

? 本段,先导入对应的resnet18模型,随后用torch.nn.Sequential()构建计算图,将之后子层的计算放入计算图中,定义相应的前向传播,最后以torch.nn.Linear()全连接层计算出数据。 (其中,resnet18模型只有卷积、全连接层,避免了池化层带来的低阶特性消失的问题。)

class Cls_Net(torch.nn.Module):

    def __init__(self, num_cls, input_size):
        torch.nn.Module.__init__(self)
        #继承nn.Module类以自定义模型

        # 通道数,例如RGB为3通道
        self._num_cls = num_cls

        #输入图片文件大小
        self.input_size = input_size

        # 导入预训练模型resnet18,17个卷积层(conv)+1个全连接层(fc)
        self.features = torchvision.models.resnet18(pretrained=True)
        del self.features.fc

        self.features = torch.nn.Sequential(
            *list(self.features.children()))
        '''
        torch.nn.Sequential()为一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行,同时以神经网络模块为元素的有序字典也可以作为传入参数。
        model.children()只会遍历模型的子层,并以列表的形式存起来
        '''

        self.fc = torch.nn.Linear(512 ** 2, num_cls)
        #制造全连接层的框架,输出类别

        #前向传播
    def forward(self, X):

        N = X.size()[0]
        #维度为0,且维度0 = 1
        X = self.features(X)
        #计算张量(tensor)的总特征
        X = X.view(N, 512, 1 ** 2)
        #将向量转换为N行,512列,当N = -1时,表示不知道行数的状态下,根据数据和512自动分配行数
        X = torch.bmm(X, torch.transpose(X, 1, 2)) / (1 ** 2)
        #torch.bmm()矩阵相乘,torch.transpose(Tensor,dim0,dim1)交换维度dim0、dim1
        X = X.view(N, 512 ** 2)
        #向量平铺为N行,512**2列
        X = torch.sqrt(X + 1e-5)
        #对每个元素取平方根之后再取倒数
        X = torch.nn.functional.normalize(X)
        #将某一个维度除以那个维度对应的范数(默认是2范数,2范数一般是指距离),实现行、列向量的缩放
        X = self.fc(X)
        assert X.size() == (N, self._num_cls)
        #判断输出是否符合要求,若符合,函数继续运行,否则报错
        return X

(3).车辆分类 实现各个标签的划分及对图像类型进行转换

? 本段,先用transforms对图像进行相应的预处理,再将各个标签划分开并定义各个函数,例get_predict()函数(用于获取图像预测标签),其中主要的predict()函数中先是将图像转换为tensor类型,再代入前向传播中算相应的数据,再把数据代入get_predict()中,得到相应的预测标签,从而实现车的分类。

class Car_Classifier(object):
    def __init__(self,num_cls,model_path=local_model_path):
        self.net = Cls_Net(num_cls=num_cls, input_size=224).to(device)
        self.net.load_state_dict(torch.load(model_path))
        #引入模型
        print('=> vehicle classifier loaded from %s' % model_path)

        self.net.eval()

        #测试数据转换
        self.transforms = torchvision.transforms.Compose([   #串联多个图片变化操作
            torchvision.transforms.Resize(size=224),         #将输入PIL图像的大小调整为给定224
            torchvision.transforms.CenterCrop(size=224),     #根据给定的size从中心裁剪
            torchvision.transforms.ToTensor(),
            torchvision.transforms.Normalize(mean=(0.485, 0.456, 0.406),
                                             std=(0.229, 0.224, 0.225)) #归一化
        ])

        # 拆分每个标签
        #颜色标签:
        self.color_attrs = color_attrs
        print('=> color_attrs:\n', self.color_attrs)
        #方向标签:
        self.direction_attrs = direction_attrs
        print('=> direction attrs:\n', self.direction_attrs)
        #车种类标签:
        self.type_attrs = type_attrs
        print('=> type_attrs:\n', self.type_attrs)

    def get_predict(self, output):
        output = output.cpu()
        '''
        从CPU中拿出输出(颜色、方向、种类的预测值)
        '''
        pred_color = output[:, :9]
        pred_direction = output[:, 9:11]
        pred_type = output[:, 11:]

        #找出各维度的最大值的下标,返回两个数组.max(1, keepdim=True)[1]表示返回第二个数组
        color_idx = pred_color.max(1, keepdim=True)[1]
        direction_idx = pred_direction.max(1, keepdim=True)[1]
        type_idx = pred_type.max(1, keepdim=True)[1]
        pred = torch.cat((color_idx, direction_idx, type_idx), dim=1)
        #torch.cat()指将张量拼接在一起,除拼接维数dim数值可不同外其余维数数值需相同,方能对齐
        return pred

    def pre_process(self, image):
        if type(image) == np.ndarray:  #类型转换为ndarry数组
            if image.shape[2] == 3:    #将所有3个通道转到 RGB 格式
                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                """
                cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。
                其中:cv2.COLOR_BGR2RGB 将BGR格式转换成RGB格式
                     cv2.COLOR_BGR2GRAY 将BGR格式转换成灰度图片
                """
            elif image.shape[2] == 1:  #将所有 1 个通道转到 RGB 格式
                image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)

            #类型转换为PIL.Image
            image = Image.fromarray(image)
        elif type(image) == PIL.JpegImagePlugin.JpegImageFile:
            #PIL.JpegImagePlugin.JpegImageFile也是一种Image类型
            if image.mode == 'L' or image.mode == 'I':
                image = image.convert('RGB')
                #如果不使用.convert('RGB')进行转换的话,读出来的图像是RGBA四通道的,A通道为透明通道,该对深度学习模型训练来说暂时用不到,因此使用convert('RGB')进行通道转换。

        return image

    def predict(self, img):
        #图像预处理
        img = self.transforms(img)   #转换为tensor
        img = img.view(1, 3, 224, 224)

        # put image data into device
        img = img.to(device)

        #计算前向传播
        output = self.net.forward(img)

        #得到预测结果
        pred = self.get_predict(output)
        color_name = self.color_attrs[pred[0][0]]
        direction_name = self.direction_attrs[pred[0][1]]
        type_name = self.type_attrs[pred[0][2]]
		#返回分类后的各个标签
        return color_name, direction_name, type_name

(4).汽车的检测和识别

? Bounding Box预测(Bounding box predictions):比如输入图像为100*100,然后我们在图像上画网格(可以是3×3按需求定义,网格的数量多少反映了图像识别的精细程度,但越多的网格处理起来也相应的更麻烦)。其中每个格子都有自己对应的标签y,𝑦 = [𝑝𝑐 𝑏𝑥 𝑏𝑦 𝑏? 𝑏𝑤 𝑐1 𝑐2 𝑐3 ],其中pc的1或0表示是否有图像,bx、by、bh、bw表示(如果存在)图像的坐标,而后面的c1、c2、c3表示对应图像的标签。

1.准备text文件的存放地址
2.初始化模型与图像的维度
3.根据bbox预测车辆属性并绘制至orig_img 4.图像的识别与检测

class Car_DC():
    def __init__(self,
                 src_dir,                                          #初始化路径
                 dst_dir,
                 car_cfg_path=local_car_cfg_path,                  #配置文件
                 car_det_weights_path=local_car_det_weights_path,  #权重模型
                 inp_dim=768,
                 prob_th=0.2,
                 nms_th=0.4,
                 num_classes=1):

        self.inp_dim = inp_dim    #输入图像的维度
        self.prob_th = prob_th
        self.nms_th = nms_th
        '''
   		 nms:即non maximum suppression即非极大抑制,顾名思义就是抑制不是极大值的元素,搜索局部的极大值,因为一张图片中经常会找出很多个可能是物体的矩形框,但是很多矩形框是没有意义的,所以需要nms来剔除
        '''
        self.num_classes = num_classes
        self.dst_dir = dst_dir

        if os.path.exists(self.dst_dir):
            for x in os.listdir(self.dst_dir):
                if x.endswith('.jpg'):
                    os.remove(self.dst_dir + '/' + x)
        else:
            os.makedirs(self.dst_dir)  #创建一个多级目录

        #初始化车辆检测模型
        self.detector = Darknet(car_cfg_path)
        self.detector.load_weights(car_det_weights_path)
        #设置图像的输入维度
        self.detector.net_info['height'] = self.inp_dim
        self.detector.to(device)
        self.detector.eval()  # 评估模型
        print('=> car detection model initiated.')

        # 多标签分类器  通道数19
        self.classifier = Car_Classifier(num_cls=19,
                                         model_path=local_model_path)

        self.imgs_path = [os.path.join(src_dir, x) for x in os.listdir(
            src_dir) if x.endswith('.jpg')]

    def cls_draw_bbox(self, output, orig_img):
        """
        1. 根据车辆的 bbox 预测车辆属性
        2. 绘制bbox至orig_img
        """
        labels = []
        pt_1s = []
        pt_2s = []
#       1.
        for det in output:
            pt_1 = tuple(det[1:3].int())  #矩形框左上角的点
            pt_2 = tuple(det[3:5].int())  #矩形框右下角的点
            pt_1s.append(pt_1)
            pt_2s.append(pt_2)

            #将BGR调回到RGB
            ROI = Image.fromarray(
                orig_img[pt_1[1]: pt_2[1],
                         pt_1[0]: pt_2[0]][:, :, ::-1])
            # 让分类器进行分类
            car_color, car_direction, car_type = self.classifier.predict(ROI)
            label = str(car_color + ' ' + car_direction + ' ' + car_type)
            labels.append(label)
            print('=> predicted label: ', label)
#        2.
        color = (0, 215, 255)
        for i, det in enumerate(output):
            pt_1 = pt_1s[i]
            pt_2 = pt_2s[i]

            #绘制边界框
            cv2.rectangle(orig_img, pt_1, pt_2, color, thickness=2)

            #获取字符串文本大小
            txt_size = cv2.getTextSize(
                label, cv2.FONT_HERSHEY_PLAIN, 2, 2)[0]
            # pt_2 = pt_1[0] + txt_size[0] + 3, pt_1[1] + txt_size[1] + 5
            pt_2 = pt_1[0] + txt_size[0] + 3, pt_1[1] - txt_size[1] - 5

            #绘制文本背景矩形
            cv2.rectangle(orig_img, pt_1, pt_2, color, thickness=-1)  # text

            # 绘制TXT
            cv2.putText(orig_img, labels[i], (pt_1[0], pt_1[1]),  # pt_1[1] + txt_size[1] + 4
                        cv2.FONT_HERSHEY_PLAIN, 2, [225, 255, 255], 2)

    def process_predict(self,
                        prediction,
                        prob_th,
                        num_cls,
                        nms_th,
                        inp_dim,
                        orig_img_size):

        scaling_factor = min([inp_dim / float(x)
                              for x in orig_img_size])  # 高、宽的换算系数
        output = post_process(prediction,
                              prob_th,
                              num_cls,
                              nms=True,
                              nms_conf=nms_th,
                              CUDA=True)

        if type(output) != int:
            output[:, [1, 3]] -= (inp_dim - scaling_factor *
                                  orig_img_size[0]) / 2.0  # x, w
            output[:, [2, 4]] -= (inp_dim - scaling_factor *
                                  orig_img_size[1]) / 2.0  # y, h
            output[:, 1:5] /= scaling_factor
            for i in range(output.shape[0]):
                #将torch.clamp(i,min,max)输入input张量每个元素的夹紧到区间 [min,max][min,max],并返回结果到一个新张量。
                output[i, [1, 3]] = torch.clamp(
                    output[i, [1, 3]], 0.0, orig_img_size[0])
                output[i, [2, 4]] = torch.clamp(
                    output[i, [2, 4]], 0.0, orig_img_size[1])
        return output

    #图像进行检测和识别
    def detect_classify(self):
        for x in self.imgs_path:
            #读取文件数据
            img = Image.open(x)
            img2det = process_img(img, self.inp_dim)
            img2det = img2det.to(device)

            #车辆检测
            prediction = self.detector.forward(img2det, CUDA=True)

            #计算换算系数
            orig_img_size = list(img.size)
            output = self.process_predict(prediction,
                                          self.prob_th,
                                          self.num_classes,
                                          self.nms_th,
                                          self.inp_dim,
                                          orig_img_size)

            orig_img = cv2.cvtColor(np.asarray(
                img), cv2.COLOR_RGB2BGR)  # RGB 转 BGR
            if type(output) != int:
                self.cls_draw_bbox(output, orig_img)
                dst_path = self.dst_dir + '/' + os.path.split(x)[1]
                if not os.path.exists(dst_path):
                    cv2.imwrite(dst_path, orig_img)


parser = argparse.ArgumentParser(description='Detect and classify cars.')
parser.add_argument('-src-dir',
                    type=str,
                    default='./test_imgs',
                    help='source directory of images')
parser.add_argument('-dst-dir',
                    type=str,
                    default='./test_result',
                    help='destination directory of images to store results.')
'''
    argparse 模块可以让人轻松编写用户友好的命令行接口。
    使用 argparse 的第一步是创建一个 ArgumentParser 对象。
    ArgumentParser 对象包含将命令行解析成 Python 数据类型所需的全部信息。
    给一个 ArgumentParser 添加程序参数信息是通过调用 add_argument() 方法完成的。
    解析参数  parser.parse_args()
'''

(5).主函数调用

if __name__ == '__main__':
    args = parser.parse_args()
    DR_model = Car_DC(src_dir=args.src_dir, dst_dir=args.dst_dir)
    DR_model.detect_classify()

友好的命令行接口。
使用 argparse 的第一步是创建一个 ArgumentParser 对象。
ArgumentParser 对象包含将命令行解析成 Python 数据类型所需的全部信息。
给一个 ArgumentParser 添加程序参数信息是通过调用 add_argument() 方法完成的。
解析参数 parser.parse_args()

运行效果如下:
在这里插入图片描述
在这里插入图片描述

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章           查看所有文章
加:2021-09-19 07:59:01  更:2021-09-19 08:01:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 14:45:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码