簡単な紹介
Pythonの従来の画像処理ライブラリPIL
(パイソンイメージング図書館)、それは言うことができる、使いやすい、強力で、基本的に画像を処理するための標準のPythonライブラリです。
しかし、することPIL
も更新するのが遅いのpython3をサポートしていますが、ありません。だから、ボランティアがいるPIL
という名前の、分岐バージョンに基づいて作成されPillow
、Pillow
アクティブな更新、python3.6に最新のサポートをし、多くの新機能が追加されます。だから我々は枕をインストールすることができます。
インストール
Pillow
インストールはピップをインストールすることができ、比較的単純で簡単です。
枕をインストールするPIP
しかし、注意すべき点は、つまりPillow
とPIL
同じ環境で共存させることはできませんので、あなたがインストールしている場合はPIL
、インストールをPillow
する前に削除する必要がありますPIL
。
それがから継承されているのでPIL
、分岐、そのPillow
輸入はこれです:
インポートPILの
#や
からPIL インポート画像
取扱説明書
画像
Image
枕は、枕のほとんどの機能を実装し、最も重要なクラスです。このクラスのインスタンスを作成するには三つの主要な方法があります。
-
ファイルから画像をロードします
-
得るために、他の画像処理
-
新しい画像を作成します。
画像を読みます
次のように一般的に言えば、我々はすべて、このクラスのインスタンスをロードされたイメージファイルから収集されます。
PIL インポート画像の
画像= 画像。(オープン'happy.png')
画像無しフォーマット場合は、Pillow
自動的にファイル形式のファイルの内容を認識しています。
新しいイメージ
Pillow
新しい使用ブランクイメージnew()
方法は、最初のパラメータ、すなわち、2番目の引数が画像(幅×高さ)の解像度を指定し、第三のパラメータが色であるカラーモードの空間パターンです。
-
あなたは、直接名一般的に使用される色で埋めることができます。「赤」。
-
以下のようにしても、16進数の色の表現で埋めることができる
#FF0000
赤で発現します。 -
タプルは、(255、0、0、255)または(255、0、0)とすると、赤を表し、通過することができます。
画像= 画像。新しい('RGB'、(200、100)、'赤')
画像を保存
画像を保存し、その後、あなたが使用する必要があるsave()
メソッドを:
画像。(保存'happy.png')
時間を節約、画像フォーマットを指定しない場合、Pillow
我々は保存されたファイル形式の拡張子の入力に応じて決定します。
画像の表現を座標
枕において、有する画像左上隅の座標は、(0,0)、これは、x軸の値は、左から右へy軸成長のボトム値へ戻るを増加しています。
我们处理图像时,常常需要去表示一个矩形的图像区域。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()
图像类
这类验证码大多是数字、字母的组合,国内也有使用汉字的。在这个基础上增加噪点、干扰线、变形、重叠、不同字体颜色等方法来增加识别难度。
相应的,验证码识别大体可以分为下面几个步骤
-
灰度处理
-
增加对比度(可选)
-
二值化
-
-
倾斜校正分割字符
-
建立训练库
-
识别
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
-
双峰法
图像由前景和背景组成,在灰度直方图上,前后二景都形成高峰,在双峰之间的最低谷处就是图像的阈值所在。 当前后景的对比较为强烈时,分割效果较好;否则基本无效。
-
迭代法
首先选择一个近似阈值作为估计值的初始值,然后进行分割,产生子图像,并根据子图像的特性来选取新的阈值,再利用新的阈值分割图像,经过几次循环,使错误分割的图像像素点降到最少。这样做的效果好于用初始阈值直接分割图像的效果。
-
求出图象的最大灰度值和最小灰度值,分别记为Pmax和Pmin,令初始阈值T0=(Pmax+Pmin)/2
-
根据阈值TK将图象分割为前景和背景,(小于 T0 的像素部分,大于T0的背景部分),并分别求其均值 avgPix, avgBac
-
求出新阈值TK = ( avgPix+avgBac) / 2;
-
若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()