Python计算机视觉编程 第一章 基本的图像操作和处理

2023-10-27

1.1 PIL:Python图像处理类库

    PIL(Python Imaging Library Python,图像处理类库)提供了通用的图像处理功能,以及大量有用的基本图像操作,比如图像缩放、裁剪、旋转、颜色转换等。

1.1.1转换图像格式

    通过 save() 方法,PIL 可以将图像保存成多种格式的文件。

save(filename,format)

代码:

from PIL import Image

im = Image.open('JMU1.jpg', "r")
im1 = im.save('JMU1.png', 'png')

结果:
在这里插入图片描述
分析:
    通过 save() 方法成功将jpg图像重新保存成png格式。

1.1.2创建缩略图

    thumbnail()方法接受一个元组参数(该参数指定生成缩略图的大小),然后将图像转换成符合元组参数指定大小的缩率图。
    例如,创建最长边为128像素的缩略图,可以使用下列命令:

pil_im.thumbnail((128,128))

代码1:

from PIL import Image
from pylab import *
im = Image.open('JMU1.jpg', "r")
subplot(121)
title('(a)')
axis('off')
imshow(im)

im.save('JMU1.png', 'png')
im.thumbnail((128, 128), resample=Image.BICUBIC)
subplot(122)
title('(b)')
axis('off')
imshow(im)
show()

结果1:
在这里插入图片描述
代码2:

from PIL import Image

im = Image.open('JMU1.jpg', "r")
im.save('JMU1.png', 'png')
im.show()
im.thumbnail((128, 128), resample=Image.BICUBIC)
im.show()

结果2:
在这里插入图片描述
分析:
    从结果1中可以看出,图像在同样大小的情况下展示,会变得模糊;从结果2中可以看出,正常情况下展示指定像素的缩略图,图像会变小。

1.1.3复制和粘贴图像区域

    使用crop()方法可以从一幅图像中裁剪指定区域:

box=(100,100,400,400)
region=pil_im.crop(box)

    该区域使用四元组来指定。四元组的坐标依次是(左,上,右,下)。PIL中指定坐标系的左上角坐标为(0,0)。我们可以旋转上面代码中获取的区域,然后使用paste()方法将该区域放回去,具体实现如下:

region=region.transpose(Image.ROTATE_180)
pil_im.paste(region,box)

代码:

from PIL import Image
from pylab import *
im = Image.open('JMU1.jpg', "r")
subplot(131)
title('(a)')
axis('off')
imshow(im)

box = (200,350,400,450)
subplot(132)
title('(b)')
axis('off')
imshow(im.crop(box))

region = im.crop(box)
im.paste(region,(400,200),None)
subplot(133)
title('(c)')
axis('off')
imshow(im)
show()

结果:
在这里插入图片描述
分析:
    从图像中裁剪的区域box = (200,350,400,450)表示裁剪区域左上角的x坐标为200,左上角y坐标为350,右下角x坐标为400,右下角y坐标为450,如结果中的(b)所示。使用paste()方法将裁剪的区域粘贴在左上角位置为(400,200)的区域。

1.1.4调整尺寸和旋转

    调用resize()方法调整一幅图像的尺寸。

out=pil_im.resize((128,128))

    调用rotate()方法旋转一幅图像,使用逆时针方式表示旋转角度。

out=pil_im.rotate(45)

代码:

from PIL import Image
from pylab import *
im = Image.open('boy.jpg', "r")
subplot(331)
title('(a)')
axis('off')
imshow(im)

Image.FLIP_LEFT_RIGHT = im.transpose(Image.FLIP_LEFT_RIGHT)
subplot(332)
title('(b)')
axis('off')
imshow(Image.FLIP_LEFT_RIGHT)

Image.FLIP_TOP_BOTTOM = im.transpose(Image.FLIP_TOP_BOTTOM)
subplot(333)
title('(c)')
axis('off')
imshow(Image.FLIP_TOP_BOTTOM)

Image.ROTATE_45 = im.rotate(45)
subplot(334)
title('(d)')
axis('off')
imshow(Image.ROTATE_45)

