Python3+OpenCV实现简单交通标志识别流程分析

作者:CCxiao5 时间:2021-03-12 06:37:41 

由于该项目是针对中小学生竞赛并且是第一次举行,所以识别的目标交通标志仅仅只有直行、右转、左转和停车让行。

数据集:

链接: https://pan.baidu.com/s/1SL0qE-qd4cuatmfZeNuK0Q 提取码: vuvi 

源代码:https://github.com/ccxiao5/Traffic_sign_recognition

整体流程如下:

  • 数据集收集(包括训练集和测试集的分类)

  • 图像预处理

  • 图像标注

  • 根据标注分割得到目标图像

  • HOG特征提取

  • 训练得到模型

  • 将模型带入识别算法进行识别

我的数据目录树。其中test_images/train_images是收集得到原始数据集。realTest/realTrain是预处理后的图像。dataTest/dataTrain是经过分类处理得到的图像,HogTest/HogTrain是通过XML标注后裁剪得到的图像。HogTest_affine/HogTrain_affine是经过仿射变换处理扩充的训练集和测试集。imgTest_hog.txt/imgTrain_hog.txt是测试集和训练集的Hog特征

Python3+OpenCV实现简单交通标志识别流程分析

一、图像处理

由于得到的数据集图像大小不一(如下),我们首先从中心区域裁剪并调整正方形图像的大小,然后将处理后的图像保存到realTrain和realTest里面。

Python3+OpenCV实现简单交通标志识别流程分析

图片名称对应关系如下:


img_label = {
"000":"Speed_limit_5",
"001":"Speed_limit_15",
"002":"Speed_limit_30",
"003":"Speed_limit_40",
"004":"Speed_limit_50",
"005":"Speed_limit_60",
"006":"Speed_limit_70",
"007":"Speed_limit_80",
"008":"No straight or right turn",
"009":"No straight or left turn",
"010":"No straight",
"011":"No left turn",
"012":"Do not turn left and right",
"013":"No right turn",
"014":"No Overhead",
"015":"No U-turn",
"016":"No Motor vehicle",
"017":"No whistle",
"018":"Unrestricted speed_40",
"019":"Unrestricted speed_50",
"020":"Straight or turn right",
"021":"Straight",
"022":"Turn left",
"023":"Turn left or turn right",
"024":"Turn right",
"025":"Drive on the left side of the road",
"026":"Drive on the right side of the road",
"027":"Driving around the island",
"028":"Motor vehicle driving",
"029":"Whistle",
"030":"Non-motorized",
"031":"U-turn",
"032":"Left-right detour",
"033":"traffic light",
"034":"Drive cautiously",
"035":"Caution Pedestrians",
"036":"Attention non-motor vehicle",
"037":"Mind the children",
"038":"Sharp turn to the right",
"039":"Sharp turn to the left",
"040":"Downhill steep slope",
"041":"Uphill steep slope",
"042":"Go slow",
"044":"Right T-shaped cross",
"043":"Left T-shaped cross",
"045":"village",
"046":"Reverse detour",
"047":"Railway crossing-1",
"048":"construction",
"049":"Continuous detour",
"050":"Railway crossing-2",
"051":"Accident-prone road section",
"052":"stop",
"053":"No passing",
"054":"No Parking",
"055":"No entry",
"056":"Deceleration and concession",
"057":"Stop For Check"
}

def center_crop(img_array, crop_size=-1, resize=-1, write_path=None):
   ##从中心区域裁剪并调整正方形图像的大小。
   rows = img_array.shape[0]
   cols = img_array.shape[1]

if crop_size==-1 or crop_size>max(rows,cols):
       crop_size = min(rows, cols)
   row_s = max(int((rows-crop_size)/2), 0)
   row_e = min(row_s+crop_size, rows)
   col_s = max(int((cols-crop_size)/2), 0)
   col_e = min(col_s+crop_size, cols)

img_crop = img_array[row_s:row_e,col_s:col_e,]

if resize>0:
       img_crop = cv2.resize(img_crop, (resize, resize))

if write_path is not None:
       cv2.imwrite(write_path, img_crop)
   return img_crop

然后根据得到的realTrain和realTest自动生成带有<size><width><height><depth><filename>的xml文件


