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

一、PIL: Python图像处理类库 

from PIL import Image
from matplotlib import pyplot as plt
img = Image.open(r'eye.jpg') # 打开图片

img1 = Image.open(r'eye.jpg').convert('L') # 转换为灰度图

img2 = Image.open(r'eye.jpg')
img2.thumbnail((50, 100)) # 生成大小为100*100的缩略图

img3 = img.crop((50, 50, 150, 150)) # 剪切图片

img4 = Image.open(r'eye.jpg')
region = img3.rotate(180)
img4.paste(region,(50, 50, 150, 150))# 旋转180度放回

img5 = img.resize((128,128)) # 调整尺寸

plt.rcParams['font.sans-serif'] = ['SimHei']# 用来设置字体样式以正常显示中文标签
plt.subplot(231), plt.imshow(img), plt.title('原始图像'), plt.axis('off')
plt.subplot(232), plt.imshow(img1, plt.cm.gray), plt.title('灰度图像'), plt.axis('off')
plt.subplot(233), plt.imshow(img2), plt.title('缩略图'), plt.axis('off')
plt.subplot(234), plt.imshow(img3), plt.title('剪切图片'), plt.axis('off')
plt.subplot(235), plt.imshow(img4), plt.title('旋转180度后放回图像'), plt.axis('off')
plt.subplot(236), plt.imshow(img5), plt.title('调整尺寸后图像'), plt.axis('off')
plt.show()

二、Matplotlib

1绘制图像、点和线

from PIL import Image
from pylab import *

img = array(Image.open(r'eye.jpg'))

imshow(img)  # 绘制图像
# 设置点
x = [100, 100, 300, 300]
y = [50, 200, 50, 200]

plot(x[:2], y[:2], 'r*')  # 前两点使用红色星状标记绘制点

# 绘制连接前两个点的线
plot(x[:2], y[:2])  # 默认为蓝色实线
plot(x[1:3], y[1:3],'g--')   # 绿线虚线
plot(x[2:4], y[2:4],'ks:')   # 带有正方形标记的黑色点线

plt.axis('off')  # 不显示坐标轴

title('plotting:"eye.jpg"')
show()

2图像轮廓和直方图

from PIL import Image
import numpy as np
from pylab import plt

img = Image.open(r'eye.jpg')
img = img.convert('L')
img = np.array(img)

plt.figure(figsize=(10, 5))

plt.subplot(121)
plt.gray()
plt.contour(img, origin='image')
plt.title('contour Image')
plt.axis('off')

plt.subplot(122)
plt.hist(img.flatten(), 128)
plt.title('Histogram')
plt.xlabel('Pixel Intensity')
plt.ylabel('Frequency')
plt.xlim([0, 256])
plt.show()

3交互式标注

from PIL import Image
from pylab import *

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

 三、NumPy

1图像数组表示

im = array(Image.open('eye.jpg'))
print(im.shape, im.dtype)

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

 

每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。图像通常被编码成无符号八位整数(uint8)。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。 

数组中的元素可以使用下标访问。位于坐标i、j,以及颜色通道k的像素值可以像下面这样访问:

value = im[i,j,k]

#多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。下面是有关灰度图像的一些例子:

im[i,:] = im[j,:]      # 将第 j 行的数值赋值给第 i 行
im[:,i] = 100          # 将第 i 列的所有数值设为100
im[:100,:50].sum()     # 计算前100 行、前 50 列所有数值的和
im[50:100,50:100]      # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
im[i].mean()           # 第 i 行所有数值的平均值
im[:,-1]               # 最后一列
im[-2,:] (or im[-2])   # 倒数第二行

 2灰度变换

from PIL import Image
from matplotlib import pyplot as plt
import numpy as np

im = Image.open(r'eye.jpg').convert('L')
im = np.array(im)