Image.ROTATE_90 = im.transpose(Image.ROTATE_90)
subplot(335)
title('(e)')
axis('off')
imshow(Image.ROTATE_90)

Image.ROTATE_180 = im.transpose(Image.ROTATE_180)
subplot(336)
title('(f)')
axis('off')
imshow(Image.ROTATE_180)

Image.ROTATE_270 = im.transpose( Image.ROTATE_270)
subplot(337)
title('(g)')
axis('off')
imshow( Image.ROTATE_270)

Image.TRANSPOSE = im.transpose(Image.TRANSPOSE)
subplot(338)
title('(h)')
axis('off')
imshow(Image.TRANSPOSE)

Image.TRANSVERSE = im.transpose(Image.TRANSVERSE)
subplot(339)
title('(i)')
axis('off')
imshow(Image.TRANSVERSE)

show()

结果:
在这里插入图片描述
分析:
    (b)将图像左右翻转,(c)将图像上下翻转,(d)将图像逆时针旋转45°,(e)将图像逆时针旋转90°,(f)将图像逆时针旋转180°,(g)将图像逆时针旋转270°,(h)将图像进行转置(相当于顺时针旋转90°),(i)将图像进行转置,再水平翻转。

1.2Matplotlib

    处理数学运算、绘制图表,或者在图像上绘制点、直线和曲线时,Matplotlib是个很好的类库,具有比PIL更强大的绘图功能。

1.2.1绘制图像、点和线

代码:

from PIL import Image
from pylab import *

# 添加中文字体支持
from matplotlib.font_manager import FontProperties

font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)

# 读取图像到数组中
im = array(Image.open('JMU3.jpg'))
figure()

# 绘制有坐标轴的
subplot(121)
imshow(im)
x = [100, 100, 400, 400]
y = [200, 500, 200, 500]

# 使用红色星状标记绘制点
plot(x, y, 'r*')


# 绘制连接两个点的线(默认为蓝色)
plot(x[:2], y[:2])
title(u'带坐标轴', fontproperties=font)

# 不显示坐标轴的
subplot(122)
imshow(im)
x = [100, 100, 400, 400]
y = [200, 500, 200, 500]

plot(x, y, 'r*')
plot(x[:2], y[:2])
axis('off')
title(u'不带坐标轴', fontproperties=font)

show()
# show()命令首先打开图形用户界面(GUI),然后新建一个窗口,该图形用户界面会循环阻断脚本,然后暂停,
# 直到最后一个图像窗口关闭。每个脚本里,只能调用一次show()命令,通常相似脚本的结尾调用。

结果:
在这里插入图片描述
    axis(‘off’) 命令可以使坐标轴不显示。
    在绘图时,有很多选项可以控制图像的颜色和样式。最有用的一些短命令如表1-1、表1-2和表1-3所示。使用方法见下面的例子:
在这里插入图片描述
在这里插入图片描述

1.2.2图像轮廓和直方图

    用PIL的convert()方法将图像转化为灰度图像。用hist()函数绘制(灰度)图像的直方图。
代码:

from PIL import Image
from pylab import *

# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
im = array(Image.open("JMU2.jpg").convert('L'))  # 打开图像,并转成灰度图像

figure()

subplot(131)
axis('off')
imshow(im)
axis('equal')
axis('off')
title(u'(a)原图像', fontproperties=font)

# 使用颜色信息
subplot(132)
gray()
# 在原点的左上角显示轮廓图像
contour(im, origin='image')
axis('equal')
axis('off')
title(u'(b)图像轮廓', fontproperties=font)

subplot(133)
hist(im.flatten(), 128)
title(u'(c)图像直方图', fontproperties=font)
plt.xlim([0,260])
plt.ylim([0,11000])

show()

结果:
在这里插入图片描述

1.2.3交互式标注

    有时用户需要和某些应用交互,例如在一幅图像中标记一些点,或者标注一些训练数据。PyLab库中的ginput()函数就可以实现交互式标注。
