【图像处理】python_基本处理


前言

基本的图像操作和处理
参考书:《Python计算机视觉编程》


基本的图像操作和处理

PIL

from PIL import Image
import os
读取图片
pil_im = Image.open('01.png')
# type(pil_im)  # 类型: PIL 图像对象
转换成灰度图
pil_im = pil_im.convert('L')
转换成缩略图
pil_im.thumbnail((128,64))  # w,h 长宽比锁定
裁剪指定区域
box = (50,50,100,100)  # 以原图左上角为(0,0),前两个值为裁剪图的左上角坐标,后两个为右下角
region = pil_im.crop(box)  # 从图像中裁剪指定区域
region = region.transpose(Image.Transpose.ROTATE_180)  # 将裁剪区域旋转180度
pil_im.paste(region,box)  # 放回原图
调整图像的尺寸
pil_im.resize((64,128))  # w,h 长宽比可以改变
旋转图像
pil_im.rotate(45)  # 逆时针
查看和保存
pil_im.show()  # 在图像查看器中显示
# pil_im.save('02.jpg')  # save保存处理后的图片
# return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]  # 返回目录中所有 JPG 图像的文件名列表

Matplotlib

from PIL import Image
from pylab import *
matplotlib.use('TkAgg')
在图像上画点、线
# 读取图像到数组中
im = array(Image.open('01.png'))
# 绘制图像
imshow(im)

# 4个点
x = [100,100,400,400]
y = [200,500,200,500]
# 使用红色星状标记绘制点
plot(x,y,'r*')

# 绘制连接前两个点的线,默认为蓝色
# '-' 实线,'--' 虚线,':' 点线;'.' 点,'o' 圆圈,'s' 正方形,'*' 星形,'+' 加号,'x' 叉号
plot(x[:2],y[:2])  # x列表,y列表,而非(x0,y0),(x1,y1)...
plot(x[1:3],y[1:3],'go-') # 带有圆圈标记的绿线
plot(x[2:],y[2:],'ks:') # 带有正方形标记的黑色点线

# 添加标题,显示绘制的图像
title('Plotting')
axis('off')  # 去除坐标轴
show()  # 每个脚本只能调用一次
获取图像轮廓和直方图
# 将图像灰度化
im = array(Image.open('01.png').convert('L'))

# 新建一个图像
figure()  # Figure 是图形窗口的抽象

subplot(211)  # 创建子图
gray()  # 不使用颜色信息
# 在原点的左上角显示轮廓图像
contour(im, origin='image')
axis('equal')  # 设置 x 和 y 轴的比例相等
axis('off')

subplot(212)
# 绘制直方图: 该图像像素值的分布情况
# hist()只接受一维数组作为输入,flatten()将任意数组按照行优先转换成一维数组
# 第二个参数指定灰度区间的数目,值越小长方形越宽
hist(im.flatten(),64)  
show()
交互式标注
im = array(Image.open('01.png'))
imshow(im)
print ('Please click 3 points')
x = ginput(3)
print ('you clicked:',x)
show()
# 报错:当前的用户接口后端不支持前端显示并交互
# 解决:matplotlib.use('TkAgg') # 显式指明使用的后端

NumPy

from PIL import Image
from numpy import *
from pylab import *
用数组表示图像

多维数组像是列表的列表,数组内的元素数据类型相同

im = array(Image.open('01.png'))
print (im.shape, im.dtype)  #(行、列、颜色通道),png多一个透明通道(𝛼通道)

im = array(Image.open('01.png').convert('L'),'f')  # 指定'f',将数据类型转换为浮点型
print (im.shape, im.dtype)
灰度变换

图片灰度通常为0-255(8位灰度图像),0表示黑色

im = array(Image.open('01.png').convert('L'))
# print (int(im.min()), int(im.max()))  # 输出图像中的最小和最大像素值
h,w = im.shape[0:2] # 获取图像的大小

