数字图像与机器视觉基础

一、位图文件分析

1、基本概念

位图图像(bitmap),亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的。这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块。扩大位图尺寸的效果是增大单个像素,从而使线条和形状显得参差不齐。然而,如果从稍远的位置观看它,位图图像的颜色和形状又显得是连续的。用数码相机拍摄的照片、扫描仪扫描的图片以及计算机截屏图等都属于位图。位图的特点是可以表现色彩的变化和颜色的细微过渡,产生逼真的效果,缺点是在保存时需要记录每一个像素的位置和颜色值,占用较大的存储空间。

2、BMP位图文件

常见的图像文件格式有:BMP、JPG(JPE,JPEG)、GIF等。
  BMP图像文件(Bitmap-File)格式是Windows采用的图像文件存储格式,在Windows环境下运行的所有图像处理软件都支持这种格式。BMP位图文件默认的文件扩展名是.BMP,有时它也会以.DIB或.RLE作扩展名。
本文采用ywl.bmp(2560 x 1440)示例:
数字图像与机器视觉基础_第1张图片

3、BMP文件结构

BMP文件由4部分组成:

① 位图文件头(bitmap-file header)
② 位图信息头(bitmap-informationheader)
③ 颜色表(color table)
④ 颜色点阵数据(bits data)

4、位图文件头

位图文件头分4部分,共14字节:

bfType:2字节,作为标识,就是“BM”二字。
bfSize:4字节,整个BMP文件的大小。
bfReserved1/2:4字节,保留字,没用。
bfOffBits:4字节,偏移数,即 位图文件头+位图信息头+调色板 的大小。

5、位深度

数字图像与机器视觉基础_第2张图片

  • 单色位图:每个像素最多可以表示2种颜色,只需要使用长度为1的二进制位来表示,因此每个像素占1/8byte。
  • 16色位图:每个像素最多可以表示16种颜色,所以只需要长度为4的二进制表示,因此每个像素占1/2byte。
  • 256色位图:每个像素最多可以表示256中颜色,所以只需要长度是8的二级制位表示就可以了,因此每个像素占1byte。
  • 24位位图:即RGB三原色位图每个像素占3个byte。

BMP图像大小计算公式: 大小= 分辨率*位深度/8

6、不同位深度图像对比

在这里插入图片描述

7、对比不同文件格式的图片文件大小

在这里插入图片描述

二、基于奇异值分解(SVD)提取图片特征值

奇异值分解(Singular Value Decomposition,以下简称SVD)是一种重要的矩阵分解方法,也是在机器学习领域广泛应用的算法,它不光可以用于降维算法中的特征分解,还可以用于推荐系统,以及自然语言处理等领域。是很多机器学习算法的基石。
本文采用ywl.bmp(2560 x 1440)示例:
数字图像与机器视觉基础_第3张图片

  • 代码:

import numpy as np
import os
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib as mpl
from pprint import pprint


def restore1(sigma, u, v, K):  # 奇异值、左特征向量、右特征向量
    m = len(u)
    n = len(v[0])
    a = np.zeros((m, n))
    for k in range(K):
        uk = u[:, k].reshape(m, 1)
        vk = v[k].reshape(1, n)
        a += sigma[k] * np.dot(uk, vk)
    a[a < 0] = 0
    a[a > 255] = 255
    # a = a.clip(0, 255)
    return np.rint(a).astype('uint8')


def restore2(sigma, u, v, K):  # 奇异值、左特征向量、右特征向量
    m = len(u)
    n = len(v[0])
    a = np.zeros((m, n))
    for k in range(K+1):
        for i in range(m):
            a[i] += sigma[k] * u[i][k] * v[k]
    a[a < 0] = 0
    a[a > 255] = 255
    return np.rint(a).astype('uint8')