代码:

from PIL import Image
from pylab import *

im = array(Image.open('JMU2.jpg'))
imshow(im)
print('Please click 3 points')
x = ginput(3)
print('you clicked:', x)
show()

结果:
在这里插入图片描述
分析:
    用ginput()交互注释,代码中设置的交互注释数据点设置为3个,显示读取的图像后,然后注释,会将注释点的坐标打印出来。

1.3NumPy

    NumPy是非常有名的 Python 科学计算工具包,其中包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性代数函数。NumPy中的数组对象可以帮助实现数组中重要的操作,比如矩阵乘积、转置、解方程系统、向量乘积和归一化, 这为图像变形、对变化进行建模、图像分类、图像聚类等提供了基础。

1.3.1图像数组表示

    NumPy中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表(或者是列表的列表),但是数组中所有的元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。
代码:

from PIL import Image
from pylab import *

im = array(Image.open('JMU2.jpg'))
print(im.shape, im.dtype)
im = array(Image.open('JMU2.jpg').convert('L'),'f')
print(im.shape, im.dtype)

结果:
在这里插入图片描述
分析:
    每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表 示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。

1.3.2灰度变换

    将图像读入 NumPy 数组对象后,我们可以对它们执行任意数学操作。一个简单的例子就是图像的灰度变换。
代码:

from PIL import Image
from numpy import *
from pylab import *

im = array(Image.open("JMU3.jpg").convert('L'))
print(int(im.min()), int(im.max()))

im2 = 255 - im  # 对图像进行反相处理
print(int(im2.min()), int(im2.max()))

im3 = (100.0/255) * im + 100   # 将图像像素值变换到 100...200 区间
print(int(im3.min()), int(im3.max()))

im4 = 255.0 * (im/255.0)**2   # 对图像像素值求平方后得到的图像
print(int(im4.min()), int(im4.max()))

figure()
gray()
subplot(1, 3, 1)
imshow(im2)
axis('off')
title(r'(a) $f(x)=255-x$')

subplot(1, 3, 2)
imshow(im3)
axis('off')
title(r'(b) $f(x)=\frac{100}{255}x+100$')

subplot(1, 3, 3)
imshow(im4)
axis('off')
title(r'(c) $f(x)=255(\frac{x}{255})^2$')
show()


结果:
在这里插入图片描述
在这里插入图片描述
分析:
    (a)对图像进行反相处理,(b)将图像的像素值变换到100…200区间,(c)对图像像素值求平方后得到的图像。

1.3.3直方图均衡化

    直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。
代码:

import cv2
import matplotlib.pyplot as plt
import numpy as np

gray_level = 256  # 灰度级


def pixel_probability(img):
    assert isinstance(img, np.ndarray)
    prob = np.zeros(shape=(256))
    for rv in img:
        for cv in rv:
            prob[cv] += 1

    r, c = img.shape
    prob = prob / (r * c)

    return prob


def probability_to_histogram(img, prob):
    prob = np.cumsum(prob)  # 累计概率
    img_map = [int(i * prob[i]) for i in range(256)]  # 像素值映射

   # 像素值替换
    assert isinstance(img, np.ndarray)
    r, c = img.shape
    for ri in range(r):
        for ci in range(c):
            img[ri, ci] = img_map[img[ri, ci]]

    return img


def plot(y, name):
    plt.figure(num=name)
    plt.bar([i for i in range(gray_level)], y, width=1)


if __name__ == '__main__':
    img = cv2.imread("cat.jpg", 0)  # 读取灰度图
    prob = pixel_probability(img)
    plot(prob, "原图直方图")

    # 直方图均衡化
    img = probability_to_histogram(img, prob)
    cv2.imwrite("source_hist.jpg", img)  # 保存图像

    prob = pixel_probability(img)
    plot(prob, "直方图均衡化结果")

    plt.show()


结果:
在这里插入图片描述
在这里插入图片描述

分析:
    均衡化之后,图像的亮度变得比较均衡,图像一些细节也得以观察到。

