编程实战(4)——python识别图像中的坐标点并保存坐标数据

文章目录

  • 编程实战(4)——python识别图像中的坐标点并保存坐标数据
      • 综述
      • 代码思路
        • 库的安装
        • 图片预处理
          • 图像细化
          • 图像二极化
        • 提取数据
        • 结果展示和保存
          • matplotlib重绘
          • 写入excel
      • 全部源码

综述

最近因为美赛的需求,需要在提取一些赛道的路线图和地形图中的准确数据,因此对这方面做了一些了解。在研究的过程中,我发现网上的很多相关的帖子并不是很靠谱,不是报错就是没有说清楚一些函数的功能,所以我打算写一篇比较详细的文章。

本文主要讲述利用python接口的opencv来完成图像识别和信息提取并重新绘制、保存为excel数据的详细过程与思路,适合opencv方面的小白观看(需要一定的numpy和matplotlib基础)。如有一些疏漏,请大佬们指出~

代码思路

我会跟着我代码的思路逐一讲解每一步的思路和函数的一些解释;

总体思路如下:

  • 第一:图片预处理,让图像二极化;
  • 第二:提取图片数据
  • 第三步:数据整理与保存

库的安装

这里一共用了三个库

import cv2import numpy as npimport matplotlib.pyplot as plt

numpy和matplotlib的安装都比较常规,但是cv2的安装不是常规的pip install cv2,是opencv-python,国内镜像下载地址:

pip3 install -i https://mirrors.aliyun.com/pypi/simple/ opencv-python

图片预处理

img = cv2.imread('你的图片路径')hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

首先打开图片,然后对图片做一个hsv灰度图的处理,因为我们常规的图片像素点都是rbg空间模式的,我们需要先转化为hsv颜色空间模式(没见过这个图像格式的可以百度一下),以便后面的图像二值化处理;

上面是我用到的图像,这是东京奥运会公路自行车比赛的路线图,我们要做的就是提取路线像素并坐标化;

图像细化

如果说要处理的图像的线条很粗,那就会影响后面的识别过程,需要先进行图像细化;如果原本图的线条就很细(比如我的),那就可以跳过这一步。

图像细化我是直接参考一个博客的代码的,这里做一个引用:图像细化,骨架提取

写的相当棒,大家可以参考一下。

图像二极化

这里我用到了一个cv库中的inRange函数,这个函数的功能是对读入的图像文件(即函数第一个参数)做一个二值化处理。

总的来说就是我们需要规定两个阈值lowerb和upperb,大于upperb和小于lowerb的图像像素点均会被转化为0(即黑色),在这个范围内的点被转化为255(白色);

low_hsv = np.array([0, 0, 221])high_hsv = np.array([180, 30, 255])mask = cv2.inRange(hsv, lowerb=low_hsv, upperb=high_hsv)

我们再来详细的讲一下这两个阈值,如果了解hsv颜色空间的话,很容易可以发现代码的前两行就是hsv颜色空间的两个值,numpy.array函数里面的参数刚好就是色调、饱和度和明度值,因为inRange函数的需要,我们要转化成numpy格式的数组。我这里需要提取所有黑色的坐标,所以设的是黑色与白色的阈值,大家可以根据自己的需求调整颜色阈值。

阈值参考:HSV基本颜色分量范围

这里的mask,就是我们需要提取数据的图像了~

提取数据

为了帮助大家理解,我们先打印看一下mask的具体情况:

print(len(mask))print(len(mask[0]))for i in range(len(mask)):    print(mask[i])

输出:

6541024[255 255 255 ... 255 255 255][255 255 255 ... 255 255 255]...[255 255 255 ... 255 255 255][255 255 255 ... 255 255 255][255 255 255 ... 255 255 255]

可以看到图像是1024*654的,还有每一行的像素点都是一个numpy数组(numpy数组打印出来元素之间是没有用逗号间隔的),之前24位的hsv图,变成了8位的灰度图(每一项只有一个数据,而不是三个数组成的array);

我们要做的是要知道每一个黑色的像素点的横纵坐标,即在mask这个像素矩阵中,获取值为0的行号和列号

方法也很简单,直接遍历每一个像素,找到值为0的像素点,存取列号和行号即可;

list_y = []list_x = []for i in range(len(mask)):    #print(mask[i])    xmax = []    for j in range(len(mask[i])):        if mask[i][j] == 0:            #print(mask[i][j],j,i)            list_x.append(j)            list_y.append(len(mask)-i)

这里需要注意,很多图像存储数据是从下往上存储的,所以我们在获取列号的时候,需要用图像的高度减去mask的列号,才是真正的列号。

结果展示和保存

matplotlib重绘

检验一下我们获取的图像数据,注意这里需要用散点图模式绘图,不然会有不太好的后果。。。

plt.plot(list_x, list_y, 'o', color='r')plt.show()

结果如下:

可以发现提取效果还是不错的~~

写入excel

这一部分的说明直接放到注释里面了

import xlwtwb = xlwt.Workbook()ws = wb.add_sheet('sheet1') # 添加一个表ws.write(0, 0, "x") # 写入数据,3个参数分别为行号,列号,和内容ws.write(0, 1, "y")i = 1 #指针,每写一个数据,向下移动写指针一行for x in list_x:    ws.write(i, 0, x)    i += 1j = 1for y in list_y:    ws.write(j, 1, y)    j += 1wb.save('1111.xls')

可以看到数据已经保存进excel里面了

这一次提取其实还有一个小问题,就是相同的横坐标下还是有多个对应该横坐标的点,如果需要做函数分析之类的操作,我们可以直接用excel作按值分组然后去每组的特定值即可,最大值,平均值均可,看个人需求。

全部源码

import cv2import numpy as npimport matplotlib.pyplot as pltimport xlwtimg = cv2.imread('3.jpg')hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)low_hsv = np.array([0, 0, 221])high_hsv = np.array([180, 30, 255])mask = cv2.inRange(hsv, lowerb=low_hsv, upperb=high_hsv)print(len(mask))print(len(mask[0]))list_y = []list_x = []for i in range(len(mask)):    print(mask[i])    xmax = []    for j in range(len(mask[i])):        if mask[i][j] == 0:            print(mask[i][j],j,i)            list_x.append(j)            list_y.append(len(mask)-i)plt.plot(list_x, list_y, 'o', color='r')plt.show()wb = xlwt.Workbook()ws = wb.add_sheet('sheet1')ws.write(0, 0, "x")ws.write(0, 1, "y")i = 1for x in list_x:    ws.write(i, 0, x)    i += 1j = 1for y in list_y:    ws.write(j, 1, y)    j += 1wb.save('1111.xls')