if __name__ == "__main__":
    A = Image.open("C:/Users/86173/Pictures/Saved Pictures/ywl.png", 'r')
    print(A)
    output_path = r'./SVD_Output'
    if not os.path.exists(output_path):
        os.mkdir(output_path)
    a = np.array(A)
    print(a.shape)
    K = 50
    u_r, sigma_r, v_r = np.linalg.svd(a[:, :, 0])
    u_g, sigma_g, v_g = np.linalg.svd(a[:, :, 1])
    u_b, sigma_b, v_b = np.linalg.svd(a[:, :, 2])
    plt.figure(figsize=(11, 9), facecolor='w')
    mpl.rcParams['font.sans-serif'] = ['simHei']
    mpl.rcParams['axes.unicode_minus'] = False
    for k in range(1, K+1):
        print(k)
        R = restore1(sigma_r, u_r, v_r, k)
        G = restore1(sigma_g, u_g, v_g, k)
        B = restore1(sigma_b, u_b, v_b, k)
        I = np.stack((R, G, B), axis=2)
        Image.fromarray(I).save('%s\\svd_%d.png' % (output_path, k))
        if k <= 12:
            plt.subplot(3, 4, k)
            plt.imshow(I)
            plt.axis('off')
            plt.title('奇异值个数:%d' % k)
    plt.suptitle('SVD与图像分解', fontsize=20)
    plt.tight_layout(0.3, rect=(0, 0, 1, 0.92))
    # plt.subplots_adjust(top=0.9)
    plt.show()

  • 结果:
    数字图像与机器视觉基础_第4张图片

三、开闭运算检测图像中硬币和细胞的个数

1、检测硬币数量

数字图像与机器视觉基础_第5张图片

  • 代码:
import cv2
import numpy as np

def stackImages(scale, imgArray):
    """
        将多张图像压入同一个窗口显示
        :param scale:float类型,输出图像显示百分比,控制缩放比例,0.5=图像分辨率缩小一半
        :param imgArray:元组嵌套列表,需要排列的图像矩阵
        :return:输出图像
    """
    rows = len(imgArray)
    cols = len(imgArray[0])
    rowsAvailable = isinstance(imgArray[0], list)
    width = imgArray[0][0].shape[1]
    height = imgArray[0][0].shape[0]
    if rowsAvailable:
        for x in range(0, rows):
            for y in range(0, cols):
                if imgArray[x][y].shape[:2] == imgArray[0][0].shape[:2]:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
                else:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]),
                                                None, scale, scale)
                if len(imgArray[x][y].shape) == 2: imgArray[x][y] = cv2.cvtColor(imgArray[x][y], cv2.COLOR_GRAY2BGR)
        imageBlank = np.zeros((height, width, 3), np.uint8)
        hor = [imageBlank] * rows
        hor_con = [imageBlank] * rows
        for x in range(0, rows):
            hor[x] = np.hstack(imgArray[x])
        ver = np.vstack(hor)
    else:
        for x in range(0, rows):
            if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
                imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
            else:
                imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None, scale, scale)
            if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
        hor = np.hstack(imgArray)
        ver = hor
    return ver


#读取图片
src = cv2.imread("C:/picture/coin.png")
img = src.copy()

