opencv:灰色和彩色图像的像素直方图及直方图均值化的实现与展示

直方图及直方图均值化的理论,实现及展示

直方图:

首先,我们来看看什么是直方图:
理论概念:
在图像处理中,经常用到直方图,如颜色直方图、灰度直方图等。 图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所 占的多少。 图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横 坐标是灰度级,纵坐标是该灰度级出现的频率。
在这里插入图片描述
意义:
• 直方图反映了图像中的灰度分布规律。它描述每个灰度级具有的像素个数,但不包含 这些像素在图像中的位置信息。
• 任何一幅特定的图像都有唯一的直方图与之对应,但不同的图像可以有相同的直方图。
• 如果一幅图像有两个不相连的区域组成,并且每个区域的直方图已知,则整幅图像的 直方图是该两个区域的直方图之和
在这里插入图片描述
代码实现:

import cv2
import numpy as np
from matplotlib import pyplot as plt

'''
calcHist—计算图像直方图
函数原型:calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None)
images:图像矩阵,例如:[image]
channels:通道数,例如:0
mask:掩膜,一般为:None
histSize:直方图大小,一般等于灰度级数
ranges:横轴范围
'''
'''
# 灰度图像直方图
# 获取灰度图像
img = cv2.imread("lenna.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#cv2.imshow("image_gray", gray)

# 灰度图像的直方图,方法一
plt.figure()
plt.hist(gray.ravel(), 256)
plt.show()


'''
'''
# 灰度图像的直方图, 方法二
hist = cv2.calcHist([gray],[0],None,[256],[0,256])
plt.figure()#新建一个图像
plt.title("Grayscale Histogram")
plt.xlabel("Bins")#X轴标签
plt.ylabel("# of Pixels")#Y轴标签
plt.plot(hist)
plt.xlim([0,256])#设置x坐标轴范围
plt.show()
'''

#彩色图像直方图
image = cv2.imread("lenna.png")
cv2.imshow("Original",image)
#cv2.waitKey(0)

chans = cv2.split(image)
colors = ("b","g","r")
plt.figure()
plt.title("Flattened Color Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")

for (chan,color) in zip(chans,colors):
    hist = cv2.calcHist([chan],[0],None,[256],[0,256])
    plt.plot(hist,color = color)
    plt.xlim([0,256])
plt.show()

运行结果:
彩色:
在这里插入图片描述
灰色:
在这里插入图片描述

直方图均值化

由上面的输出结果,我们很容易发现,像素的分布是不均匀的,我们需要将像素的分布变得均匀,这就用到了直方图均值化。
概念:
直方图均衡化是将原图像的直方图通过变换函数变为均匀的直方图,然后按均匀直方图修改原 图像,从而获得一幅灰度分布均匀的新图像。
直方图均衡化的作用是图像增强。
步骤:
为了将原图像的亮度范围进行扩展,需要一个映射函数,将原图像的像素值均衡映射到新直 方图中,这个映射函数有两个条件: (1)为了不打乱原有的顺序,映射后亮、暗的大小关系不能改变, (2) 映射后必须在原有的范围内,比如(0-255)
步骤:

  1. 依次扫描原始灰度图像的每一个像素,计算出图像的灰度直方图H
  2. 计算灰度直方图的累加直方图
  3. 根据累加直方图和直方图均衡化原理得到输入与输出之间的映射关系。
  4. 最后根据映射关系得到结果:dst(x,y) = H’(src(x,y))进行图像变换
    理论公式:
    1.对于输入图像的任意一个像素p, p∈[0,255], 总能在输出图像里有对应的像素q, q∈[0,255] 使得下面等式成 立(输入和输出的像素总量相等):
    在这里插入图片描述
    2.其中,输出图像每个灰度级的个数:
    在这里插入图片描述
    3.代入累加直方图公式:
    在这里插入图片描述
    (因为k是从0开始的,所以是乘(q+1),H和W分别为图像像素长和宽)
    最后用一张图来讲解一下:
    在这里插入图片描述
    如左边黄色55图像中,像素值从0-9。
    为其建立一个矩阵,pix值从0-9。
    Ni值为该像素值存在的个数,如在5
    5的图像中,0像素有3个,Ni值为3。
    Pi值为该像素值的概率,在这里插入图片描述
    sunmPi值就是之前Pi的总和,也可以看作sumPi=Pi+sumP(i-1),比如sumP0=P0=0.12,sumP1=sumP0+P1
    最后的结果就是sumPi×255-1(注意:图中忘记减一了)*,将这个结果的数值替换原像素数值。
    代码实现:
