15-python python crawler base image processing, PIL library

Python image processing -Pillow

 

Brief introduction

Python conventional image processing libraries PIL(Python Imaging Library), it can be said is basically the standard Python library to process images, powerful, easy to use.

However, due to PILnot support Python3, but also slow to update. So volunteers have PILcreated on the basis of a branch version, named Pillow, Pillowthe latest support to python3.6, update active, and adds many new features. So we can install Pillow.

 

installation

PillowInstallation is relatively simple and straightforward pip can be installed:

pip install Pillow

But the point to note is that Pillowand PILcan not coexist in the same environment, so if you have installed PIL, then the installation Pillowshould be removed before PIL.

Because it is inherited from PILthe branch, so Pillowthe import is this:

Import PIL 
# or
from PIL Import Image

 

manual

Image

ImagePillow is the most important class that implements most of the functionality of the Pillow. To create an instance of this class there are three main ways:

  1. Load an image from a file

  2. Other image processing to obtain

  3. Create a new image

Read image

Generally speaking, we are all collected from the image file is loaded instance of this class, as follows:

from PIL import Image
picture = Image.open('happy.png')

If no picture format, then Pillowautomatically recognize the file contents of the file format.

New Image

PillowBlank image using the new new()method, the first parameter is the spatial pattern of color mode i.e., the second argument specifies the resolution of the image (width x height), the third parameter is the color.

  • You can directly fill in the name commonly used colors. The 'red'.

  • You can also fill in hexadecimal color representation, as #FF0000expressed in red.

  • Tuples can pass, such as (255, 0, 0, 255) or (255, 0, 0) represents red.

picture = Image.new('RGB', (200, 100), 'red')

Save image

Save the picture, then you need to use the save()method:

picture.save('happy.png')

Save time, if not specified picture format, then Pillowwe will decide according to the saved file format extension input.

 

Coordinate representation of the image

In Pillow, the image with the upper-left corner coordinates are (0,0), this means, the x-axis values increase from left to right, top to bottom value of the y-axis growth.

我们处理图像时,常常需要去表示一个矩形的图像区域。Pillow中很多方法都需要传入一个表示矩形区域的元祖参数。

这个元组参数包含四个值,分别代表矩形四条边的距离X轴或者Y轴的距离。顺序是(左,顶,右,底)。其实就相当于,矩形的左上顶点坐标为(左,顶),矩形的右下顶点坐标为(右,底),两个顶点就可以确定一个矩形的位置。

右和底坐标稍微特殊,跟python列表索引规则一样,是左闭又开的。可以理解为[左, 右)[顶, 底)这样左闭右开的区间。比如(3, 2, 8, 9)就表示了横坐标范围[3, 7];纵坐标范围[2, 8]的矩形区域。

 

常用属性

  • PIL.Image.filename

    图像源文件的文件名或者路径,只有使用open()方法创建的对象有这个属性。

    类型:字符串

  • PIL.Image.format

    图像源文件的文件格式。

  • PIL.Image.mode

    图像的模式,一般来说是“1”, “L”, “RGB”, 或者“CMYK” 。

  • PIL.Image.size

    图像的大小

  • PIL.Image.width

    图像的宽度

  • PIL.Image.height

    图像的高度

  • PIL.Image.info

    图像的一些信息,为字典格式

 

常用方法

裁剪图片

Image使用crop()方法来裁剪图像,此方法需要传入一个矩形元祖参数,返回一个新的Image对象,对原图没有影响。

croped_im = im.crop((100, 100, 200, 200))

复制与粘贴图像

复制图像使用copy()方法:

copyed_im = im.copy()

粘贴图像使用paste()方法:

croped_im = im.crop((100, 100, 200, 200))
im.paste(croped_im, (0, 0))

im对象调用了paste()方法,第一个参数是被裁剪下来用来粘贴的图像,第二个参数是一个位置参数元祖,这个位置参数是粘贴的图像的左顶点。

调整图像的大小

调整图像大小使用resize()方法:

resized_im = im.resize((width, height))

resize()方法会返回一个重设了大小的Image对象。

或者使用thumbnail()方法

