目录
- Question:模板
- tips:关于直方图的几个操作解释(仅供参考)
- Question21:直方图归一化(Histogram Normalization)
- Question22:直方图操作
- 以上都只是改变直方图范围,没有改变直方图形状
- Question23:直方图均衡
- Question24:gamma校正
- 后面给出代码使用OpenCV提供的API操作,想看作者如何直接操作图像而不利用API请去原项目查看[「画像処理100本ノック」中文版本!](https://github.com/gzr2017/ImageProcessing100Wen)
- Question25:最邻近插值
- Question26:双线性插值
- Question27:双三次插值
- Question28:仿射变换--平移
- Question29:仿射变换--放大缩小
- Question30:仿射变换-旋转
Question:模板
这是一个Question的模块框架
代码:
print("这是一个Question的模块框架")
tips:关于直方图的几个操作解释(仅供参考)
Question21:直方图归一化(Histogram Normalization)
将直方图所有分量限制在一定范围。虽然有0 ~ 255共256个分级,但是一些图片实际上只分布在一部分上,比如某些图片灰度级只分布在10 ~ 160中,减小了像素中的差异化,使得照片看起来灰蒙蒙的效果。
用严肃点的话术讲就是动态范围低(最大值:最小值),直方图归一化也叫灰度变换。如下图所示,将绿色是某原图的像素分布,浅绿色是经直方图归一化后的像素分布,可见动态范围变大了,图像中的差异化也也明显了。
归一化原理也简单,将10变为0,将160变为255,中间部分等比例扩大即可,公式如下:
- 将范围从[c,d]变换到[a,b]
如图,这是极端情况,所以通用的变换公式是上方给出的公式,该灰度变换时,按等比例扩展到[a,b],但是起点在a处,所以结果要+a;同时变换后的存在[<a]和[>b]的部分,而我们要求到[a,b],所以[<a]的全部置为a,[>b]的全部置为b
。
代码:
#
import numpy as np
import cv2
import matplotlib.pyplot as plt
def histNorm(gray,a=0,b=255):
c=gray.min()
d=gray.max()
out=gray.copy()
out=(b-a)/(d-c)*(out-c)+a
out[out<a]=a
out[out>b]=b
out=out.astype(np.uint8)
return out
img=cv2.imread("../imori_dark.jpg")
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
out =histNorm(gray)
cv2.imshow("",out)
cv2.waitKey(0)
Question22:直方图操作
改变图片的均值和标准差到指定均值和标准差,比如图片的均值和标准差为[m,s],先将其改变为[m0,s0],公式如下:
在深度学习中通常出现out=(img-127)/128
的操作,实际上对于这是将图片改变到均值为0,标准差为1.
代码:
import numpy as np
import cv2
def getImageMS(img):
m=np.mean(img)
s=np.sqrt(np.var(img))
return m,s
def imageMS(img,m0,s0):
m,s=getImageMS(img)
out=img.copy()
out=s0/s*(out-m)+m0
out=out.astype(np.uint8)
return out
img=cv2.imread("../imori_dark.jpg")
out=imageMS(img,128,52)
cv2.imshow("img",img)
cv2.imshow("out",out)
cv2.waitKey(0)
以上都只是改变直方图范围,没有改变直方图形状
Question23:直方图均衡
仅仅依靠输入图像的直方图信息,就可以得到一个变换函数,利用该变换函数可以将输入图像达到上述效果,该过程就是直方图均衡化。
也就是前面几种方法都是依靠最大值、最小值、均值和标准差的额外信息完成操作,而直方图均衡仅利用直方图信心完成直方图均衡化。
公式如下:
- Zmax:最大像素值
- S:像素总数
- h(i):像素值为0~i的像素总个数
公式表达:像素值为i
的像素的新的值为z’
import numpy as np
import cv2
import matplotlib.pyplot as plt
def hist_equal(img,z_max=255):
h,w,c=img.shape
s=h*w*c
sum_h=0
out=img.copy()
for i in range(255):
ind=np.where(img==i)
sum_h+=len(img[ind])
z=z_max/s*sum_h
out[ind]=z
out=out.astype(np.uint8)
return out
img=cv2.imread("../imori.jpg")
out=hist_equal(img)
plt.hist(out.ravel(), bins=255, rwidth=0.8, range=(0, 255))
plt.show()
cv2.imshow("",out)
cv2.waitKey(0)
Question24:gamma校正
一种非线性校正,转换公式如下:
I
i
n
I_{in}
Iin是图像范围限定在[0,1]之间的,通过控制c和g
来进行校正。
使用c=1,g=2.2的gamma校正:
可以看到像素值是变大了的,同时暗部提亮的值比亮部提亮的值更大。
代码:
import numpy as np
import cv2
import matplotlib.pyplot as plt
def gammaProcess(img,c=1,g=2.2):
out=img.copy()
out=1/c*(img/255)**(1/g)*255
return out.astype(np.uint8)
img=cv2.imread("../imori_gamma.jpg")
# 展示原图的直方图
plt.hist(img.ravel(), bins=255, rwidth=0.8, range=(0, 255))
plt.show()
out=gammaProcess(img) # gamma校正
# 展示校正后直方图
plt.hist(out.ravel(), bins=255, rwidth=0.8, range=(0, 255))
plt.show()
cv2.imshow("",out)
cv2.waitKey(0)
后面给出代码使用OpenCV提供的API操作,想看作者如何直接操作图像而不利用API请去原项目查看「画像処理100本ノック」中文版本!
Question25:最邻近插值
这是图像方法的算法,关于更多插值放大请移步:图像方法之内插法
opencv中关于图像尺寸操作的方法是:
cv2.resize(img,dsize,fx,fy,interpolation)
- dsize:将图像缩放到什么尺寸,比如(500,500)即将img缩放到(500,500)
- fx和fy:行和列的缩放比例因子,比如fx=2,fy=2即将img长宽各放大2倍
- interpolation:插值方法,效果越好,速度越慢,可选如下:
interpolation | 描述 |
---|---|
cv2.INTER_NEAREST | 最近邻插值 |
cv2.INTER_LINEAR | 双线性插值(默认) |
cv2.INTER_CUBIC | 双三次插值 |
cv2.INTER_AREA | 使用像素区域关系重新采样(推荐) |
# 采用最邻近插值法,将图片img缩放大长和宽的0.7倍
img_small=cv2.resize(img,None,fx=0.7,fy=0.7,interpolation=cv2.INTER_NEAREST)
# 采用最邻近插值法,直接缩放到(256,256)
img_256=cv2.resize(img,(256,256),interpolation=cv2.INTER_NEAREST)
Question26:双线性插值
双线性插值其实就是根据位置所在比例来估计像素值,比最邻近插值更加有道理一点,当然计算量更大,效果更好。
代码:
import cv2
img=cv2.imread("../imori.jpg")
# 将img放大2倍,插值方法为双线性
out=cv2.resize(img,dsize=None,fx=2,fy=2,interpolation=cv2.INTER_LINEAR)
cv2.imshow("",out)
cv2.waitKey(0)
Question27:双三次插值
速度更慢,效果更好(画质有提升,但是不太明显,放大倍数越大越明显)
代码:
import cv2
img=cv2.imread("../imori.jpg")
# 将img放大3倍,插值方法为双线性
out=cv2.resize(img,dsize=None,fx=3,fy=3,interpolation=cv2.INTER_LINEAR)
# 将img放大3倍,插值方法为双三次
out2=cv2.resize(img,dsize=None,fx=3,fy=3,interpolation=cv2.INTER_CUBIC)
# 展示双线性和双三次放大的图像
cv2.imshow("INTER_LINEAR",out)
cv2.imshow("INTER_CUBIC",out2)
cv2.waitKey(0)
Question28:仿射变换–平移
利用仿射变换让图像在x方向上+30,在y方向上-30吧
什么是仿射变换
如何利用利用仿射变换让图像在x方向上+30,在y方向上-30?看上方链接的栗子5-1
代码:
# 定义原图中三个点pts1
pts1=np.float32(([0,0],[0,127],[127,127]))
# 定义结果图中三个点pts2
pts2=np.float32(([30,0],[30,97],[127,97]))
# 利用这三对点,求出变换矩阵M
M=cv2.getAffineTransform(pts1,pts2)
# 再利用变换矩阵对原图所有像素点进行变换
# 并指定了输出图像的shape和原图shape一样
res=cv2.warpAffine(img,M,dsize=img.shape[0:2])
cv2.imshow("",res)
cv2.waitKey(0)
Question29:仿射变换–放大缩小
Question30:仿射变换-旋转
使用仿射变换,使图像逆时针旋转30°
我们知道通过仿射矩阵可以使图像进行仿射变换效果,上述是通过3组对应点来求得变换矩阵,实际上我们可以直接确定变换矩阵:
- A:旋转角度
- t:平移距离
这在文章图像仿射变换第4小节列举过常用变换矩阵。
通过角度我们能够计算出矩阵M:
效果(旋转30°,不进行偏移):
效果(旋转30°,横轴左偏移20,纵轴下偏移20):
代码:
import numpy as np
import cv2
img=cv2.imread("../imori.jpg")
tx=-20 # 控制横轴偏移
ty=20 # 控制纵轴偏移
A=-30 # 旋转角度
M=np.array([[np.cos(A*np.pi/180),-np.sin(A*np.pi/180),tx],[np.sin(A*np.pi/180),np.cos(A*np.pi/180),ty]])
res=cv2.warpAffine(img,M,dsize=img.shape[0:2])
res=res.astype(np.uint8)
cv2.imshow("",res)
cv2.waitKey(0)