im2 = 255 - im # 对图像进行反相处理
im3 = (100.0/255) * im + 100 # 将图像像素值变换到100...200 区间
im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(141), plt.imshow(im, plt.cm.gray), plt.title('原始灰度图像'), plt.axis('off')
plt.subplot(142), plt.imshow(im2, plt.cm.gray), plt.title('反向处理'), plt.axis('off')
plt.subplot(143), plt.imshow(im3, plt.cm.gray), plt.title('区间变换'), plt.axis('off')
plt.subplot(144), plt.imshow(im4, plt.cm.gray), plt.title('求平方'), plt.axis('off')
plt.show()
# 查看图像中的最小和最大像素值
print(int(im.min()), int(im.max()))
print(int(im2.min()), int(im2.max()))
print(int(im3.min()), int(im3.max()))
print(int(im4.min()), int(im4.max()))

变换函数图像:

最大值和最小值: 

3图像缩放

def imresize(im,sz):
  """ 使用PIL 对象重新定义图像数组的大小"""
  pil_im = Image.fromarray(np.uint8(im))

  return np.array(pil_im.resize(sz))

 4直方图均衡化

def histeq(im,nbr_bins=256):
  """ 对一幅灰度图像进行直方图均衡化"""

  # 计算图像的直方图
  imhist,bins = histogram(im.flatten(),nbr_bins,density=True)
  cdf = imhist.cumsum() # cumulative distribution function
  cdf = 255 * cdf / cdf[-1] # 归一化

  # 使用累积分布函数的线性插值,计算新的像素值
  im2 = interp(im.flatten(),bins[:-1],cdf)

  return im2.reshape(im.shape), cdf

im = array(Image.open('eye.jpg').convert('L'))
im2, cdf = histeq(im)

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(221), plt.imshow(im, plt.cm.gray), plt.title('原始灰度图像'), plt.axis('off')
plt.subplot(222), plt.imshow(im2, plt.cm.gray), plt.title('直方图均衡化后图像'), plt.axis('off')
plt.subplot(223), plt.hist(im.flatten(), 128), plt.title('原始灰度图像直方图'), plt.xlabel('Pixel Intensity'), plt.ylabel(
    'Frequency'), plt.xlim([0, 256])
plt.subplot(224), plt.hist(im2.flatten(), 128), plt.title('直方图均衡化后图像直方图'), plt.xlabel('Pixel Intensity'), plt.ylabel(
    'Frequency'), plt.xlim([0, 256])
plt.show()

 错误:

原文:
imhist, bins = np.histogram(img.flatten(), nbr_bins, normed=True)

报错:
TypeError: histogram() got an unexpected keyword argument 'normed'

原因:
numpy.histogram函数中的normed参数在NumPy版本1.21.0中被弃用,并在版本1.24.0中被删除。

改为:
imhist, bins = np.histogram(img.flatten(), nbr_bins, density=True)

5图像平均 

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

def compute_average(imlist):
  """ 计算图像列表的平均图像"""

  # 打开第一幅图像,将其存储在浮点型数组中
  averageim = array(Image.open(imlist[0]), 'f')

  for imname in imlist[1:]:
    try:
      averageim += array(Image.open(imname))
    except:
      print(imname + '...skipped')
  averageim /= len(imlist)

  # 返回uint8 类型的平均图像
  return array(averageim, 'uint8')

 该函数可以自动跳过不能打开的图像。

还可以使用 mean() 函数计算平均图像。mean() 函数需要将所有的图像堆积到一个数组中;也就是说,如果有很多图像,该处理方式需要占用很多内存。

6图像的主成分分析(PCA)

PCA(Principal Component Analysis,主成分分析)是一个非常有用的降维技巧。在使用尽可能少维数的前提下,尽量多地保持训练数据的信息。一兆像素的图像具有百万维。由于图像具有很高的维数,在许多计算机视觉应用中,我们经常使用降维操作。PCA 产生的投影矩阵可以被视为将原始坐标变换到现有的坐标系,坐标系中的各个坐标按照重要性递减排列。

将变平的图像堆积起来,我们可以得到一个矩阵,矩阵的一行表示一幅图像。在计算主方向之前,所有的行图像按照平均图像进行了中心化。我们通常使用 SVD(Singular Value Decomposition,奇异值分解)方法来计算主成分;但当矩阵的维数很大时,SVD 的计算非常慢,所以此时通常不使用 SVD 分解。

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