im = Image.open('test.jpg')
#获得图像尺寸
w, h = im.size  
# 缩放到50%
im.htumbnail((w//2, h//2))  
#显示图片
im.show()  

 thumbnail() 方法可以用来制作缩略图。它接受一个二元数组作为缩略图的尺寸,然后将示例缩小到指定尺寸

旋转图像和翻转图像

旋转图像使用rotate()方法,此方法按逆时针旋转,并返回一个新的Image对象:

# 逆时针旋转90度
im.rotate(90)
im.rotate(180)
im.rotate(20, expand=True)

旋转的时候,会将图片超出边界的边角裁剪掉。如果加入expand=True参数,就可以将图片边角保存住。

翻转图像使用transpose()

# 水平翻转
im.transpose(Image.FLIP_LEFT_RIGHT)
# 垂直翻转
im.transpose(Image.FLIP_TOP_BOTTOM)

 

获得图片通道名称

im.getbands()

通过通道分割图片

split()

split()可以将多通道图片按通道分割为单通道图片。返回各个通道的灰度图组成的元组

R, G, B = im.split()

split()方法返回的是一个元祖,元祖中的元素则是分割后的单个通道的图片。

getchannel(channel)

getchannel()可以获取单个通道的图片:

R = im.getchannel("R")

模式转化

img = im.convert("L")   

获取单个像素的值

使用getpixel(xy)方法可以获取单个像素位置的值:

im.getpixel((100, 100))

传入的xy需要是一个元祖形式的坐标。

如果图片是多通道的,那么返回的是一个元祖。

加载图片全部数据

我们可以使用load()方法加载图片所有的数据,并比较方便的修改像素的值:

pixdata = im.load()
pixdata[100,200] = 255

此方法返回的是一个PIL.PyAccess,可以通过这个类的索引来对指定坐标的像素点进行修改。

获取全部像素内容

getdata(band = None) 方法,用来获取 Image 类的对象中的像素内容

该方法会将图片中的像素内容,逐行逐行地拼接起来,作为一个完整的序列返回。方法的返回类型,是 PIL 库的内部类型。我们可以用 list(im.getdata()) 得到标准的 Python list 对象。

band 意味「通道」。当 band = None 时,方法返回所有通道的像素内容;当 band = 0时,则返回第一个通道的像素内容。例如,对于 RGB 模式的位图,band = 0 返回 R 通道的内容;band = 2 返回 B 通道的内容。

from PIL import Image

im = Image.open('test.jpg')
print(im.getdata())  #获取所有通道的值 类似生成器的对象
print(list(im.getdata(0)))  #获取第一个通道的值, 转化为列表

####

关闭图片并释放内存

此方法会删除图片对象并释放内存

im.close()

 

图像类

这类验证码大多是数字、字母的组合,国内也有使用汉字的。在这个基础上增加噪点、干扰线、变形、重叠、不同字体颜色等方法来增加识别难度。

相应的,验证码识别大体可以分为下面几个步骤

  1. 灰度处理

  2. 增加对比度(可选)

  3. 二值化

  4. 降噪

  5. 倾斜校正分割字符

  6. 建立训练库

  7. 识别

 

0. 灰度化

像素点是最小的图像单元,一张图片由好多的像素点构成, 一个像素点的颜色是由RGB三个值来表现的,所以一个像素点矩阵对应三个颜色向量矩阵,我们对图像的处理就是对这个像素点矩阵的操作,想要改变某个像素点的颜色,只要在这个像素点矩阵中找到这个像素点的位置(x, y),因为一个像素点的颜色由红、绿、蓝三个颜色变量表示,所以我们通过给这三个变量赋值,来改变这个像素点的颜色.

图片的灰度化,就是让像素点矩阵中的每一个像素点都满足下面的关系:R=G=B,此时的这个值叫做灰度值.

灰度化的转化公式一般为:

R = G = B = 处理前的 R*0.3 + G*0.59 + B*0.11

img = img.convert('L')  #转为灰度图

 

1. 二值化

二值化就是让图像的像素点矩阵中的每个像素点的灰度值为0(黑)或者255(白) ,从而实现二值化,让整个图像呈现只有黑和白的效果。

原理是利用设定的一个阈值来判断图像像素为0还是255,小于阈值的变为0(黑色), 大于的变为255(白色)。

这个临界灰度值就被称为阈值,阈值的设置很重要。阈值过大或过小都会对图片造成损坏。

选择阈值的原则是:既要尽可能保存图像信息,又要尽可能减少背景和噪声的干扰,

常用方法

  • 取阈值为127(0~255的中数,(0+255)/2=127 )

    好处是计算量小速度快,

    缺点也是很明显的 ,对于图片中内容色彩分布较大的图片,很容易造成内容的缺失。

  • 平均值法

    计算像素点矩阵中的所有像素点的灰度值的平均值avg

    (像素点1灰度值+...+像素点n灰度值)/ n = 像素点平均值avg

    这样做比方法1好一些。 但可能导致部分对象像素或者背景像素丢失。

def averageThreshold(img):
   pixdata = img.load()
   width,height = img.size
   
   threshold = sum(img.getdata())/(width*height)   #计算图片的平均阈值

   # 遍历所有像素,大于阈值的为白色
   for y in range(height):
       for x in range(width):
           if pixdata[x, y] < threshold:
               pixdata[x, y] = 0
           else:
               pixdata[x,y] = 255

   return img

 

  • 双峰法

    图像由前景和背景组成,在灰度直方图上,前后二景都形成高峰,在双峰之间的最低谷处就是图像的阈值所在。 当前后景的对比较为强烈时,分割效果较好;否则基本无效。

  • 迭代法

    首先选择一个近似阈值作为估计值的初始值,然后进行分割,产生子图像,并根据子图像的特性来选取新的阈值,再利用新的阈值分割图像,经过几次循环,使错误分割的图像像素点降到最少。这样做的效果好于用初始阈值直接分割图像的效果。

    1. 求出图象的最大灰度值和最小灰度值,分别记为Pmax和Pmin,令初始阈值T0=(Pmax+Pmin)/2

    2. 根据阈值TK将图象分割为前景和背景,(小于 T0 的像素部分,大于T0的背景部分),并分别求其均值 avgPix, avgBac

    3. 求出新阈值TK = ( avgPix+avgBac) / 2;

    4. 若T0=TK,则所得即为阈值;否则转2,迭代计算 。

from PIL import Image

def iterGetThreshold(img, pixdata, width, height):
   pixPrs = pixBac = []                     #用于统计前景和背景平均阈值
   threshold = 0
   pixel_min, pixel_max = img.getextrema()  # 获得图片中最大和最小灰度值
   newThreshold = int((pixel_min + pixel_max) / 2)  # 初始阈值

   while True:
       if abs(threshold -  newThreshold) < 5:   #差值小于5,退出
           break
       for y in range(height):
           for x in range(width):
               if pixdata[x, y] >= newThreshold:
                   pixBac.append(pixdata[x,y])    #大于阈值 为背景
               else:
                   pixPrs.append(pixdata[x,y])    #小于, 前景

       avgPrs = sum(pixPrs)/len(pixPrs)
       avgBac = sum(pixBac)/len(pixBac)
       threshold = newThreshold
       newThreshold = int((avgPrs+avgBac)/2)

   return newThreshold


def binary(img, threshold=None):
   img = img.convert('L')  #转为灰度图
   pixdata = img.load()
   width, height = img.size

   if not threshold:
       threshold = iterGetThreshold(img, pixdata,width, height)
   # 遍历所有像素,大于阈值的为白色
   for y in range(height):
       for x in range(width):
           if pixdata[x, y] < threshold:
               pixdata[x, y] = 0
           else:
               pixdata[x,y] = 255

   return img

img = Image.open('test-1.jpg')
img.show()
new_img = Binary(img)
new_img.show()

 

2. 降噪

从前面经过二值化处理,如果一个像素点是图片或者干扰因素的一部分,那么它的灰度值一定是0,即黑色; 如果一个点是背景,则其灰度值应该是255,白色。

因此对于孤立的噪点,其周围应该都是白色,或者大多数点都是白色pixel

如果图片分辨率够高,一个噪点实际上可能是有很多个点组成 ,所以此时的判断条件应该放宽,即一个点是黑色的并且相邻的8个点为白色点的个数大于一个固定值,那么这个点就是噪点 。

常见的4邻域、8邻域算法。所谓的X邻域算法,可以参考手机九宫格输入法,按键5为要判断的像素点,4邻域就是判断上下左右,8邻域就是判断周围8个像素点。如果这4或8个点中255的个数大于某个阈值则判断这个点为噪音,阈值可以根据实际情况修改。

这个方法对小噪点比较好,如果阀值设的比较大,很多验证码字符也会受到很大影响,因为验证码可能就是一些断断续续的点连出来的,阀值设太大,尽管噪点没了,验证码也会没了。

def depoint(img, N=2):
   pixdata = img.load()
   width, height = img.size
   for y in range(1, height - 1):
       for x in range(1, width - 1):
           count = 0
           if pixdata[x, y - 1] == 255:  # 上
               count = count + 1
           if pixdata[x, y + 1] == 255:  # 下
               count = count + 1
           if pixdata[x - 1, y] == 255:  # 左
               count = count + 1
           if pixdata[x + 1, y] == 255:  # 右
               count = count + 1

           # if pixdata[x-1, y-1] == 255: #左上
           #     count = count + 1
           # if pixdata[x+1, y-1] == 255: #右上
           #     count = count + 1
           # if pixdata[x-1, y+1] == 255: #左下
           #     count = count + 1
           # if pixdata[x+1, y+1] == 255: #右下
           #     count = count + 1

           if count > N:
               pixdata[x, y] = 255  #设置为白色
   return img

depoint(img).show()

 

Guess you like

Origin www.cnblogs.com/winfun/p/10985560.html