im2 = 255 - im  # 对图像进行反相处理
im3 = (100.0/255) * im + 100  # 将图像像素值变换到 100...200 区间
im4 = 255.0 * (im/255.0)**2  # 对图像像素值求平方后得到的图像
数组转回图像
pil_im = Image.fromarray(im4)  # 将uint8数组转换为PIL,array()的反操作
# pil_im = Image.fromarray(uint8(im))  # 不确定数据的类型时,先转为uint8
pil_im.show()
# imshow(im2.reshape(h,w))  # 或使用 reshape()将一维数组转为二维图像
# show()
直方图均衡化

使变换后的图像中每个灰度值的分布概率都相同

增强图像的对比度,图像灰色区域的细节变得清晰

def histeq(im,nbr_bins=256):  
    """ 对一幅灰度图像进行直方图均衡化:
        input: im,灰度图; nbr_bins,灰度区间的数目
        output: im2,直方图均衡化后的图像; cdf,用来做像素值映射的累积分布函数  """
    # 计算图像的直方图
    imhist,bins = histogram(im.flatten(),nbr_bins)
    cdf = imhist.cumsum()  # 累积分布函数
    cdf = 255 * cdf / cdf[-1]  # 归一化
    # 使用累积分布函数的线性插值,计算新的像素值
    im2 = interp(im.flatten(),bins[:-1],cdf)
    return im2.reshape(im.shape), cdf
  
im = array(Image.open('01.png').convert('L'))
im2,cdf = histeq(im)
图像平均

图像大小相同,则直接相加,然后除以图像的数目;减少噪声

# 打开第一幅图像,将其存储在浮点型数组中
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)
主成分分析

主成分:协方差矩阵对应最大特征值的特征向量, 对数据影响最大的特征

投影矩阵 V 的每行向量都是正交的,包含了训练数据方差依次减少的坐标方向

def pca(X):
    """ 主成分分析:
    输入:矩阵 X ,其中该矩阵中存储训练数据,每一行为一条训练数据
          一行表示一幅图像
    返回:投影矩阵(按照维度的重要性排序)、方差和均值 """
  
    # 获取维数
    num_data,dim = X.shape  # num_data数据点的数量,dim数据的维度
    # 数据中心化
    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]):  # range()返回一个列表,arange()返回一个数组,xrange()返回一个产生器
            V[:,i] /= S
    else:
        # PCA-使用SVD方法(奇异值分解)
        U,S,V = linalg.svd(X)
        V = V[:num_data]  # 仅仅返回前 nun_data 维的数据才合理
    # 返回投影矩阵、方差和均值
    return V,S,mean_X
使用pickle模块

封装:接受python对象,将其转换为字符串表示(写入.pkl文件)

拆封:从字符串表示中重构该对象

import pickle

# 保存均值和主成分数据
V,S,immean = pca(X)
with open('font_pca_modes.pkl', 'wb') as f:  # w写,b二进制,r读,a文件末尾
    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)
读写文本文件
savetxt('test.txt',x,'%i')
x = loadtxt('test.txt')

SciPy

图像模糊

高斯模糊:将(灰度)图像I和一个高斯核进行卷积操作

\qquad $ I_{\sigma}=I\ast G_{\sigma}$

\qquad $ G_{\sigma}=\frac{1}{2\pi{\sigma}2}e{-(x2+y2)/2{\sigma}^2} ,标准差为 ,标准差为 ,标准差为\sigma$的二维高斯核

\qquad σ \sigma σ越大,处理后的图像细节丢失越多

from PIL import Image
from numpy import *
from scipy import ndimage  
im = array(Image.open('01.png'))
im2 = ndimage.gaussian_filter(im,2)  # guassian_filter()的最后一个参数表示标准差𝜎
# 报错:使用scipy.ndimage.gaussian_filter而非scipy.ndimage.filters.gaussian_filter
# 解决:filters模块已不再使用,scipy.ndimage.gaussian_filter直接对彩色图像进行高斯模糊,不需要逐通道遍历操作
图像导数

图像 I I I的梯度向量: ∇ I = [ I x , I y ] T \nabla{I}={[I_x,I_y]}^T I=[Ix,Iy]T

