#python+Opencv 直方图
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
#读取灰度图
img = cv.imread('G:\\sundries\\CVpictures\\pocketmon2.jpg',0)
#查找直方图
hist = cv.calcHist([img],[0],None,[256],[0,256])
'''
cv.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])
1.images:它是uint8或float32类型的源图像。它应该放在方括号中,即“ [img]”。
2.channels:也以方括号给出。它是我们计算直方图的通道的索引。例如,如果输入为灰度图
像,则其值为[0]。对于彩色图像,您可以传递[0],[1]或[2]分别计算蓝色,绿色或红色通道的
直方图。
3.mask:图像掩码。为了找到完整图像的直方图,将其指定为“无”。但是,如果要查找图像特
定区域的直方图,则必须为此创建一个掩码图像并将其作为掩码。
4.histSize:这表示我们的BIN计数。需要放在方括号中。对于全尺寸,我们通过[256]。
5.ranges:这是我们的RANGE。通常为[0,256]
'''
#还可以用Numpy中的np.histogram(img_gray.ravel(),256,[0,256])
#也可以用np.bincount(img_gray.ravel(),minlength = 256),要设置minlength为256
#绘制直方图
#1.使用Matplotlib
plt.hist(img.ravel(),256,[0,256])
plt.show()
#2.使用matplotlib的法线图(比较适合BGR图)
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('G:\\sundries\\CVpictures\\pocketmon2.jpg')
color = ('b','g','r')
#先找到直方图数据
for i,col in enumerate(color):
histr = cv.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()
#3.使用OpenCV
# create a mask,创建一个掩膜
mask = np.zeros(img.shape[:2], np.uint8)
#第100-300行、100-400列为白色
mask[100:300, 100:400] = 255
#将original image与掩膜结合,此时得到原图的100-300行、100-400列没有被遮盖
masked_img = cv.bitwise_and(img,img,mask = mask)
# 计算掩码区域和非掩码区域的直方图
# 检查作为掩码的第三个参数
hist_full = cv.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv.calcHist([img],[0],mask,[256],[0,256])
plt.subplot(221),plt.imshow(img, 'gray')
plt.subplot(222),plt.imshow(mask,'gray')
plt.subplot(223),plt.imshow(masked_img, 'gray')
plt.subplot(224),plt.plot(hist_full),plt.plot(hist_mask)
plt.xlim([0,256])
plt.show()
##关于位操作的补充
(图是偶然间看到的,仅供参考,原作者联系可删)
预期效果:
流程:
#直方图均衡(提高图像的对比度)
#直方图均衡
#理论(from opencv-python Toturial)
#Numpy操作
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('G:\\sundries\\CVpictures\\pocketmon2.jpg',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
'''
cumsum的用法介绍:
cumsum(a, axis=None, dtype=None, out=None)
a.cumsum(axis=None, dtype=None, out=None)
返回:沿着指定轴的元素 累加和 所组成的数组,其形状应与输入数组a一致
其中cumsum函数的参数:
a:数组
axis:轴索引,整型,若a为n维数组,则axis的取值范围为[0,n-1]
dtype:返回结果的数据类型,若不指定,则默认与a一致。
out:数据类型为数组。用来放置结果的替代输出数组,它必须具有与输出结果具有相同的形状和缓冲长度
'''
cdf_normalized = cdf * float(hist.max()) / cdf.max()
'''
normalized,图像归一化
cv2.normalize(src[, dst[, alpha[, beta[, norm_type[, dtype[, mask]]]]]]) → dst
src:输入数组
dst:输出数组
alpha: range normalization模式的最小值
beta: range normalization模式的最大值,不用于norm normalization(范数归一化)模式。
normType: 归一化的类型,可以有以下的取值:
NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。
NORM_INF: 此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)
NORM_L1 : 归一化数组的L1-范数(绝对值的和)
NORM_L2: 归一化数组的(欧几里德)L2-范数
dtype:dtype为负数时,输出数组的type与输入数组的type相同;否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype).
mask:操作掩膜,用于指示函数是否仅仅对指定的元素进行操作
更多归一化的解释可以参考这个博文:https://blog.csdn.net/lanmeng_smile/article/details/49903865
水平有限,没法具体解释
'''
```python
```python
```python
#绘图并观察图片的直方图像素强度的分布
plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
plt.show()
#你可以看到直方图位于较亮的区域。我们需要全频谱。为此,我们需要一个转换函数,将亮区域的输入像素映射到整个区域的输出像素。这就是直方图均衡化的作用
'''
现在我们找到最小的直方图值(不包括0),并应用wiki页面中给出的直方图均衡化方程。但我在这里
用过,来自Numpy的掩码数组概念数组。对于掩码数组,所有操作都在非掩码元素上执行。您可
以从Numpy文档中了解更多关于掩码数组的信息。
'''
cdf_m = np.ma.masked_equal(cdf,0)
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
cdf = np.ma.filled(cdf_m,0).astype('uint8')
#现在我们有了查找表,该表为我们提供了有关每个输入像素值的输出像素值是什么的信息。因此,我们仅应用变换
img2 = cdf[img]
这样我们就能看到均衡过的图了
图片因格式问题就不放出来了,官方文件里有
#Opencv的直方图均衡
#全局均衡
img = cv.imread('wiki.jpg',0)
equ = cv.equalizeHist(img)
res = np.hstack((img,equ)) #stacking images side-by-side
cv.imwrite('res.png',res)
#进行全局均衡可能导致部分细节处理不到位
'''
因此,为了解决这个问题,使用了**自适应直方图均衡**。
在这种情况下,图像被分成称为“tiles”的小块(在OpenCV中,tileSize默认为 8x8 )。
然后,像往常一样对这些块中的每一个进行直方图均衡。因此,在较小的区域中,直方图将限制在一个较小的区域中(除非存在噪声)。
如果有噪音,它将被放大。为了避免这种情况,应用了对比度限制。
如果任何直方图bin超出指定的对比度限制(在OpenCV中默认为40),则在应用直方图均衡之前,将这些像素裁剪并均匀地分布到其他bin。
均衡后,要消除图块边界中的伪影,请应用双线性插值。
'''
#下面的代码片段显示了如何在OpenCV中应用CLAHE
import numpy as np
import cv2 as cv
img = cv.imread('tsukuba_l.png',0)
# create a CLAHE object (Arguments are optional).
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
cv.imwrite('clahe_2.jpg',cl1)
关于双线性插值可以参考这个大佬的文章:
大佬的双线性插值解释
还有这个实例也不错:
#coding=utf-8
import cv2
import numpy as np
'''双线性插值'''
img = cv2.imread('timg.jpeg', cv2.CV_LOAD_IMAGE_GRAYSCALE) # load the gray image
cv2.imwrite('img.jpg', img)
h, w = img.shape[:2]
```python
# shrink to half of the original
a1 = np.array([[0.5, 0, 0], [0, 0.5, 0]], np.float32)
d1 = cv2.warpAffine(img, a1, (w, h), borderValue=125)
# shrink to half of the original and move
a2 = np.array([[0.5, 0, w /4], [0, 0.5, h / 4]], np.float32)
d2 = cv2.warpAffine(img, a2, (w, h),flags=cv2.INTER_NEAREST,borderValue=125)
# rotate based on d2
a3 = cv2.getRotationMatrix2D((w / 2, h / 2), 90, 1)
d3 = cv2.warpAffine(d2, a3, (w, h),flags=cv2.INTER_LINEAR, borderValue=125)
cv2.imshow('img',img)
cv2.imshow('d1',d1)
cv2.imshow('d2',d2)
cv2.imshow('d3',d3)
cv2.waitKey(0)
cv2.destroyAllWindows()
不喜勿喷,我就当做笔记