def pca(X):
    """ 主成分分析:
    输入:矩阵X ,其中该矩阵中存储训练数据,每一行为一条训练数据
    返回:投影矩阵(按照维度的重要性排序)、方差和均值"""

    # 获取维数
    num_data,dim = X.shape

    # 数据中心化
    mean_X = X.mean(axis=0)
    X = X - mean_X

    if dim > num_data :
         # PCA- 使用紧致技巧
        M = dot(X,X.T) # 协方差矩阵
        e,EV = linalg.eigh(M) # 特征值和特征向量
        tmp = dot(X.T,EV).T # 这就是紧致技巧
        V = tmp[::-1] # 由于最后的特征向量是我们所需要的,所以需要将其逆转
        S = sqrt(e)[::-1] # 由于特征值是按照递增顺序排列的,所以需要将其逆转
        for i in range(V.shape[1]):
             V[:,i] /= S
    else:
        # PCA- 使用SVD 方法
        U,S,V = linalg.svd(X)
        V = V[:num_data] # 仅仅返回前nun_data 维的数据才合理

    # 返回投影矩阵、方差和均值
    return V,S,mean_X


def get_imlist(path):
    return [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.png')]

imlist = get_imlist('mnist_train/0/')
im = array(Image.open(imlist[0])) # 打开一幅图像,获取其大小
m,n = im.shape[0:2] # 获取图像的大小
imnbr = len(imlist) # 获取图像的数目

# 创建矩阵,保存所有压平后的图像数据
immatrix = array([array(Image.open(im)).flatten()
               for im in imlist],'f')

# 执行 PCA 操作
V,S,immean = pca(immatrix)

# 显示一些图像(均值图像和前 7 个模式)
figure()
gray()
subplot(2,4,1)
imshow(immean.reshape(m,n))
for i in range(7):
  subplot(2,4,i+2)
  imshow(V[i].reshape(m,n))

show()

 7使用pickle模块

pickle模块可以接受几乎所有的 Python 对象,并且将其转换成字符串表示,该过程叫做封装(pickling)。从字符串表示中重构该对象,称为拆封(unpickling)。这些字符串表示可以方便地存储和传输。 

# 保存均值和主成分数据
f = open('font_pca_modes.pkl', 'wb')
pickle.dump(immean,f)
pickle.dump(V,f)
f.close()

# 载入均值和主成分数据
f = open('font_pca_modes.pkl', 'rb')
immean = pickle.load(f)
V = pickle.load(f)
f.close()

print(immean)

保存: 

读取: 

在本书接下来的章节中,我们将使用 with 语句处理文件的读写操作。可以自动打开和关闭文件(即使在文件打开时发生错误) 

# 打开文件并保存
with open('font_pca_modes.pkl', 'wb') as f:
  pickle.dump(immean,f)
  pickle.dump(V,f)

# 打开文件并载入
with open('font_pca_modes.pkl', 'rb') as f:
  immean = pickle.load(f)
  V = pickle.load(f)

如果数据中不包含复杂的数据结构,比如在一幅图像上点击的点列表,NumPy 的读写函数会很有用。保存一个数组 x 到文件中 

#保存
savetxt('test.txt',x,'%i')
#读取
x = loadtxt('test.txt')

 四、SciPy

SciPy 提供很多高效的操作,可以实现数值积分、优化、统计、信号处理,以及图像处理功能。

1图像模糊

图像的 高斯模糊是非常经典的图像卷积例子。通常是其他图像处理操作的一部分,比如图像插值操作、兴趣点计算以及 很多其他应用。
黑白图像:
from PIL import Image
from numpy import *
from scipy.ndimage import filters

from matplotlib import pyplot as plt

im = array(Image.open('eye.jpg').convert('L'))
im2 = filters.gaussian_filter(im,2)
im3 = filters.gaussian_filter(im,5)
im4 = filters.gaussian_filter(im,10)

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(141), plt.imshow(im, plt.cm.gray), plt.title('原始灰度图像'), plt.axis('off')
plt.subplot(142), plt.imshow(im2, plt.cm.gray), plt.title('σ=2高斯滤波器'), plt.axis('off')
plt.subplot(143), plt.imshow(im3, plt.cm.gray), plt.title('σ=5高斯滤波器'), plt.axis('off')
plt.subplot(144), plt.imshow(im4, plt.cm.gray), plt.title('σ=10高斯滤波器'), plt.axis('off')

plt.show()

彩色图像: 

im = array(Image.open('eye.jpg'))
im2 = zeros(im.shape)
for i in range(3):
    im2[:,:,i] = filters.gaussian_filter(im[:,:,i],5)
im2 = uint8(im2)

2图像导数

强度的 变化可以用灰度图像 I (对于彩色图像,通常对每个颜色通道分别计算导数)的 x y 方向导数I_{x}I_{y} 进行描述。
from PIL import Image
from numpy import *
from scipy.ndimage import filters
from matplotlib import pyplot as plt

im = array(Image.open('eye.jpg').convert('L'))
# Sobel 导数滤波器
imx = zeros(im.shape)
filters.sobel(im,1,imx)
imy = zeros(im.shape)
filters.sobel(im,0,imy)
magnitude = sqrt(imx**2+imy**2)

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(141), plt.imshow(im, plt.cm.gray), plt.title('原始灰度图像'), plt.axis('off')
plt.subplot(142), plt.imshow(imx, plt.cm.gray), plt.title('x导数图像'), plt.axis('off')
plt.subplot(143), plt.imshow(imy, plt.cm.gray), plt.title('y导数图像'), plt.axis('off')
plt.subplot(144), plt.imshow(magnitude, plt.cm.gray), plt.title('梯度大小图像'), plt.axis('off')

plt.show()

3形态学:对象计数

from scipy.ndimage import measurements,morphology
from PIL import Image
from matplotlib import pyplot as plt
from numpy import *
# 载入图像,然后使用阈值化操作,以保证处理的图像为二值图像
im = array(Image.open('ta.jpg').convert('L'))
im = 1*(im<128)
labels, nbr_objects = measurements.label(im)
print("Number of objects:", nbr_objects)

# 形态学开操作更好地分离各个对象
im_open = morphology.binary_opening(im,ones((9,5)),iterations=2)
labels_open, nbr_objects_open = measurements.label(im_open)
print("Number of objects:", nbr_objects_open)

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(221), plt.imshow(im, plt.cm.gray), plt.title('二值化图像'), plt.axis('off')
plt.subplot(222), plt.imshow(labels, plt.cm.gray), plt.title('labels数组图像'), plt.axis('off')
plt.subplot(223), plt.imshow(im_open, plt.cm.gray), plt.title('开操作后图像'), plt.axis('off')
plt.subplot(224), plt.imshow(labels_open, plt.cm.gray), plt.title('开操作后labels数组图像'), plt.axis('off')
plt.show()

五、高级示例:图像去噪

图像去噪是在去除图像噪声的同时,尽可能地保留图像细节和结构的处理技术。我们这里使用ROF(Rudin-Osher-Fatemi)去噪模型。图像去噪对于很多应用来说都非常重要;这些应用范围很广,小到让你的假期照片看起来更漂亮,大到提高卫星图像的质量。ROF模型具有很好的性质:使处理后的图像更平滑,同时保持图像边缘和结构信息。

from numpy import *
from numpy import random
from scipy.ndimage import filters
import rof
from matplotlib import pyplot as plt
# 使用噪声创建合成图像
im = zeros((500,500))
im[100:400,100:400] = 128
im[200:300,200:300] = 255
im = im + 30*random.standard_normal((500,500))
U,T = rof.denoise(im,im)
G = filters.gaussian_filter(im,10)

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(131), plt.imshow(im, plt.cm.gray), plt.title('原始图像'), plt.axis('off')
plt.subplot(132), plt.imshow(G, plt.cm.gray), plt.title('经过高斯模糊后图像'), plt.axis('off')
plt.subplot(133), plt.imshow(U, plt.cm.gray), plt.title('经过 ROF 模型去噪后图像'), plt.axis('off')
plt.show()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值