#灰度
img_1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#二值化
ret, img_2 = cv2.threshold(img_1, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

#腐蚀
kernel = np.ones((20, 20), int)
img_3 = cv2.erode(img_2, kernel, iterations=1)

#膨胀
kernel = np.ones((3, 3), int)
img_4 = cv2.dilate(img_3, kernel, iterations=1)

#找到硬币中心
contours, hierarchy = cv2.findContours(img_4, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2:]

#标识硬币
cv2.drawContours(img, contours, -1, (0, 0, 255), 5)

#显示图片
cv2.putText(img, "count:{}".format(len(contours)), (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 3)
cv2.putText(src, "src", (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 3)
cv2.putText(img_1, "gray", (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 3)
cv2.putText(img_2, "thresh", (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 3)
cv2.putText(img_3, "erode", (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 3)
cv2.putText(img_4, "dilate", (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 3)
imgStack = stackImages(1, ([src, img_1, img_2], [img_3, img_4, img]))
cv2.imshow("imgStack", imgStack)
cv2.waitKey(0)

  • 结果:
    数字图像与机器视觉基础_第6张图片

2、检测细胞数量

数字图像与机器视觉基础_第7张图片

  • 代码:
#导入函数库
import cv2
import numpy as np
#读取一张硬币图像
img = cv2.imread("C:/picture/cell.png")
#低通滤波处理
#对图像进行泛洪处理
h, w = img.shape[:2]  # 获取图像的长和宽
mask = np.zeros((h+2, w+2), np.uint8)  # 进行图像填充
cv2.floodFill(img, mask, (w-1, h-1), (255, 255, 255), (2, 2, 2), (3, 3, 3), 8)
#图像灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#通过高斯滤波对图像进行模糊处理,可以理解为对图像细胞去噪
blur = cv2.GaussianBlur(gray, (3, 3), 0, 0)  # 这里可以用中值滤波,具体视对图像效果选择
#cv2.imshow("blur", blur)
#cv2.waitKey(0)
#通过二进制阈值化对图像进行阈值化处理,将细胞轮廓与周围噪声区分开来
ret, thresh1 = cv2.threshold(blur, 189, 255, cv2.THRESH_BINARY)
#cv2.imshow("thresh1", thresh1)
#cv2.waitKey(0)
#进行闭运算,去除图像内部噪声
kernel = np.ones((7, 7), np.uint8)  # 设置卷积核
close = cv2.morphologyEx(thresh1, cv2.MORPH_CLOSE, kernel)  # 闭运算
#cv2.imshow("close", close)
#cv2.waitKey(0)
#利用canny算法对图像进行轮廓提取
Canny = cv2.Canny(close, 20, 150)
#显示图像轮廓提取图像
cv2.imshow("Canny", Canny)
#等待键盘键值关闭
cv2.waitKey(0)
#在提取出的轮廓图像中找出轮廓线条
(cnts1, _) = cv2.findContours(Canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#在终端打印出细胞个数
print("图像中的细胞共有:", len(cnts1), "个")
#将细胞轮廓线条画在原图
coins1 = img.copy()
cv2.drawContours(coins1, cnts1, -1, (0, 255, 0), 2)
#显示在原图上面检测出来的细胞
cv2.imshow("Cells", coins1)
#等待键盘键值关闭
cv2.waitKey(0)

  • 结果:
    数字图像与机器视觉基础_第8张图片

四、图片条形码定位

数字图像与机器视觉基础_第9张图片

  • 代码:
import cv2
import pyzbar.pyzbar as pyzbar
import numpy
from PIL import Image, ImageDraw, ImageFont


def decodeDisplay(img_path):

    img_data = cv2.imread(img_path)
    # 转为灰度图像
    gray = cv2.cvtColor(img_data, cv2.COLOR_BGR2GRAY)
    barcodes = pyzbar.decode(gray)

    for barcode in barcodes:

        # 提取条形码的边界框的位置
        # 画出图像中条形码的边界框
        (x, y, w, h) = barcode.rect
        cv2.rectangle(img_data, (x, y), (x + w, y + h), (0, 255, 0), 2)
        # 条形码数据为字节对象,所以如果我们想在输出图像上
        # 画出来,就需要先将它转换成字符串
        barcodeData = barcode.data.decode("utf-8")
        barcodeType = barcode.type
        #不能显示中文
        # 绘出图像上条形码的数据和条形码类型
        #text = "{} ({})".format(barcodeData, barcodeType)
        #cv2.putText(imagex1, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX,5, (0, 0, 125), 2)

        #更换为:
        img_PIL = Image.fromarray(cv2.cvtColor(img_data, cv2.COLOR_BGR2RGB))
        # 参数(字体,默认大小)
        font = ImageFont.truetype('msyh.ttc', 35)
        # 字体颜色(rgb)
        fillColor = (0, 255, 255)
        # 文字输出位置
        position = (x, y-50)
        # 输出内容
        str = barcodeData
        # 需要先把输出的中文字符转换成Unicode编码形式(  str.decode("utf-8)   )

        draw = ImageDraw.Draw(img_PIL)
        draw.text(position, str, font=font, fill=fillColor)
        # 使用PIL中的save方法保存图片到本地
        img_PIL.save('1.jpg', 'jpeg')
        # 向终端打印条形码数据和条形码类型
        print("扫描结果==》 类别: {0} 内容: {1}".format(barcodeType, barcodeData))

if __name__ == '__main__':
    decodeDisplay("C:/picture/ma.png")
  • 结果:
    在这里插入图片描述
    数字图像与机器视觉基础_第10张图片

五、总结

对图片的一些格式有了一定了解,处理图像使用OpenCV很方便

参考文章

【计算机视觉】数字图像与机器视觉基础
数字图像与机器视觉基础(1)

你可能感兴趣的:(图像处理,计算机视觉,人工智能)