\qquad 梯度的大小: ∣ ∇ I ∣ = I x 2 + I y 2 |\nabla{I}|=\sqrt{{I_x}^2+{I_y}^2} ∣∇I=Ix2+Iy2 ,描述图像强度大小的变化

\qquad 梯度的角度: α = arctan ⁡ 2 ( I y , I x ) \alpha=\arctan2(I_y,I_x) α=arctan2(Iy,Ix),描述图像中在每个点(像素)上强度变化最大的方向

通过卷积以离散近似的方式计算图像的导数:

\qquad I x = I ∗ D x I_x=I\ast D_x Ix=IDx I y = I ∗ D y I_y=I\ast D_y Iy=IDy

\qquad 对于 D x D_x Dx D y D_y Dy,通常选择 Prewitt 滤波器,或者 Sobel 滤波器

上述滤波器的尺度需要随着图像分辨率的变化而变化,要在任意尺度上计算导数,可以使用高斯导数滤波器:

\qquad I x = I ∗ G σ x I_x=I\ast G_{\sigma x} Ix=IGσx I y = I ∗ G σ y I_y=I\ast G_{\sigma y} Iy=IGσy

im = array(Image.open('01.png').convert('L'))
# Sobel 导数滤波器
imx = zeros(im.shape)
ndimage.sobel(im,1,imx)  # 1表示x方向
imy = zeros(im.shape)
ndimage.sobel(im,0,imy)  # 0表示y方向

# 高斯导数滤波器
sigma = 1 # 标准差
imx = zeros(im.shape)
ndimage.gaussian_filter(im, (sigma,sigma), (0,1), imx)
imy = zeros(im.shape)
ndimage.gaussian_filter(im, (sigma,sigma), (1,0), imy)

magnitude = sqrt(imx**2+imy**2)
pil_im = Image.fromarray(magnitude)
pil_im.show()
形态学:对象计数

形态学:度量和分析基本形状,通常用于处理二值图像

二值图像:图像的每个像素只能取两个值,通常是 0 和 1

im = array(Image.open('01.png').convert('L'))
im = 1*(im<128)  # 通过阈值化方式来确保该图像是二值图像
labels, nbr_objects = ndimage.label(im)
print ("Number of objects:", nbr_objects)

# 开操作 更好地分离各个对象
# (y,x)表示以一个像素为中心时,使用哪些相邻像素,此处为在y方向上使用9个像素(上面4个、像素本身、下面 4个),在x方向上使用5个
# iterations 决定执行该操作的次数
im_open = ndimage.binary_opening(im,ones((9,5)),iterations=2)
labels_open, nbr_objects_open = ndimage.label(im_open)
print ("Number of objects:", nbr_objects_open)
读写.mat文件
from scipy.io import loadmat,savemat
# 读,返回字典类型dic
data = loadmat('test.mat')
# 写
savemat('test.mat',data)
以图像形式保存数组
from scipy.misc import imsave
imsave('test.jpg',im)

图像去噪

ROF模型

(灰度)图像 I I I的全变差定义为梯度范数之和

\qquad 连续表示: J ( I ) = ∫ ∣ ∇ I ∣ d x J(I)=\int{|\nabla I|}dx J(I)=∣∇Idx

\qquad 离散表示: J ( I ) = ∑ x ∣ ∇ I ∣ J(I)=\sum_{x} {|\nabla I|} J(I)=x∣∇I

ROF 模型的目标函数: min ⁡ U { ∣ ∣ I − U ∣ ∣ 2 + 2 λ J ( U ) } \min_{U}\{{||I-U||}^2+2\lambda J(U)\} minU{∣∣IU∣∣2+2λJ(U)}

\qquad 范数 ∣ ∣ I − U ∣ ∣ ||I-U|| ∣∣IU∣∣度量去噪后图像 U U U和原始图像 I I I的差异

\qquad 本质上该模型使去噪后的图像像素值“平坦”变化,但是在图像区域的边缘上,允许去噪后的图像像素值“跳跃”变化

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值