机器学习与图像识别(一)—— 图像预处理

   此文章为笔者近期学习机器学习与图像处理过程中的一些经验总结和重要的知识点梳理,文章持续更新。本文主要讲述对图像进行识别之前一般要完成的预处理工作······

  1. 图像基础知识简述
       按照笔者的理解,图像一般就是一个三维数组,这个数组的长和宽代表着图像的长和宽(长和宽分别表示横向、纵向像素点的个数),也就是图像的大小(size),比如说128128height的图表示横向、纵向各有128个像素点。数组的高根据图像的格式不同其值也不同,如果在计算机中其高占2bit,我们把这样的图像称为二值图像(所谓二值图像即是非黑即白,只有两种颜色);如果高占8bit,我们把这样的图像称为灰度图像,高的值(即value)取0~255,0表示纯黑,255表示纯白,127表示纯灰色,其余值按比例往0,255两端趋近;如果其高为24bit,一般称此图为RGB模型(Red,Green,Blue)的图像,三种底色按不同的比例可以调和出各种各样的颜色。当然,除了二值图、灰度图和RGB图像,还有基于其他数学模型的图像比如用的同样比较多的HSV模型(Hue,Saturation,Value)。各个模型之间可以互相转换,下面的预处理如果用到我们会再解释。
  2. 图像平移和大小放缩
       图像的平移是为了把我们感兴趣的、图中的对象移到图像的中间位置,便于尽可能准确地、定量地分析图像的特征。图像的平移有多种思路:
    1)、切割法,直接通过数组操作,把感兴趣的区域(即兴趣域)切出来保存,剩余的就舍弃,用python的numpy库举例如下:
import numpy as np
import cv2 as cv
#利用numpy库随机生成一个120✖120的RGB彩图
exm = np.random.randint(0,255,size = [120,120,3],dtype = np.uint8)
roi = exm[0:32,12:66,:] #切割出0~32,12~66的区域
#显示出原图和兴趣域
cv.imshow("Original",exm)
cv.imshow("ROI",roi)

   切割法适用于我们已知兴趣域大致位置的情况,该法实现简单但适用性不广,下面再介绍一种思路,即前景提取法。
2)、前景提取法,我们根据兴趣域与非兴趣域之间最典型的区别(比如色彩、纹理、轮廓、边缘特征等)获取目标区域,下面以提取图中香蕉皮为例简单说明:

import numpy as np
import cv2 as cv
banana = cv.imread("banana.jpg") #读取原图
Hsv_bnn = cv.cvtColor(banana,cv.COLOR_BGR2HSV) #将RGB色彩空间转换为HSV色彩空间
minYe = np.array([20, 5, 5]) #设定色彩上下限,主要是黄色的色彩范围
maxYe = np.array([44,250,255])
msk = cv.inRange(Hsv_bnn,minYe,maxYe) #获取满足条件的掩模
bnn = cv.bitwise_and(banana,banana,mask = msk)
cv.imshow("BNN",bnn) #显示

   上面牵涉到了色彩空间转换,HSV色彩空间请点击这里查看详述。之所以将RGB转换为HSV色彩模型,是因为香蕉皮的色彩的Hue值与背景图的Hue值相差较大,可以将二者分离开。
   至于图像的放缩,我们知道一个数组越大,计算机处理该数组时消耗的内存越大、花费的时间越长(这里牵涉到评估一种算法的指标,即时间复杂度和空间复杂度)。图像作为一个很大的数组,如果不对其大小进行一定的处理,将会导致识别算法的效率非常低,所以,有必要在预处理时对图像大小进行调整。一般来说,我们根据待识别对象的最低分辨率(所谓最低分辨率就是指在某个显示器上能分辨出图像内容的最少的像素点的个数)尽量选择小的图像。比如32✖32(比如识别手写的数字)、128✖128(较低精度)、256✖256(较高精度的图像)等。一般512✖512的图像就很大了,如果不是特殊的要求最好不要超过该值。
下面给出基于opencv-python的实现图像放缩的代码:

pic_len = 512
for i in range(10):
    a = cv.imread("bottle-"+str(i)+".jpg") #读取图像
    l,w,h = a.shape #获取图像的属性
    #print(str(i)+"= ",a.shape)
    #cv.imshow("Original "+str(i),a)
    if l >= w:  #初步放缩,以防图像大但兴趣域小的情况出现
        size = (round((pic_len/l)*w),pic_len)
    else:
        size = (pic_len,round((pic_len/w)*l))
    a = cv.resize(a,size)
    l,w,h = a.shape
    #print("after "+str(i)+'= ',a.shape)
    #cv.imshow("After "+str(i),a)
   
   #将图片调整为方形的图片
    s = np.ones((pic_len,pic_len,3),dtype = np.uint8)*255
    if l <= pic_len and w <=pic_len:
        s[((pic_len-l)//2):(l+(pic_len-l)//2),((pic_len-w)//2):(w+(pic_len-w)//2),:] = a[:,:,:]
    elif l > pic_len and w <=pic_len:
        s[:,((pic_len-w)//2):(w+(pic_len-w)//2),:] = a[((l-pic_len)//2):(pic_len+(l-pic_len)//2),:,:]
    elif l <= pic_len and w > pic_len:
        s[((pic_len-l)//2):(l+(pic_len-l)//2),:,:] = a[:,((w-pic_len)//2):(pic_len+(w-pic_len)//2),:]
    elif l >pic_len and w > pic_len:
        s[:,:,:] = a[((l-pic_len)//2):(pic_len+(l-pic_len)//2),((w-pic_len)//2):(pic_len+(w-pic_len)//2),:]
    #cv.imshow("s"+str(i),s)
    #cv.imwrite("bana-"+str(i)+".jpg",s)


#第二次放缩,得到最终的图片
    size = (256,256)
    c = cv.resize(s,size)
    cv.imshow(str(i),c)
   # cv.imwrite("bo-"+str(i)+".jpg",c) #256*256大小的图片

   上面的代码是批量处理同名带编号的一类图片的大小,先将图片略放大至再缩小以便得到大小相似的居于图片中间的图片。

   上面所述的图像预处理只是最基础的图像处理操作,除此之外还有形态学操作、阈值处理、图像滤波、直方图处理、轮廓处理、边缘检测等,更多基于opencv的图像处理将会首先更新在公众号“24K纯学渣”上面,欢迎大家学习交流!上述如有不妥或纰漏之处也欢迎大家评论指出!

发布了25 篇原创文章 · 获赞 9 · 访问量 6191

猜你喜欢

转载自blog.csdn.net/qq_42144047/article/details/104491366