def write_img_to_xml(imgfile, xmlfile):

img = cv2.imread(imgfile)
   img_folder, img_name = os.path.split(imgfile)
   img_height, img_width, img_depth = img.shape
   doc = Document()

annotation = doc.createElement("annotation")
   doc.appendChild(annotation)

folder = doc.createElement("folder")
   folder.appendChild(doc.createTextNode(img_folder))
   annotation.appendChild(folder)

filename = doc.createElement("filename")
   filename.appendChild(doc.createTextNode(img_name))
   annotation.appendChild(filename)

size = doc.createElement("size")
   annotation.appendChild(size)

width = doc.createElement("width")
   width.appendChild(doc.createTextNode(str(img_width)))
   size.appendChild(width)

height = doc.createElement("height")
   height.appendChild(doc.createTextNode(str(img_height)))
   size.appendChild(height)

depth = doc.createElement("depth")
   depth.appendChild(doc.createTextNode(str(img_depth)))
   size.appendChild(depth)

with open(xmlfile, "w") as f:
       doc.writexml(f, indent="\t", addindent="\t", newl="\n", encoding="utf-8")

<annotation>
<folder>/home/xiao5/Desktop/Test2/data/realTest/PNGImages</folder>
<filename>000_1_0001_1_j.png</filename>
<size>
<width>640</width>
<height>640</height>
<depth>3</depth>
</size>
</annotation>

然后对realTrain和realTest的图片进行标注,向默认XML添加新的信息(矩形信息)。


<annotation>
<folder>PNGImages</folder>
<filename>021_1_0001_1_j.png</filename>
<path>
C:\Users\xiao5\Desktop\realTest\PNGImages\021_1_0001_1_j.png
</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>640</width>
<height>640</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>Straight</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>13</xmin>
<ymin>22</ymin>
<xmax>573</xmax>
<ymax>580</ymax>
</bndbox>
</object>
</annotation>

处理完后利用我们添加的矩形将图片裁剪下来并且重命名进行分类。主要思路是:解析XML文档,根据<name>标签进行分类,如果是直行、右转、左转、停止,那么就把它从原图中裁剪下来并重命名,如果没有<object>那么就认为是负样本,其中在处理负样本的时候,我进行了颜色识别,把一张负样本图片根据颜色(红色、蓝色)裁剪成几张负样本,这样做的好处是:我们在进行交通标志的识别时,也是使用的颜色识别来选取到交通标志,我们从负样本中分割出来的相近颜色样本有利于负样本的训练,提高模型精度。


def produce_proposals(xml_dir, write_dir, square=False, min_size=30):
               ##返回proposal_num对象
   proposal_num = {}
   for cls_name in classes_name:
       proposal_num[cls_name] = 0

index = 0
   for xml_file in os.listdir(xml_dir):
       img_path, labels = parse_xml(os.path.join(xml_dir,xml_file))
       img = cv2.imread(img_path)
       ##如果图片中没有出现定义的那几种交通标志就把它当成负样本
       if len(labels) == 0:
           neg_proposal_num = produce_neg_proposals(img_path, write_dir, min_size, square, proposal_num["background"])
           proposal_num["background"] = neg_proposal_num
       else:
           proposal_num = produce_pos_proposals(img_path, write_dir, labels, min_size, square=True, proposal_num=proposal_num)

if index%100 == 0:
           print ("total xml file number = ", len(os.listdir(xml_dir)), "current xml file number = ", index)
           print ("proposal num = ", proposal_num)
       index += 1

return proposal_num

Python3+OpenCV实现简单交通标志识别流程分析

为了提高模型的精确度,还对目标图片(四类图片)进行仿射变换来扩充训练集。


def affine(img, delta_pix):
   rows, cols, _ = img.shape
   pts1 = np.float32([[0,0], [rows,0], [0, cols]])
   pts2 = pts1 + delta_pix
   M = cv2.getAffineTransform(pts1, pts2)
   res = cv2.warpAffine(img, M, (rows, cols))
   return res

