|
车牌照图片识别预处理(核心技巧):
1、图像重置大小 变成正方形 并转换为单通道的黑白图片
2、通过卷积操作 对轮廓信息提取
3、二值化操作 让轮廓信息像素变成纯白 其他位置纯黑
4、最大池化操作(多次) 让白色区域扩大或膨胀(将这些白色区域的周边也变成白色的,连成一片)
5、将有一些膨胀不太好的地方,还原成黑色 乘以-1之后的最大池化
6、继续最大池化操作(多次) 继续膨胀
- # -- encoding:utf-8 --
- """
- 车牌照图片识别预处理(核心技巧):
- 1、图像重置大小 变成正方形 并转换为单通道的黑白图片
- 2、通过卷积操作 对轮廓信息提取
- 3、二值化操作 让轮廓信息像素变成纯白 其他位置纯黑
- 4、最大池化操作(多次) 让白色区域扩大或膨胀(将这些白色区域的周边也变成白色的,连成一片)
- 5、将有一些膨胀不太好的地方,还原成黑色 乘以-1之后的最大池化
- 6、继续最大池化操作(多次) 继续膨胀
- """
- import matplotlib.pyplot as plt
- import tensorflow as tf
- def show_image_tensor(image_tensor):
- # 要求:使用交互式会话
- # 获取图像tensor对象对应的image对象,image对象时一个[h,w,c]
- # print(image_tensor)
- image = image_tensor.eval()
- # print(image)
- print("图像大小为:{}".format(image.shape))
- if len(image.shape) == 3 and image.shape[2] == 1:
- # 黑白图像
- plt.imshow(image[:, :, 0], cmap='Greys_r')
- plt.show()
- elif len(image.shape) == 3:
- # 彩色图像
- plt.imshow(image)
- plt.show()
- sess = tf.InteractiveSession()
- path = '0.jpg'
- # 读取图像数据并转换为[height, width, channels]的格式
- img = tf.image.decode_jpeg(tf.read_file(path), channels=3)
- # # 图像重置大小
- img = tf.image.resize_images(img, size=(300, 300), method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
- # # 将图像转换为黑白的图像
- img = tf.image.rgb_to_grayscale(img)
- # old_img = img
- # 边缘/轮廓信息提取(卷积操作)
- img = tf.cast(img, dtype=tf.float32)
- # (300, 300, 1) --->变成 (1, 300, 300, 1) [NHWC]
- img = tf.expand_dims(img, 0)
- for i in range(1):
- sobel_gx = [-1, 0, +1, 0, 0, 0, -1, 0, +1]
- # [filter_height, filter_width, in_channels, out_channels]
- filter = tf.constant(value=sobel_gx, dtype=tf.float32, shape=[3, 3, 1, 1])
- img = tf.nn.conv2d(img, filter=filter, strides=[1, 1, 1, 1], padding='SAME', data_format='NHWC')
- img = img[0]
- #
- # 二值化,小于等于170的设置为0,大于170设置为255
- # condition? True : False
- img = tf.where(tf.less_equal(img, 170), tf.zeros_like(img), tf.ones_like(img) * 255)
- #
- # 将白色的区域(车牌区域就是白色的),扩大/膨胀 ==> 将这些白色区域的周边也变成白色的 ==> 只需要做一个maxpool
- # 变成 (1, 300, 300, 1) [NHWC]
- img = tf.expand_dims(img, 0)
- # 膨胀
- for i in range(2):
- # 池化核大小: 2*3
- img = tf.nn.max_pool(img, ksize=(1, 2, 3, 1), strides=(1, 1, 1, 1), padding='SAME', data_format="NHWC")
- # # 将有一些膨胀不太好的地方,还原成黑色
- img = tf.nn.max_pool(img * -1, ksize=(1, 3, 1, 1), strides=(1, 1, 1, 1), padding='SAME', data_format="NHWC")
- img = img * -1
- #
- # # 继续膨胀一次
- for i in range(5):
- img = tf.nn.max_pool(img, ksize=(1, 1, 5, 1), strides=(1, 1, 1, 1), padding='SAME', data_format="NHWC")
- # 维度还原:(300, 300, 1)
- img = img[0]
- print(img)
- print(img.get_shape())
- show_image_tensor(img)
复制代码
- # -- encoding:utf-8 --
- """
- 提取车牌号(使用OpenCV的方式)
- OpenCV 图像处理 封装得非常好
- 下载opencv:https://www.lfd.uci.edu/~gohlke/pythonlibs/
- 通过whl文件安装 比较好
- opencv_python-4.0.1-cp36-cp36m-win_amd64.whl
- pip install wheel
- pip install xxx.whl
- opencv-python 4.0.1
- """
- import cv2
- import numpy as np
- def preprocess(gray):
- """
- 对灰度对象进行形态转换(预处理)
- :param gray:
- :return:
- """
- # 高斯平滑
- gaussian = cv2.GaussianBlur(gray, (3, 3), 0, 0, cv2.BORDER_DEFAULT)
- # 中值滤波
- median = cv2.medianBlur(gaussian, 5)
- # Sobel算子,对边缘进行处理(获取边缘信息,其实就是卷积过程 3*3)
- # x:[-1, 0, +1, -2, 0, +2, -1, 0, +1]
- # y:[-1, -2, -1, 0, 0, 0, +1, +2, +1]
- sobel = cv2.Sobel(median, cv2.CV_8U, 1, 0, ksize=3)
- # 二值化 170作为分界值 0或255
- ret, binary = cv2.threshold(sobel, 170, 255, cv2.THRESH_BINARY)
- # 膨胀&腐蚀
- element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
- element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 7))
- # 膨胀一次,让轮廓突出
- dilation = cv2.dilate(binary, element2, iterations=1)
- # 腐蚀一次,去掉细节
- erosion = cv2.erode(dilation, element1, iterations=1)
- # 再次膨胀,让轮廓明显一些
- dilation2 = cv2.dilate(erosion, element2, iterations=3)
- # cv2.imshow('dfy', dilation2)
- # cv2.waitKey(0)
- return dilation2
- def find_plate_number_region(img):
- """
- 寻找可能是车牌区域的轮廓
- :param img:
- :return:
- """
- # 查找轮廓(img: 原始图像,contours:矩形坐标点,hierarchy:图像层次)
- # contours, hierarchy
- # img, contours, hierarchy = cv2.findContours(img, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
- contours, hierarchy = cv2.findContours(img, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
- # 查找矩形
- max_ratio = -1
- max_box = None
- ratios = []
- number = 0
- for i in range(len(contours)):
- cnt = contours[i]
- # 计算轮廓面积
- area = cv2.contourArea(cnt)
- # 面积太小的过滤掉 1000是阈值 自己给定
- if area < 1000:
- continue
- # 找到最小的矩形
- rect = cv2.minAreaRect(cnt)
- # 矩形的四个坐标(顺序不定,但是一定是一个左下角、左上角、右上角、右下角这种循环顺序(开始是哪个点未知))
- box = cv2.boxPoints(rect)
- # 转换为long类型
- box = np.int0(box)
- # 计算长宽高
- height = abs(box[0][1] - box[2][1])
- weight = abs(box[0][0] - box[2][0])
- ratio = float(weight) / float(height)
- # 正常的车牌宽高比在2.7~5之间
- if ratio > max_ratio:
- max_box = box
- if ratio > 5.5 or ratio < 2:
- continue
- # 将结果添加到序列中
- number += 1
- ratios.append((box, ratio))
- # 根据找到的图像矩阵数量进行数据输出
- if number == 1:
- # 直接返回
- return ratios[0][0]
- elif number > 1:
- # 有多个满足条件的
- # 如果存在多个,这里不做太多考虑,直接返回一个最可能的(也就是ratio在2.7~5之间的中间数字的那个图像)
- # TODO: 实际上可以在这里训练一个模型,用于判断图片中是否有车牌照(就一个简单的神经网络即可)
- filter_ratios = list(filter(lambda t: t[1] >= 2.7 and t[1] <= 5, ratios))
- size_filter_ratios = len(filter_ratios)
- if size_filter_ratios == 1:
- return filter_ratios[0][1]
- elif size_filter_ratios > 1:
- r = [filter_ratios[i][1] for i in range(size_filter_ratios)]
- sort_r = np.argsort(r)
- return filter_ratios[int(len(sort_r) / 2)][0]
- else:
- # 直接返回最大值
- max_index = np.argmax(ratios, 0)
- return ratios[max_index[1]][1]
- else:
- # 直接返回最大值
- return max_box
- def cut(img_or_img_path):
- """
- 截取车牌区域
- :param img:
- :return:
- """
- if isinstance(img_or_img_path, str):
- img = cv2.imread(img_or_img_path)
- else:
- img = img_or_img_path
- # 将图像转换为灰度图
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- # 图像预处理
- dilation = preprocess(gray)
- # 查找车牌区域(只会有一个)
- box = find_plate_number_region(dilation)
- # 返回区域对应的图像
- # 因为不知道,点的顺序,所以对左边点坐标排序
- ys = [box[0, 1], box[1, 1], box[2, 1], box[3, 1]]
- xs = [box[0, 0], box[1, 0], box[2, 0], box[3, 0]]
- ys_sorted_index = np.argsort(ys)
- xs_sorted_index = np.argsort(xs)
- # 获取x上的坐标
- x1 = box[xs_sorted_index[0], 0]
- x2 = box[xs_sorted_index[3], 0]
- # 获取y上的坐标
- y1 = box[ys_sorted_index[0], 1]
- y2 = box[ys_sorted_index[3], 1]
- # 截取图像
- img_plate = img[y1:y2, x1:x2]
- return img_plate
- if __name__ == '__main__':
- path = '0.jpg'
- cut_img = cut(path)
- print(cut_img.shape)
- cv2.imwrite('test.jpg', cut_img)
- cv2.imshow('wx:dfy_88888', cut_img)
- cv2.waitKey(0)
复制代码
|
|