1.3.4图像平均

    图像平均操作是减少图像噪声的一种简单方式,通常用于艺术特效。可以简单地从图像列表中计算出一幅平均图像。假设所有的图像具有相同的大小,我们可以将这些图像简单地相加,然后除以图像的数目,来计算平均图像。

1.3.5使用pickle模块

    如果想要保存一些结果或者数据以方便后续使用,Python中的pickle模块非常有用。pickle模块可以接受几乎所有的Python对象,并且将其转换成字符串表示,该过程叫做封装。从字符串表示中重构该对象,称为拆封。 这些字符串表示可以方便地存储和传输。

1.4SciPy

    SciPy是建立在NumPy基础上,用于数值运算的开源工具包。SciPy提供很多高效的操作,可以实现数值积分、优化、统计、信号处理,以及对我们来说最重要的图像处理功能。

1.4.1图像模糊

    图像的高斯模糊是非常经典的图像卷积例子。本质上,图像模糊就是将(灰度)图像 I I I和一个高斯核进行卷积操作:
I σ = I ∗ G σ I_{\sigma }=I*G_{\sigma} Iσ=IGσ
其中 ∗ * 表示卷积操作: G σ G_{\sigma} Gσ是标准差为 σ \sigma σ的二维高斯核,定义为:
G σ = 1 2 π σ e − ( x 2 + y 2 ) / 2 σ 2 G_{\sigma} =\frac{1}{2\pi \sigma }e^{-(x^{2}+y^{2})/2\sigma ^{2}} Gσ=2πσ1e(x2+y2)/2σ2
代码:

from PIL import Image
from pylab import *
from scipy.ndimage import filters

# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\\windows\\fonts\\SimSun.ttc", size=14)

im = array(Image.open('JMU2.jpg').convert('L'))

figure()
gray()
axis('off')
subplot(1, 4, 1)
axis('off')
title(u'原图', fontproperties=font)
imshow(im)

for bi, blur in enumerate([2, 5, 10]):
  im2 = zeros(im.shape)
  im2 = filters.gaussian_filter(im, blur)
  im2 = np.uint8(im2)
  imNum=str(blur)
  subplot(1, 4, 2 + bi)
  axis('off')
  title(u'标准差为'+imNum, fontproperties=font)
  imshow(im2)

show()

结果:
在这里插入图片描述
分析:
    随着标准差 σ \sigma σ的增大,图像的模糊程度也增大,图像丢失的细节也越多。

1.4.2图像导数

    强度的变化可以用灰度图像 I I I x 和 y x和y xy方向导数 I x 和 I y I_{x}和I_{y} IxIy进行描述。
    图像的梯度向量为 ▽ I = [ I x , I y ] T \triangledown I=[I_{x},I_{y}]^{T} I=[Ix,Iy]T。梯度有两个重要的属性,一是梯度的大小:
∣ ▽ I ∣ = I x 2 + I y 2 \left | \triangledown I \right |=\sqrt{I_{x}^{2}+I_{y}^{2}} I=Ix2+Iy2
它描述了图像强度变化的强弱,一是梯度的角度:
α = a r c t a n 2 ( I y , I x ) \alpha =arctan2(I_{y},I_{x}) α=arctan2(Iy,Ix)
描述了图像中在每个点(像素)上强度变化最大的方向。
    我们可以用离散近视的方式来计算图像的导数。图像导数大多数可以通过卷积简单地实现:
I x = I ∗ D x 和 I y = I ∗ D y I_{x}=I*D_{x}和I_{y}=I*D_{y} Ix=IDxIy=IDy
对于 D x 和 D y D_{x}和D_{y} DxDy,通常选择Prewitt滤波器或者Sobel滤波器。这些导数滤波器可以使用scipy.ndimage.filters模块的标准卷积操作来简单地实现。
代码:

from PIL import Image
from pylab import *
from scipy.ndimage import filters
import numpy

# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)

im = array(Image.open('JMU3.jpg').convert('L'))
gray()