def affine_dir(img_dir, write_dir, max_delta_pix):
   img_names = os.listdir(img_dir)
   img_names = [img_name for img_name in img_names if img_name.split(".")[-1]=="png"]
   for index, img_name in enumerate(img_names):
       img = cv2.imread(os.path.join(img_dir,img_name))
       save_name = os.path.join(write_dir, img_name.split(".")[0]+"f.png")
       delta_pix = np.float32(np.random.randint(-max_delta_pix,max_delta_pix+1,[3,2]))
       img_a = affine(img, delta_pix)
       cv2.imwrite(save_name, img_a)

Python3+OpenCV实现简单交通标志识别流程分析

二、HOG特征提取

处理好图片后分别对训练集和测试集进行特征提取得到imgTest_HOG.txt和imgTrain_HOG.txt


def hog_feature(img_array, resize=(64,64)):
   ##提取HOG特征

img = cv2.cvtColor(img_array, cv2.COLOR_BGR2GRAY)
   img = cv2.resize(img, resize)
   bins = 9
   cell_size = (8, 8)
   cpb = (2, 2)
   norm = "L2"
   features = ft.hog(img, orientations=bins, pixels_per_cell=cell_size,
                       cells_per_block=cpb, block_norm=norm, transform_sqrt=True)
   return features

def extra_hog_features_dir(img_dir, write_txt, resize=(64,64)):
   ##提取目录中所有图像HOG特征

img_names = os.listdir(img_dir)
   img_names = [os.path.join(img_dir, img_name) for img_name in img_names]
   if os.path.exists(write_txt):
       os.remove(write_txt)

with open(write_txt, "a") as f:
       index = 0
       for img_name in img_names:
           img_array = cv2.imread(img_name)
           features = hog_feature(img_array, resize)
           label_name = img_name.split("/")[-1].split("_")[0]
           label_num = img_label[label_name]
           row_data = img_name + "\t" + str(label_num) + "\t"

for element in features:
               row_data = row_data + str(round(element,3)) + " "
           row_data = row_data + "\n"
           f.write(row_data)

if index%100 == 0:
               print ("total image number = ", len(img_names), "current image number = ", index)
           index += 1

三、模型训练

利用得到的HOG特征进行训练模型得到svm_model.pkl


def load_hog_data(hog_txt):

img_names = []
   labels = []
   hog_features = []
   with open(hog_txt, "r") as f:
       data = f.readlines()
       for row_data in data:
           row_data = row_data.rstrip()
           img_path, label, hog_str = row_data.split("\t")
           img_name = img_path.split("/")[-1]
           hog_feature = hog_str.split(" ")
           hog_feature = [float(hog) for hog in hog_feature]
           #print "hog feature length = ", len(hog_feature)
           img_names.append(img_name)
           labels.append(label)
           hog_features.append(hog_feature)
   return img_names, np.array(labels), np.array(hog_features)

def svm_train(hog_features, labels, save_path="./svm_model.pkl"):

clf = SVC(C=10, tol=1e-3, probability = True)
   clf.fit(hog_features, labels)
   joblib.dump(clf, save_path)
   print ("finished.")

四、交通标志识别及实验测试

交通标志识别的流程:颜色识别得到阈值范围内的二值图、然后进行轮廓识别、剔除多余矩阵。


def preprocess_img(imgBGR):
       ##将图像由RGB模型转化成HSV模型
   imgHSV = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2HSV)
   Bmin = np.array([110, 43, 46])
   Bmax = np.array([124, 255, 255])
       ##使用inrange(HSV,lower,upper)设置阈值去除背景颜色
   img_Bbin = cv2.inRange(imgHSV,Bmin, Bmax)
   Rmin2 = np.array([165, 43, 46])
   Rmax2 = np.array([180, 255, 255])
   img_Rbin = cv2.inRange(imgHSV,Rmin2, Rmax2)
   img_bin = np.maximum(img_Bbin, img_Rbin)
   return img_bin