import cv2
import numpy as np
from matplotlib import pyplot as plt

'''
equalizeHist—直方图均衡化
函数原型: equalizeHist(src, dst=None)
src:图像矩阵(单通道图像)
dst:默认即可
'''

# # 获取灰度图像
# img = cv2.imread("lenna.png", 1)
# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# #cv2.imshow("image_gray", gray)
#
# # 灰度图像直方图均衡化
# dst = cv2.equalizeHist(gray)
#
# # 直方图
# hist = cv2.calcHist([dst],[0],None,[256],[0,256])
#
# plt.figure()
# plt.hist(dst.ravel(), 256)
# plt.show()
#
# cv2.imshow("Histogram Equalization", np.hstack([gray, dst]))
# cv2.waitKey(0)



# 彩色图像直方图均衡化
img = cv2.imread("lenna.png", 1)
cv2.imshow("src", img)

# 彩色图像均衡化,需要分解通道 对每一个通道均衡化
(b, g, r) = cv2.split(img)
bH = cv2.equalizeHist(b)
gH = cv2.equalizeHist(g)
rH = cv2.equalizeHist(r)
# 合并每一个通道
result = cv2.merge((bH, gH, rH))
cv2.imshow("dst_rgb", result)

cv2.waitKey(0)


chans = cv2.split(result)
colors = ("b","g","r")
plt.figure()
plt.title("Flattened Color Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")

for (chan,color) in zip(chans,colors):
    hist = cv2.calcHist([chan],[0],None,[256],[0,256])
    plt.plot(hist,color = color)
    plt.xlim([0,256])
plt.show()

运行结果:
彩色:在这里插入图片描述
在这里插入图片描述
灰色:
在这里插入图片描述
在这里插入图片描述
结果对比很明显像素值变的均匀很多,而且明显彩色图像变得更亮了一些。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
统计直方图是图像处理中的一种常见的方法,可以用来了解图像的亮度分布情况。而均值分割是一种简单有效的图像分割方法,可以将图像中的目标和背景分离出来。 下面是利用C++实现统计直方图并进行均值分割的步骤: 1. 读入图像并将其转换为灰度图像。 2. 统计图像的直方图,可以使用OpenCV库中的calcHist函数。 3. 计算直方图均值,即亮度的平均值。 4. 将图像分成两部分,一部分是亮度低于均值像素,另一部分是亮度高于均值像素。 5. 分别计算两部分像素均值,作为分割后的两个区域的阈值。 6. 将图像根据阈值进行分割,得到分割后的目标和背景。 7. 可以将分割后的图像输出,或者进行后续处理。 下面是一个简单的C++代码示例,实现了图像的统计直方图均值分割: ```c++ #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main() { // 读入图像并转换为灰度图像 Mat src = imread("lena.jpg"); Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); // 统计直方图 Mat hist; int histSize = 256; float range[] = { 0, 256 }; const float* histRange = { range }; calcHist(&gray, 1, 0, Mat(), hist, 1, &histSize, &histRange); // 计算直方图均值 int totalPixels = gray.rows * gray.cols; double mean = 0; for (int i = 0; i < histSize; i++) { mean += i * hist.at<float>(i); } mean /= totalPixels; // 分割图像 Mat dst = Mat::zeros(gray.size(), gray.type()); double lowThres = mean, highThres = mean; while (true) { double lowMean = 0, highMean = 0; int lowCount = 0, highCount = 0; for (int i = 0; i < histSize; i++) { if (i <= lowThres) { lowMean += i * hist.at<float>(i); lowCount += hist.at<float>(i); } else { highMean += i * hist.at<float>(i); highCount += hist.at<float>(i); } } if (lowCount > 0) { lowMean /= lowCount; } if (highCount > 0) { highMean /= highCount; } if (lowThres == lowMean && highThres == highMean) { break; } lowThres = lowMean; highThres = highMean; } for (int i = 0; i < gray.rows; i++) { for (int j = 0; j < gray.cols; j++) { if (gray.at<uchar>(i, j) <= lowThres) { dst.at<uchar>(i, j) = 0; } else { dst.at<uchar>(i, j) = 255; } } } // 输出分割后的图像 imshow("src", src); imshow("dst", dst); waitKey(0); return 0; } ``` 需要注意的是,这个方法是基于全局阈值的均值分割方法,对于复杂的图像可能效果不佳。在实际应用中,可以结合其他的分割方法来提高分割效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值