subplot(1, 4, 1)
axis('off')
title(u'(a)原始灰度图像', fontproperties=font)
imshow(im)

# Sobel derivative filters
imx = zeros(im.shape)
filters.sobel(im, 1, imx)
subplot(1, 4, 2)
axis('off')
title(u'(b)x导数图像', fontproperties=font)
imshow(imx)

imy = zeros(im.shape)
filters.sobel(im, 0, imy)
subplot(1, 4, 3)
axis('off')
title(u'(c)y导数图像', fontproperties=font)
imshow(imy)

mag = 255-numpy.sqrt(imx**2 + imy**2)
subplot(1, 4, 4)
title(u'(d)梯度大小图像', fontproperties=font)
axis('off')
imshow(mag)

show()

结果:
在这里插入图片描述
分析:
    在这个方法中,滤波器的尺度需要随着图像分辨率的变化而变化。为了在图像噪声方面更稳健,以及在任意尺度上计算导数,我们可以使用高斯导数滤波器 。处理方法如下。
代码:

# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
from scipy.ndimage import filters
import numpy

def imx(im, sigma):
    imgx = zeros(im.shape)
    filters.gaussian_filter(im, sigma, (0, 1), imgx)
    return imgx


def imy(im, sigma):
    imgy = zeros(im.shape)
    filters.gaussian_filter(im, sigma, (1, 0), imgy)
    return imgy


def mag(im, sigma):
    imgmag = 255 - numpy.sqrt(imgx ** 2 + imgy ** 2)
    return imgmag


im = array(Image.open('JMU3.jpg').convert('L'))
figure()
gray()

sigma = [2, 5, 10]

for i in  sigma:
    subplot(3, 4, 4*(sigma.index(i))+1)
    axis('off')
    imshow(im)
    imgx=imx(im, i)
    subplot(3, 4, 4*(sigma.index(i))+2)
    axis('off')
    imshow(imgx)
    imgy=imy(im, i)
    subplot(3, 4, 4*(sigma.index(i))+3)
    axis('off')
    imshow(imgy)
    imgmag=mag(im, i)
    subplot(3, 4, 4*(sigma.index(i))+4)
    axis('off')
    imshow(imgmag)

show()

结果:
在这里插入图片描述

1.4.3形态学:对象计数

    形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合。 形态学通常用于处理二值图像,但是也能够用于灰度图像。二值图像是指图像的每 个像素只能取两个值,通常是 0 和 1。二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。
代码:

from PIL import Image
from scipy.ndimage import measurements, morphology
from pylab import *

"""   This is the morphology counting objects example in Section 1.4.  """

# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)

figure()
gray()
im = array(Image.open('JMU2.jpg').convert('L'))
subplot(221)
imshow(im)
axis('off')
title(u'(a)原始二值图像', fontproperties=font)
im = (im < 128)

labels, nbr_objects = measurements.label(im)
print("Number of objects:", nbr_objects)
subplot(222)
imshow(labels)
axis('off')
title(u'(b)原始图像的标签图像', fontproperties=font)

im_open = morphology.binary_opening(im, ones((9, 5)), iterations=2)
subplot(223)
imshow(im_open)
axis('off')
title(u'(c)开操作后的二值图像', fontproperties=font)

labels_open, nbr_objects_open = measurements.label(im_open)
print("Number of objects:", nbr_objects_open)
subplot(224)
imshow(labels_open)
axis('off')
title(u'(d)开操作后图像的标签图像', fontproperties=font)

show()

结果:
在这里插入图片描述
在这里插入图片描述
分析:
    通过阈值化方式来确保该图像是二值图像。通过和 1 相乘,脚本将布尔数组转换成二进制表示。然后,我们使用 label()函数寻找单个的物体,并且按照它们属于哪个对象将整数标签给像素赋值。

1.5总结

    本章讲解了操作和处理图像的基础知识,这些知识在《数字图像处理》课程中已经多次使用过了。现在又通过实验对这些知识更进一步学习了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python计算机视觉编程 第一章 基本的图像操作和处理 的相关文章

随机推荐