'''
提取轮廓,返回轮廓矩形框
'''
def contour_detect(img_bin, min_area=0, max_area=-1, wh_ratio=2.0):
   rects = []
   ##检测轮廓,其中cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_NONE 存储所有的边界点
   ##findContours返回三个值:第一个值返回img,第二个值返回轮廓信息,第三个返回相应轮廓的关系
   contours, hierarchy= cv2.findContours(img_bin.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
   if len(contours) == 0:
       return rects
   max_area = img_bin.shape[0]*img_bin.shape[1] if max_area<0 else max_area
   for contour in contours:
       area = cv2.contourArea(contour)
       if area >= min_area and area <= max_area:
           x, y, w, h = cv2.boundingRect(contour)
           if 1.0*w/h < wh_ratio and 1.0*h/w < wh_ratio:
               rects.append([x,y,w,h])
   return rects

然后加载模型进行测验


if __name__ == "__main__":
   cap = cv2.VideoCapture(0)
   cv2.namedWindow('camera')
   cv2.resizeWindow("camera",640,480)
   cols = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
   rows = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
   clf = joblib.load("/home/xiao5/Desktop/Test2/svm_model.pkl")
   i=0
   while (1):
       i+=1
       ret, img = cap.read()
       img_bin = preprocess_img(img)
       min_area = img_bin.shape[0]*img.shape[1]/(25*25)
       rects = contour_detect(img_bin, min_area=min_area)
       if rects:
           Max_X=0
           Max_Y=0
           Max_W=0
           Max_H=0
           for r in rects:
               if r[2]*r[3]>=Max_W*Max_H:
                   Max_X,Max_Y,Max_W,Max_H=r
           proposal = img[Max_Y:(Max_Y+Max_H),Max_X:(Max_X+Max_W)]##用Numpy数组对图像像素进行访问时,应该先写图像高度所对应的坐标(y,row),再写图像宽度对应的坐标(x,col)。
           cv2.rectangle(img,(Max_X,Max_Y), (Max_X+Max_W,Max_Y+Max_H), (0,255,0), 2)
           cv2.imshow("proposal", proposal)
           cls_prop = hog_extra_and_svm_class(proposal, clf)
           cls_prop = np.round(cls_prop, 2)
           cls_num = np.argmax(cls_prop)##找到最大相似度的索引
           if cls_names[cls_num] is not "background":
               print(cls_names[cls_num])
           else:
               print("N/A")
       cv2.imshow('camera',img)
       cv2.waitKey(40)
   cv2.destroyAllWindows()
   cap.release()

Python3+OpenCV实现简单交通标志识别流程分析

来源:https://www.cnblogs.com/cc-xiao5/p/11316283.html

标签:Python3,OpenCV,交通标志,识别
0
投稿

猜你喜欢

  • asp清理缓存的代码

    2011-03-09 11:16:00
  • 使用Python对Csv文件操作实例代码

    2023-04-17 23:50:57
  • SQL Server 置疑、可疑、正在恢复等情况分析

    2012-01-05 18:51:59
  • Python pickle模块常用方法代码实例

    2023-12-25 03:09:16
  • python3实现163邮箱SMTP发送邮件

    2021-02-28 07:59:19
  • mysql int范围与最大值分析

    2012-03-26 18:24:00
  • 判断python字典中key是否存在的两种方法

    2023-08-19 00:18:05
  • 在python中将字符串转为json对象并取值的方法

    2022-12-19 02:22:37
  • python采集博客中上传的QQ截图文件

    2021-03-03 16:45:25
  • ASP中数据库调用中常见错误的现象和解决

    2007-09-20 13:24:00
  • Python绘制地理图表可视化神器pyecharts

    2021-01-22 18:08:58
  • 不能使用“;文件已在使用中 Microsoft JET Database Engine

    2012-12-04 20:34:36
  • python之json文件转xml文件案例讲解

    2021-11-18 04:54:23
  • sklearn+python:线性回归案例

    2023-10-19 20:07:01
  • Python2.x版本中maketrans()方法的使用介绍

    2021-02-14 21:04:25
  • Microsoft Enterprise Library 5.0 如何集成MyS

    2011-03-16 15:19:00
  • python实现简单俄罗斯方块

    2022-10-21 07:37:27
  • 基于OpenCV的网络实时视频流传输的实现

    2022-10-18 03:03:57
  • Python OpenCV图像指定区域裁剪的实现

    2021-12-30 01:41:35
  • openCV提取图像中的矩形区域

    2022-06-11 06:38:55
  • asp之家 网络编程 m.aspxhome.com