当前位置: 首页 > news >正文

OpenCV-Python (官方)中文教程(部分一)_Day22

22.3 2D直方图

在前面的部分我们介绍了如何绘制一维直方图,之所以称为一维,是因为我们只考虑了图像的一个特征:灰度值。但是在 2D 直方图中我们就要考虑 两个图像特征。对于彩色图像的直方图通常情况下我们需要考虑每个的颜色(Hue)和饱和度(Saturation)。根据这两个特征绘制  2D 直方图。

OpenCV 的官方文档中包含一个创建彩色直方图的例子。本节就是要和大 家一起来学习如何绘制颜色直方图,这会对我们下一节学习直方图投影有所帮 助。

OpenCV 中的 2D 直方图

使用函数 cv2.calcHist() 来计算直方图既简单又方便。如果要绘制颜色 直方图的话,我们首先需要将图像的颜色空间从 BGR 转换到 HSV。(记住, 计算一维直方图,要从 BGR 转换到 HSV)。计算 2D 直方图,函数的参数要 做如下修改:

• channels=[0,1] 因为我们需要同时处理 H 和 S 两个通道。

• bins=[180,256]H 通道为 180,S 通道为 256。

• range=[0,180,0,256]H 的取值范围在 0 到 180,S 的取值范围 在 0 到 256。

代码如下:

import cv2

import numpy as np

img = cv2.imread('home.jpg')

hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

以下是使用 OpenCV 计算 ​​2D 直方图​​(如基于颜色或梯度方向的联合分布)的完整代码示例:

import cv2

import numpy as np

from matplotlib import pyplot as plt

# 读取图像并转换到HSV颜色空间

img = cv2.imread('image.jpg')

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 定义2D直方图的参数

channels = [0, 1]  # 使用H(色调)和S(饱和度)通道

h_bins = 180       # H通道的bin数量(0-180)

s_bins = 256       # S通道的bin数量(0-256)

hist_size = [h_bins, s_bins]

h_range = [0, 180] # H通道取值范围

s_range = [0, 256] # S通道取值范围

ranges = h_range + s_range  # 合并范围

# 计算2D直方图

#images: 输入图像列表(需用 [] 包裹,如 [hsv])。

#channels: 要统计的通道索引(如 [0, 1] 表示H和S通道)。

#mask: 掩模图像(None 表示全图统计)。

#histSize: 每个维度的bin数量(如 [180, 256])。

#ranges: 每个通道的取值范围(如 [0, 180, 0, 256])。

hist = cv2.calcHist([hsv], channels, None, hist_size, ranges)

# 归一化直方图(可选,方便可视化)cv2.normalize() 将直方图缩放到 [0, 255],便于显示。

hist_norm = cv2.normalize(hist, None, 0, 255, cv2.NORM_MINMAX)

# 可视化

# 创建图形

plt.figure(figsize=(12, 6))

# 子图1:原始图像

plt.subplot(121)

plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

plt.title('Original Image')

plt.axis('off')

# 子图2:2D直方图热力图

plt.subplot(122)

plt.imshow(hist_norm, cmap='jet', interpolation='nearest')

plt.title('2D Histogram (H-S)')

plt.xlabel('Saturation')

plt.ylabel('Hue')

plt.colorbar()

plt.tight_layout()

plt.savefig('2d_histogram_result.png', dpi=300)

plt.show()

 

其他常见 2D 直方图类型​​

​​(1) 梯度方向 vs. 梯度幅值​

# 计算梯度

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0)  # x方向梯度

gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1)  # y方向梯度

mag, ang = cv2.cartToPolar(gx, gy)     # 梯度幅值和方向(弧度)

ang_deg = ang * 180 / np.pi             # 弧度转角度(0~180°)

# 计算2D直方图(方向 vs. 幅值)

hist = cv2.calcHist(

    [ang_deg, mag], [0, 1], None,

    [180, 256], [0, 180, 0, 256]  # 方向分180bin,幅值分256bin

)

​​(2) BGR 双通道直方图​

# 计算B和G通道的2D直方图

hist = cv2.calcHist(

    [img], [0, 1], None,

    [256, 256], [0, 256, 0, 256]

)

在图像处理中,梯度方向 vs. 梯度幅值和BGR 双通道直方图是两种完全不同的特征表示方法,它们的核心区别体现在统计对象、应用场景和物理意义上。以下是详细对比:

​1. 梯度方向 vs. 梯度幅值直方图​

​​统计对象​

​​梯度方向(Orientation)​​:图像中每个像素点的梯度角度(如边缘方向)。

​​梯度幅值(Magnitude)​​:梯度强度的量化值(如边缘的明显程度)。

​​计算步骤​​

​​计算梯度​​:

gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0)  # x方向梯度

gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1)  # y方向梯度

mag, ang = cv2.cartToPolar(gx, gy)     # 幅值和方向(弧度转角度:ang = ang * 180 / np.pi)

​​2D直方图​​:

横轴:梯度方向(0°~180°,无符号梯度)或(0°~360°,有符号梯度)。

纵轴:梯度幅值(通常归一化到 [0, 256])。

​​物理意义​​

​​方向​​:描述边缘或纹理的走向(如水平、垂直、对角线)。

​​幅值​​:描述边缘的强度(值越大,边缘越明显)。

​应用场景​

​​边缘检测​​:增强对物体轮廓的敏感性。

​​纹理分析​​:区分不同纹理模式(如条纹 vs 斑点)。

​​HOG(方向梯度直方图)​​:行人检测、物体识别。

​​可视化示例​

plt.imshow(ang, cmap='hsv')  # 用HSV颜色映射方向

plt.colorbar(label='Angle (degrees)')

​​2. BGR 双通道直方图​​

​统计对象​​

​​B通道​​:图像的蓝色分量强度(0~255)。

​​G通道​​:图像的绿色分量强度(0~255)。

​​计算步骤​

hist = cv2.calcHist([img], [0, 1], None, [256, 256], [0, 256, 0, 256])

​​物理意义​​

​​颜色分布​​:反映图像中蓝色和绿色的联合分布(如天空的蓝色 vs 草地的绿色)。

​​相关性​​:若直方图对角线密集,说明B和G通道高度相关(如青色区域)。

​​应用场景​​

​​颜色分割​​:区分不同颜色的物体(如交通标志识别)。

​​白平衡分析​​:检测图像色偏。

​​图像检索​​:基于颜色相似性搜索图片。

​​可视化示例​​

plt.imshow(hist, cmap='jet', extent=[0, 256, 0, 256])

plt.xlabel('Blue Channel')

plt.ylabel('Green Channel')

​3. 核心区别总结​​

4. 选择依据​​

​​用梯度直方图

需要分析图像的结构特征(如边缘、纹理)时,例如检测车辆轮廓或指纹识别。

​​用BGR直方图

需要分析图像的颜色特征时,例如区分红色苹果和绿色背景。

以下是完整的代码对比,展示梯度方向-幅值直方图、BGR双通道直方图原始图像的视觉效果,并生成对比图:

import cv2

import numpy as np

import matplotlib.pyplot as plt

def plot_comparison(image_path):

    # 1. 读取图像

    img = cv2.imread(image_path)

    if img is None:

        print("错误:图像加载失败!")

        return

    

    # 转换为RGB格式(用于Matplotlib显示)

    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 2. 计算梯度方向-幅值直方图

    gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0)  # x方向梯度

    gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1)  # y方向梯度

    mag, ang = cv2.cartToPolar(gx, gy)      # 梯度幅值和方向(弧度)

    ang_deg = ang * 180 / np.pi             # 弧度转角度(0~180°)

    hist_gradient = cv2.calcHist(

        [ang_deg, mag], [0, 1], None,

        [180, 256], [0, 180, 0, 256]        # 方向分180bin,幅值分256bin

    )

    hist_gradient_norm = cv2.normalize(hist_gradient, None, 0, 255, cv2.NORM_MINMAX)

    # 3. 计算B-G双通道直方图

    hist_bgr = cv2.calcHist(

        [img], [0, 1], None,                # 使用B和G通道

        [256, 256], [0, 256, 0, 256]        # 每个通道分256bin

    )

    hist_bgr_norm = cv2.normalize(hist_bgr, None, 0, 255, cv2.NORM_MINMAX)

    # 4. 绘制对比图

    plt.figure(figsize=(15, 5))

    # 子图1:原始图像

    plt.subplot(131)

    plt.imshow(img_rgb)

    plt.title('Original Image')

    plt.axis('off')

    # 子图2:梯度方向-幅值直方图

    plt.subplot(132)

    plt.imshow(hist_gradient_norm, cmap='jet', extent=[0, 256, 0, 180], aspect='auto')

    plt.title('Gradient Orientation vs. Magnitude')

    plt.xlabel('Magnitude')

    plt.ylabel('Orientation (degrees)')

    plt.colorbar()

    # 子图3:B-G双通道直方图

    plt.subplot(133)

    plt.imshow(hist_bgr_norm, cmap='jet', extent=[0, 256, 0, 256], aspect='auto')

    plt.title('B vs. G Channel Histogram')

    plt.xlabel('Green Channel')

    plt.ylabel('Blue Channel')

    plt.colorbar()

    plt.tight_layout()

    plt.savefig('comparison_result.png', dpi=300, bbox_inches='tight')

    plt.show()

    print("对比图已保存为 comparison_result.png")

if __name__ == "__main__":

plot_comparison('image.jpg')  # 替换为你的图像路径

关键区别对比​

梯度直方图中,方向为0°通常对应垂直边缘,90°对应水平边缘。

B-G直方图中,对角线密集区域表示蓝色和绿色通道强相关(如天空或水面)。

在梯度方向-幅值直方图(Gradient Orientation vs. Magnitude Histogram)中,线条分布和颜色强度可以直接反映原始图像的边缘特征、纹理结构和方向性。以下是具体分析方法:

1. 直方图坐标轴含义​​

​​横轴(X轴)​​:梯度幅值(Magnitude)

表示边缘的强度,值越大(右侧)对应原图中越明显的边缘。

​​纵轴(Y轴)​​:梯度方向(Orientation)

表示边缘的角度(0°~180°),例如:

​0°​​:垂直边缘(如建筑物的竖线)

​​90°​​:水平边缘(如地平线)

​​45°/135°​​:对角线边缘(如斜向纹理)

​​颜色强度(热力图颜色)​​:

颜色越亮(如黄色/白色)表示该方向和幅值的边缘出现频率越高。

​2. 如何从直方图反推原图特征?​​

​​(1) 观察线条聚集区域​​

​案例1:垂直线条主导​

直方图中Y=0°附近有亮线→ 原图有大量垂直边缘(如栅栏、高楼)。

​​示例:

# 生成测试图像(垂直线条)

img = np.zeros((100, 100), dtype=np.uint8)

img[:, 30:35] = 255  # 垂直白条

​​案例2:水平线条主导​​

直方图中Y=90°附近有亮线 → 原图有水平边缘(如海平面、书架)。

​示例

img[40:45, :] = 255  # 水平白条

​​(2) 分析幅值分布​​

​​高幅值集中(右侧亮)​​:

原图有​​清晰锐利的边缘​​(如物体轮廓、文字)。

​​低幅值分散(左侧亮)​​

原图以​​柔和纹理​​为主(如云彩、模糊背景)。

​​(3) 多方向混合特征​​

​​亮斑分散在多个角度​​

原图存在​​复杂纹理​​(如树叶、毛发)。

​​示例​​(树叶图像):

​(4) 颜色强度变化​​

​​连续亮带​​:

表示边缘方向连续变化(如圆形物体的渐变边缘)。

​​离散亮点​​:

表示特定方向的孤立边缘(如人工规则图案)。

​3. 总结​​

通过梯度直方图可以直观判断原图的:

​​边缘主导方向​​(垂直/水平/斜向)。

​​边缘清晰度​​(幅值高低)。

​​纹理复杂度​​(分散或集中的亮斑)。

​​实用技巧​​:

若直方图在 ​​0°和90°​​ 同时有高峰 → 图像包含网格状结构(如棋盘)。

若直方图在 ​​所有方向均匀分布​​ → 图像可能是噪声或随机纹理(如砂纸)。

import cv2

import numpy as np

import matplotlib.pyplot as plt

# 1. 生成测试图像

img = np.zeros((200, 200), dtype=np.uint8)

img[20:100, 20:100] = 255  # 白色方块

img[120:180, 120:180] = 255 # 另一个白色方块

cv2.line(img, (0, 0), (200, 200), 255, 2)  # 对角线白线

# 2. 计算梯度

gx = cv2.Scharr(img, cv2.CV_32F, 1, 0)

gy = cv2.Scharr(img, cv2.CV_32F, 0, 1)

mag, ang = cv2.cartToPolar(gx, gy)

ang_deg = ang * 180 / np.pi  # 转换为角度

# 3. 归一化幅度

mag_norm = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_32F)

# 4. 确保数据是2D矩阵

ang_deg = np.squeeze(ang_deg)

mag_norm = np.squeeze(mag_norm)

# 5. 计算直方图 (修正了bins和范围)

hist = cv2.calcHist(

    [ang_deg, mag_norm],  # 输入

    [0, 1],               # 使用的通道

    None,                 # 无掩模

    [180, 32],            # bins数量 [角度, 幅度]

    [0, 180, 0, 256]      # 范围

)

# 6. 可视化 (修正了显示方式)

plt.figure(figsize=(12, 5))

plt.subplot(131)

plt.imshow(img, cmap='gray')

plt.title('Test Image')

plt.subplot(132)

plt.imshow(mag_norm, cmap='jet')

plt.colorbar()

plt.title('Gradient Magnitude')

plt.subplot(133)

# 对直方图进行对数变换以便更好地显示

hist_log = np.log1p(hist)

plt.imshow(hist_log.T, cmap='jet', aspect='auto', extent=[0, 180, 0, 64])

plt.colorbar()

plt.xlabel('Angle (degrees)')

plt.ylabel('Magnitude')

plt.title('Gradient Histogram (log scale)')

plt.tight_layout()

plt.show()

在梯度直方图(Gradient Histogram)中,​​X轴和Y轴​​分别表示以下内容:

​​X轴(横轴)​​:​​梯度方向(Gradient Angle)​

​​单位​​:角度(degrees)

​范围​​:0° ~ 180°(因为梯度方向是​​无符号​​的,即 0° 和 180° 代表相同的方向)

​​含义​​:

​​0°​​ 表示​​垂直边缘​​(梯度方向向右,即从黑到白的过渡方向)

​​90°​​ 表示​​水平边缘​​(梯度方向向上)

​​45°​​ 表示​​对角线边缘​​(从左上到右下)

​​135°​​ 表示​​另一条对角线边缘​​(从右上到左下)

​​Y轴(纵轴)​​:​​梯度幅度(Gradient Magnitude)​

​​单位​​:归一化后的像素强度(0~255)

​​范围​​:0 ~ 64(取决于 cv2.calcHist 的 bins 设置)

​​含义​​:

​​值越大​​,表示该方向的边缘​​越强​​(即梯度变化越剧烈)

​​值越小​​,表示该方向的边缘​​越弱​​(即梯度变化越平缓)

​直方图颜色(Color)​​:

​​颜色越亮(如黄色/白色)​​,表示该角度和幅度的梯度​​出现频率越高​​。

​​颜色越暗(如蓝色/黑色)​​,表示该角度和幅度的梯度​​出现频率越低​​。

​示例解释(针对你的测试图像)​​

你的测试图像包含:

​​两个白色方块​​(20:100, 20:100 和 120:180, 120:180):

它们的边缘会产生​​水平(90°)和垂直(0°)​​的梯度。

​​一条对角线白线​​(从 (0,0) 到 (200,200)):

会产生​​45°​​ 方向的梯度。

因此,在梯度直方图中,你应该看到:

​​X=0° 和 X=90°​​ 附近有较强的响应(来自方块的边缘)。

​​X=45°​​ 附近也有较强的响应(来自对角线白线)。

​​Y轴​​ 的值较高,表示这些方向的梯度幅度较大。

Numpy中2D直方图

Numpy 同样提供了绘制 2D 直方图的函数:np.histogram2d()。(还记得吗,绘制  1D  直方图时我们使用的是 np.histogram())。

import cv2

import numpy as np

from matplotlib import pyplot as plt

img = cv2.imread('home.jpg')

hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])

第一个参数是 H 通道,第二个参数是 S 通道,第三个参数是 bins 的数 目,第四个参数是数值范围。

现在我们要看看如何绘制颜色直方图。

import cv2

import numpy as np

import matplotlib.pyplot as plt

# 1. 生成测试图像

img = np.zeros((200, 200), dtype=np.uint8)

img[20:100, 20:100] = 255  # 白色方块

img[120:180, 120:180] = 255 # 另一个白色方块

cv2.line(img, (0, 0), (200, 200), 255, 2)  # 对角线白线

# 2. 计算梯度(使用Scharr算子)

gx = cv2.Scharr(img, cv2.CV_32F, 1, 0)

gy = cv2.Scharr(img, cv2.CV_32F, 0, 1)

mag, ang = cv2.cartToPolar(gx, gy)

ang_deg = ang * 180 / np.pi  # 转换为角度(0°~180°)

# 3. 归一化梯度幅度(0~255)

mag_norm = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX).flatten()

# 4. 使用np.histogram2d计算2D直方图

hist, xedges, yedges = np.histogram2d(

    ang_deg.flatten(),  # 角度数据(X轴)

    mag_norm,           # 幅度数据(Y轴)

    bins=[180, 64],     # bins数量:[角度, 幅度]

    range=[[0, 180], [0, 256]]  # 范围:角度0~180°,幅度0~256

)

# 5. 可视化

plt.figure(figsize=(15, 5))

# 子图1:原图

plt.subplot(131)

plt.imshow(img, cmap='gray')

plt.title('Original Image')

# 子图2:梯度幅度图

plt.subplot(132)

plt.imshow(mag, cmap='jet')

plt.colorbar()

plt.title('Gradient Magnitude')

# 子图3:2D直方图(使用pcolormesh显示)

plt.subplot(133)

# 使用对数变换增强低值可见性

hist_log = np.log(hist.T + 1)  # 转置并取对数

#使用 pcolormesh 绘制2D直方图,比 imshow 更精确(能正确显示bin边缘)

plt.pcolormesh(xedges, yedges, hist_log, cmap='jet')

plt.colorbar(label='Log Frequency')

plt.xlabel('Gradient Angle (degrees)')

plt.ylabel('Gradient Magnitude')

plt.title('2D Gradient Histogram (np.histogram2d)')

plt.xlim(0, 180)

plt.ylim(0, 256)

plt.tight_layout()

plt.show()

cv2.calcHist() 和 np.histogram2d() 都可以计算二维直方图,但它们在​​输入格式、计算方式、返回值结构​​以及​​与OpenCV/NumPy生态的兼容性​​上有显著区别。以下是详细对比:

1. 输入格式​

函数                     输入数据要求

cv2.calcHist()     输入是列表形式的数组(即使单通道也要包在列表中),例如 [ang_deg, mag_norm]。

np.histogram2d() 直接接受两个独立的NumPy数组(x和y),例如 ang_deg.flatten(), mag_norm。

​​示例:​​

# cv2.calcHist

hist_cv2 = cv2.calcHist([ang_deg, mag_norm], [0, 1], None, [180, 64], [0, 180, 0, 256])

# np.histogram2d

hist_np, xedges, yedges = np.histogram2d(ang_deg.flatten(), mag_norm, bins=[180, 64], range=[[0, 180], [0, 256]])

​​2. 返回值​

函数                     返回值

cv2.calcHist()       返回单一的直方图数组(形状为 (bins[0], bins[1], ...)),无边界信息。

np.histogram2d()  返回直方图数组 + 两个方向的bin边界数组(hist, xedges, yedges),便于精确绘制。

​​关键区别:​​

cv2.calcHist() 的返回值直接是直方图,适合快速可视化(如 imshow)。

np.histogram2d() 返回的 xedges 和 yedges 可以用于 pcolormesh,能更精确地显示bin的边界。

​3. 数据类型与性能​​

函数   数据类型支持 计算效率

cv2.calcHist() 对OpenCV的 cv2.CV_32F 或 cv2.CV_8U 类型优化更好,适合图像数据。 高度优化,适合大规模图像数据。

np.histogram2d() 直接处理NumPy数组,支持任意数据类型(如 float64)。 通用性强,但可能稍慢于OpenCV。

​​4. 可视化适配性​

函数 推荐可视化方法 适用场景

cv2.calcHist() plt.imshow(hist.T, extent=[0, 180, 0, 256])(需手动转置和设置范围)。 快速查看直方图分布。

np.histogram2d() plt.pcolormesh(xedges, yedges, hist.T)(自动对齐bin边界)。 需要精确显示bin边界的场景。

​​示例对比:​​

# 使用cv2.calcHist + imshow

plt.imshow(hist_cv2.T, cmap='jet', extent=[0, 180, 0, 256], aspect='auto')

# 使用np.histogram2d + pcolormesh

plt.pcolormesh(xedges, yedges, hist_np.T, cmap='jet')

​5. 功能扩展性​

函数 额外功能

cv2.calcHist() 支持掩模(mask参数)、多通道直方图(如RGB图像的3D直方图)。

np.histogram2d() 支持权重(weights参数)、密度归一化(density=True),适合统计分析。

​​6. 实际效果对比(以你的代码为例)​​

​​cv2.calcHist() 结果​​

直方图是一个 ​​密集的二维数组​​,直接映射到像素坐标。

使用 imshow 时需手动调整 extent 和转置(hist.T)。

​​np.histogram2d() 结果​​

直方图 + bin边界信息,可通过 pcolormesh 精确绘制。

天然支持非均匀bin(通过自定义 xedges 和 yedges)。

​​总结:如何选择?​​

场景 推荐函数

图像处理(如梯度直方图) cv2.calcHist()

统计分析与非均匀bin np.histogram2d()

需要精确bin边界的可视化 np.histogram2d()

高性能计算(大规模数据) cv2.calcHist()

两种方法本质上是等价的(最终直方图一致),但接口和生态适配不同。​​如果已用OpenCV处理图像,优先用 cv2.calcHist;若需更灵活的统计功能,选 np.histogram2d。​

import cv2

import numpy as np

import matplotlib.pyplot as plt

# 1. 生成测试图像

img = np.zeros((200, 200), dtype=np.uint8)

img[20:100, 20:100] = 255  # 白色方块

img[120:180, 120:180] = 255 # 另一个白色方块

cv2.line(img, (0, 0), (200, 200), 255, 2)  # 对角线白线

# 2. 计算梯度(使用Scharr算子)

gx = cv2.Scharr(img, cv2.CV_32F, 1, 0)

gy = cv2.Scharr(img, cv2.CV_32F, 0, 1)

mag, ang = cv2.cartToPolar(gx, gy)

ang_deg = ang * 180 / np.pi  # 转换为角度(0°~180°)

# 3. 归一化梯度幅度(0~255)

mag_norm = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX).flatten()

# 4. 使用np.histogram2d计算2D直方图

hist, xedges, yedges = np.histogram2d(

    ang_deg.flatten(),  # 角度数据(X轴)

    mag_norm,           # 幅度数据(Y轴)

    bins=[180, 64],     # bins数量:[角度, 幅度]

    range=[[0, 180], [0, 256]]  # 范围:角度0~180°,幅度0~256

)

# 5. 可视化(新增边界数组的可视化)

plt.figure(figsize=(18, 6))

# 子图1:原图

plt.subplot(141)

plt.imshow(img, cmap='gray')

plt.title('Original Image')

# 子图2:梯度幅度图

plt.subplot(142)

plt.imshow(mag, cmap='jet')

plt.colorbar()

plt.title('Gradient Magnitude')

# 子图3:2D直方图(显式标注边界数组)

plt.subplot(143)

hist_log = np.log(hist.T + 1)  # 转置并取对数

plt.pcolormesh(xedges, yedges, hist_log, cmap='jet')

plt.colorbar(label='Log Frequency')

# 标注边界数组的关键点(红色虚线)

for i in [0, 90, 180]:  # 角度边界示例

    plt.axvline(x=xedges[i], color='red', linestyle='--', alpha=0.5)

for j in [0, 32, 63]:   # 幅度边界示例

    plt.axhline(y=yedges[j], color='red', linestyle='--', alpha=0.5)

plt.xlabel('Gradient Angle (degrees)\nRed Dashed: xedges')

plt.ylabel('Gradient Magnitude\nRed Dashed: yedges')

plt.title('2D Histogram with Bin Edges')

# 子图4:边界数组的数值展示

plt.subplot(144)

plt.axis('off')  # 关闭坐标轴

text_content = (

    "=== Boundary Arrays ===\n"

    f"xedges (angle bins):\n{np.round(xedges[:5], 1)} ... {np.round(xedges[-5:], 1)}\n"

    f"Shape: {xedges.shape}\n\n"

    f"yedges (magnitude bins):\n{np.round(yedges[:5], 1)} ... {np.round(yedges[-5:], 1)}\n"

    f"Shape: {yedges.shape}"

)

plt.text(0, 0.5, text_content, fontfamily='monospace', va='center')

plt.title('np.histogram2d() Output')

plt.tight_layout()

plt.show()

绘制2D直方图

方法 1:使用 cv2.imshow() 我们得到结果是一个 180x256 的两维数组。 所以我们可以使用函数 cv2.imshow() 来显示它。但是这是一个灰度图,除 非我们知道不同颜色 H 通道的值,否则我们根本就不知道那到底代表什么颜色。

关键点:​​

​​输出结果​​:

cv2.calcHist() 返回的直方图是一个 ​​180x256 的二维数组​​(假设角度分180个bin,幅度分256个bin)。

这个数组的每个值表示 ​​某个角度和幅度组合的频次​​(即统计次数)。

​显示问题​​:

cv2.imshow() 会将其当作​​灰度图像​​显示,亮度越高表示频次越高。

​​缺点​​:

你无法直接知道 ​​颜色(Hue)对应的实际角度值​​(例如,哪个灰度值代表45°?)。

缺乏直观的颜色映射(如 jet、viridis 等颜色条)。

​示例代码:​​

hist = cv2.calcHist([ang_deg, mag_norm], [0, 1], None, [180, 256], [0, 180, 0, 256])

cv2.imshow("Histogram (Grayscale)", hist)

cv2.waitKey(0)

​​效果​​:

显示一个灰度图,亮度表示频次,但无法直接关联到角度和幅度。

方法 2:使用 Matplotlib() 我们还可以使用函数 matplotlib.pyplot.imshow() 来绘制 2D 直方图,再搭配上不同的颜色图(color_map)。这样我们会对每 个点所代表的数值大小有一个更直观的认识。但是跟前面的问题一样,你还是 不知道那个数代表的颜色到底是什么。虽然如此,我还是更喜欢这个方法,它 既简单又好用。

关键点:​​

​​优势​​:

Matplotlib 的 imshow() 支持 ​​颜色映射(color_map)​​,例如 jet、hot、viridis 等。

可以通过颜色条(colorbar)直观地看到数值大小对应的颜色。

​​插值参数 interpolation='nearest'​​:

默认情况下,imshow() 会对图像进行​​平滑插值​​,可能导致直方图的bin边界模糊。

设置 interpolation='nearest' 可以保留原始bin的锐利边界,避免误导。

​依然存在的问题​​:

虽然颜色图能显示数值大小,但 ​​X/Y轴的刻度需要手动关联到实际的角度和幅度值​​(需通过 extent 参数设置)。

例如:extent=[0, 180, 0, 256] 表示X轴是角度(0°~180°),Y轴是幅度(0~256)。

​​示例代码:​​

plt.imshow(hist.T, cmap='jet', interpolation='nearest', extent=[0, 180, 0, 256])

plt.colorbar(label='Frequency')

plt.xlabel('Angle (degrees)')

plt.ylabel('Magnitude')

plt.title('2D Histogram with Color Map')

plt.show()

​​效果​​:

显示一个彩色直方图,颜色表示频次,X/Y轴标签明确角度和幅度范围。

注意:在使用这个函数时,要记住设置插值参数为 nearest。

为什么作者更喜欢Matplotlib?​

​​直观性​​:颜色映射比灰度图更容易理解数值分布。

​​灵活性​​:支持调整坐标轴、添加标签、颜色条等。

​​易用性​​:适合嵌入到更复杂的图表中(如子图、叠加其他数据)。

​关键总结​

方法 优点 缺点 适用场景

​​cv2.imshow()​​ 简单快速,适合OpenCV流程。 只能显示灰度图,无颜色映射和坐标轴标签。 快速调试,无需详细分析。

​​plt.imshow()​​ 支持颜色映射、坐标轴标签、插值控制。 需手动设置 extent 和 interpolation。 需要直观展示和定量分析时。

​​如何改进?​

如果希望​​直接关联颜色与角度​​,可以:

​​自定义颜色映射​​:将角度(Hue)映射到HSV颜色空间,再转换为RGB显示。

​​添加交互式工具​​:用 mplcursors 库实现鼠标悬停时显示角度和幅度值。

​​示例(HSV颜色映射):​​

hsv = np.zeros((*hist.shape, 3), dtype=np.uint8)

hsv[..., 0] = np.linspace(0, 180, 180).astype(np.uint8)  # Hue = 角度

hsv[..., 1] = 255  # 饱和度固定

hsv[..., 2] = cv2.normalize(hist, None, 0, 255, cv2.NORM_MINMAX)  # 亮度 = 频次

rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)

plt.imshow(rgb, extent=[0, 180, 0, 256])

plt.colorbar(label='Magnitude Frequency')

plt.show()

这样颜色直接代表角度,亮度代表频次,解决了“不知道颜色对应什么角度”的问题。

代码如下:

import cv2

import numpy as np

from matplotlib import pyplot as plt

img = cv2.imread('home.jpg')

hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hist = cv2.calcHist( [hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] )

plt.imshow(hist,interpolation = 'nearest')

plt.show()

下面是输入图像和颜色直方图。X 轴显示 S 值,Y 轴显示 H 值。

在直方图中,你可以看到在 H=100,S=100 附近有比较高的值。这部分与天的蓝色相对应。同样另一个峰值在 H=25 和 S=100 附近。这一宫殿的黄 色相对应。你可用通过使用图像编辑软件(GIMP)修改图像,然后在绘制直方图看看我说的对不对。

import cv2

import numpy as np

import matplotlib.pyplot as plt

# 1. 生成测试图像(200x200的黑底,带两个白方块和一条对角线)

img = np.zeros((200, 200), dtype=np.uint8)

img[20:100, 20:100] = 255    # 第一个白方块

img[120:180, 120:180] = 255  # 第二个白方块

cv2.line(img, (0, 0), (200, 200), 255, 2)  # 对角线

# 2. 计算梯度(使用Scharr算子)

gx = cv2.Scharr(img, cv2.CV_32F, 1, 0)  # x方向梯度

gy = cv2.Scharr(img, cv2.CV_32F, 0, 1)  # y方向梯度

mag, ang = cv2.cartToPolar(gx, gy)      # 梯度幅值和角度

ang_deg = ang * 180 / np.pi             # 弧度转角度(0°~180°)

# 3. 归一化梯度幅值到0~255范围,并统一数据类型

mag_norm = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)

ang_deg = ang_deg.astype(np.float32)    # 强制转为float32

mag_norm = mag_norm.astype(np.float32)  # 强制转为float32

# 4. 检查数据尺寸和类型(关键调试步骤)

print("ang_deg shape:", ang_deg.shape, "dtype:", ang_deg.dtype)  # 应为 (200,200) float32

print("mag_norm shape:", mag_norm.shape, "dtype:", mag_norm.dtype)  # 应为 (200,200) float32

# 5. 计算2D直方图

hist = cv2.calcHist([ang_deg, mag_norm], [0, 1], None, [180, 256], [0, 180, 0, 256])

# 6. 使用对数变换增强可视化

hist_log = np.log(hist + 1)

hist_normalized = cv2.normalize(hist_log, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)

cv2.imshow("Grayscale Histogram (Log Scale)", hist_normalized)

cv2.waitKey(2000)

# 7. 优化Matplotlib显示

plt.figure(figsize=(15, 5))

plt.subplot(131)

plt.imshow(img, cmap='gray')

plt.title("Original Image")

plt.subplot(132)

plt.imshow(hist_log.T, cmap='gray', interpolation='nearest', extent=[0, 180, 0, 256])

plt.colorbar()

plt.title("Log Histogram (Grayscale)")

plt.subplot(133)

plt.imshow(hist_log.T, cmap='jet', interpolation='nearest', extent=[0, 180, 0, 256],

           vmax=np.max(hist_log)*0.5)  # 调整vmax以突出高频部分

plt.colorbar()

plt.title("Log Histogram (Color)")

plt.tight_layout()

plt.show()

方法 3:OpenCV 风格 在官方文档中有一个关于颜色直方图的例子。运行 一下这个代码,你看到的颜色直方图也显示了对应的颜色。简单来说就是:输 出结果是一副由颜色编码的直方图。效果非常好(虽然要添加很多代码)。

在那个代码中,作者首先创建了一个 HSV 格式的颜色地图,然后把它转 换成 BGR 格式。再将得到的直方图与颜色直方图相乘。作者还用了几步来去 除小的孤立的的点,从而得到了一个好的直方图。

我把对代码的分析留给你们了,自己去玩一下把。下边是对上边的图运行 这段代码之后得到的结果:

从直方图中我们可以很清楚的看出它们代表的颜色,蓝色,黄色,还有棋盘带来的白色,漂亮!!

这段代码是一个基于 ​​HSV 颜色空间​​ 的 ​​2D 直方图可视化​​ 程序,主要用于分析视频帧的 ​​色调(Hue)​​ 和 ​​饱和度(Saturation)​​ 分布。

import numpy as np

import cv2

from time import clock # 用于计时(但实际未使用)

import sys

import video # video 模块也是  opencv 官方文档中自带的

if __name__ == '__main__': #确保代码仅在直接运行时执行,而不是被导入时执行。

    # 构建  HSV 颜色地图

    hsv_map = np.zeros((180, 256, 3), np.uint8)

    #  np.indices 可以返回由数组索引构建的新数组。

    #  例如:np.indices( 3,2);其中(3,2)为原来数组的维度:行和列。

    #  返回值首先看输入的参数有几维:(3,2)有2维,所以从输出的结果应该是[[a],[b]], 其中包含两个3行,2列数组。第二看每一维的大小,第一维为3,所以a中的值就0到2(最大索引数),a中的每一个值就是它的行索引;同样的方法得到 b(列索引)

    #  结果就是: array([[[0, 0],[1, 1],[2, 2]], [[0, 1],0, 1],[0, 1]]])

    h, s = np.indices(hsv_map.shape[:2]) #生成坐标网格,h 和 s 分别表示行和列的索引。

    hsv_map[:, :, 0] = h  # 色调(Hue,0-179)

    hsv_map[:, :, 1] = s  # 饱和度(Saturation,0-255)

    hsv_map[:, :, 2] = 255  # 亮度(Value,固定为最大值)

    hsv_map = cv2.cvtColor(hsv_map, cv2.COLOR_HSV2BGR) #将 HSV 转换为 BGR 格式,以便用 imshow 正确显示。

    cv2.imshow('hsv_map', hsv_map)

    cv2.namedWindow('hist', 0)  # 0 表示窗口大小可调

    hist_scale = 10  # 直方图缩放因子初始值

    def set_scale(val):

        global hist_scale

        hist_scale = val ## 更新缩放因子

    #添加滑动条,动态调整直方图的缩放因子(hist_scale)。

    cv2.createTrackbar('scale', 'hist', hist_scale, 32, set_scale)

    try:

        fn = sys.argv[1]  # 尝试从命令行参数获取视频文件路径

    except:

        fn = 0  # 默认使用摄像头(设备索引 0)

    cam = video.create_capture(fn, fallback='synth:bg=../cpp/baboon.jpg:class=chess:noise=0.05')

    while True:

        flag, frame = cam.read()  # 读取一帧

        cv2.imshow('camera', frame)  # 显示原始帧

        # 图像金字塔

        # 通过图像金字塔降低分辨率,但不会对直方图有太大影响。

        # 但这种低分辨率,可以很好抑制噪声,从而去除孤立的小点对直方图的影响。

        small = cv2.pyrDown(frame)  # 降采样(缩小图像,减少计算量)

        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)  # 转换到 HSV 空间

        # 取  v 通道  (亮度) 的值。

        # dark = hsv[...,2] < 32

        # 此步操作得到的是一个布尔矩阵,小于  32 的为真,大于  32 的为假。

        #目的​​:排除暗区(如阴影),因为它们对颜色分析无意义。

        dark = hsv[:, :, 2] < 32 # 找到亮度 <32 的像素(暗区)

        hsv[dark] = 0 # 将暗区的 HSV 值设为 0(忽略这些像素)

        h = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

        # numpy.clip(a, a_min, a_max, out=None)[source]

        # 给定一个区间,区间外的值被裁剪到区间边缘。例如,如果指定的间隔为[0,1],小于0的值将变为0,大于1的值将变为1。

        # >>> a = np.arange(10)

        # >>> np.clip(a, 1, 8)

        # array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8])

        #hist_scale​​:通过滑动条调整的缩放因子。clip​​:确保值在 [0,1] 范围内。

        h = np.clip(h * 0.005 * hist_scale, 0, 1)

        # 可以在切片语法中使用'newaxis'对象来创建长度为1的轴。也可以用None代替newaxis,效果完全一样

        # h 从一维变成  3 维

        #h[:, :, np.newaxis]​​:将直方图从 2D 扩展为 3D(与 hsv_map 相乘)。hsv_map * h​​:用直方图的值加权颜色地图,高频区域显示更亮。

        vis = hsv_map * h[:, :, np.newaxis] / 255.0 # 将直方图映射到颜色空间

        cv2.imshow('hist', vis)

        ch = 0xFF & cv2.waitKey(1)

        if ch == 27:

            break

cv2.destroyAllWindows()

总结​

​​功能​​:实时分析视频帧的 ​​色调和饱和度分布​​,通过颜色地图直观显示。

​关键点​​:

HSV 颜色空间更适合颜色分析。

2D 直方图(H+S)反映颜色的分布规律。

滑动条动态调整直方图缩放,增强交互性。

忽略低亮度区域,避免噪声干扰。

处理单张彩色图像:

import numpy as np

import cv2

import sys

def main():

    # 1. 读取输入图像(替换为你的图片路径)

    if len(sys.argv) > 1:

        image_path = sys.argv[1]

    else:

        image_path = 'test.jpg'  # 默认图像文件名

    

    frame = cv2.imread(image_path)

    if frame is None:

        print("Error: 无法加载图像,请检查路径!")

        return

    # 2. 创建HSV颜色地图(与之前相同)

    hsv_map = np.zeros((180, 256, 3), np.uint8)

    h, s = np.indices(hsv_map.shape[:2])

    hsv_map[:, :, 0] = h

    hsv_map[:, :, 1] = s

    hsv_map[:, :, 2] = 255

    hsv_map = cv2.cvtColor(hsv_map, cv2.COLOR_HSV2BGR)

    # 3. 创建可调参数的窗口

    cv2.namedWindow('hist', cv2.WINDOW_NORMAL)

    hist_scale = 10

    def set_scale(val):

        global hist_scale

        hist_scale = val

    cv2.createTrackbar('Scale', 'hist', hist_scale, 32, set_scale)

    # 4. 图像处理函数

    def update_hist():

        # 转换为HSV

        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

        

        # 过滤暗区(亮度<32的像素)

        dark = hsv[:, :, 2] < 32

        hsv[dark] = 0

        

        # 计算2D直方图(H和S通道)

        hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

        

        # 缩放直方图

        hist = np.clip(hist * 0.005 * hist_scale, 0, 1)

        

        # 可视化

        vis = hsv_map * hist[:, :, np.newaxis] / 255.0

        cv2.imshow('hist', vis)

    # 初始显示

    cv2.imshow('input', frame)

    update_hist()

    # 5. 交互循环

    while True:

        key = cv2.waitKey(10)

        if key == 27:  # ESC退出

            break

        update_hist()  # 更新直方图(响应滑动条)

    cv2.destroyAllWindows()

if __name__ == '__main__':

main()

如何使用?​​

​​准备图像​​:

将你的图像放在同一目录下(如 test.jpg),或通过命令行参数指定路径。

​​运行效果​​:

窗口 input 显示原始图像。

窗口 hist 显示 ​​HSV 2D直方图​​,可通过滑动条调整亮度。

​​颜色含义​​:

​​X轴​​:色调(Hue,0°~180°对应红→绿→蓝→红)

​​Y轴​​:饱和度(Saturation,0~255 从灰到纯色)

​​亮度​​:颜色越亮表示该HSV组合出现频率越高。

相关文章:

OpenCV-Python (官方)中文教程(部分一)_Day22

22.3 2D直方图 在前面的部分我们介绍了如何绘制一维直方图,之所以称为一维,是因为我们只考虑了图像的一个特征&#xff1a;灰度值。但是在 2D 直方图中我们就要考虑 两个图像特征。对于彩色图像的直方图通常情况下我们需要考虑每个的颜色&#xff08;Hue&#xff09;和饱和度&…...

【软考-高级】【信息系统项目管理师】【论文基础】采购管理过程输入输出及工具技术的使用方法

采购管理概念 项目采购管理包括从项目团队外部采购或获取所需产品、服务或成果的各个过程。项目采购管理包括编制和管理协议所需的管理和控制过程&#xff0c;例如合同、订购单、协议备忘录&#xff08;MOA&#xff09;和服务水平协议&#xff08;SLA&#xff09;。 采购管理…...

基于STM32、HAL库的CP2102-GMR USB转UART收发器 驱动程序设计

一、简介: CP2102-GMR是Silicon Labs公司生产的一款USB转UART桥接芯片,主要特点包括: 集成USB 2.0全速功能控制器 内置USB收发器,无需外部电阻 工作电压:3.0V至3.6V 支持的数据格式:数据位8,停止位1,无校验 最高支持1Mbps的波特率 内置512字节接收缓冲区和512字节发送…...

信息系统项目管理工程师备考计算类真题讲解十四

一、最小生成树问题 此问题采用破圈法来解决&#xff0c; 1&#xff09;以1节点为例&#xff0c;找到路径最小 点&#xff1a;1--5&#xff1a;距离为3 2&#xff09;找1--5最短的节点&#xff0c;选择4&#xff1a;1--5--4&#xff1a;距离为&#xff1a;5 3&#xff09;找…...

二叉树的基本操作

二叉树的基本操作(C 语言版) 1 二叉树的定义 二叉树的图长这样: 二叉树是每个结点最多有两个子树的树结构,常被用于实现二叉查找树和二叉堆。二叉树是链式存储结构,用的是二叉链,本质上是链表。二叉树通常以结构体的形式定义,如下,结构体内容包括三部分:本节点所存储…...

网络基础入门第6-7集(抓包技术)

前言&#xff1a; 来自小迪安全v2023 内容&#xff1a; 第六集&#xff1a; 大致内容&#xff1a;burpsuit、茶杯、fiddler的抓包流程 1、安装抓包软件的相关证书 2、各大抓包软件的测试 注意用burp抓模拟器的数据包&#xff0c;需要将ip地址设置为本地的ip地址&#xff…...

自定义Widget开发:自定义布局实现

自定义Widget开发&#xff1a;自定义布局实现 一、Flutter布局系统基础 1. 布局约束&#xff08;Constraints&#xff09; 在Flutter中&#xff0c;布局系统基于约束&#xff08;Constraints&#xff09;的概念。每个widget都会接收来自其父widget的约束&#xff0c;并根据这…...

MyBatis(进阶)(xml标签)

本节⽬标 1. 学习MyBatis的动态SQL查询 2. 掌握MyBatis在项⽬中的应⽤, 可以使⽤Spring MVC完成⼀些基础的功能 1. 动态SQL&#xff08;XML&#xff09; 动态 SQL 是Mybatis的强⼤特性之⼀&#xff0c;能够完成不同条件下不同的 sql 拼接 可以参考官⽅⽂档&#xff1a; M…...

英皇娱乐X乐华娱乐携手造星!“英皇乐华青少年艺人培训班”正式启动!

2025年5月8日&#xff0c;英皇娱乐集团与乐华娱乐集团联合宣布&#xff0c;双方将在北京市燕京实验中学合作开设“英皇乐华青少年艺人培训班”&#xff0c;为8至18岁的青少年提供专业的演艺及才艺学习平台。此次合作旨在集合两大娱乐公司在演艺行业的资源与优势&#xff0c;共同…...

Linux云计算训练营笔记day04(Rocky Linux中的命令)

mv 移动(剪切) 源数据会消失 格式: mv 源文件 目标路径 touch /opt/a.txt 创建文件 mv /opt/a.txt /root 移动文件&#xff0c;没有改名 mkdir gongli 创建目录 mv gongli /opt/ 移动目录&#xff0c;没有改名 mv /opt/gongli tedu 移动目录&#xff0c;改名了 …...

枚举 · 例13-【模板】双指针

登录—专业IT笔试面试备考平台_牛客网 代码区&#xff1a; #include<algorithm> #include<iostream> #include<vector> #include<unordered_set> using namespace std;struct INTER{int left,right; }; bool compare(const INTER&a,const INTER&a…...

Linux网络编程day7 线程池and UDP

线程池 typedef struct{void*(*function)(void*); //函数指针&#xff0c;回调函数void*arg; //上面函数的参数 }threadpool_task_t; //各子线程任务的结构体/*描述线程池相关信息*/struct threadpool_t{pthread_mutex_t lock; …...

WHAT - ahooks vs swr 请求

文章目录 ahooks特点常用 Hooks 示例1. useRequest — 封装网络请求逻辑&#xff08;比 SWR / React Query 更轻量&#xff09;2. useDebounce — 防抖值3. useLocalStorageState — 本地存储的状态4. useBoolean — 快速管理布尔状态5. useEventListener — 添加事件监听 ahoo…...

算法训练营第十一天|150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素

150. 逆波兰表达式求值 题目 思路与解法 第一思路&#xff1a; 比较简单 class Solution:def evalRPN(self, tokens: List[str]) -> int:stack []for item in tokens:if item ! and item ! - and item ! * and item ! / :stack.append(item)else:b int(stack.pop())a …...

可视化图解算法35:在二叉树中找到两个节点的最近公共祖先(二叉树的最近公共祖先)

1. 题目 描述 给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2&#xff0c;请找到 o1 和 o2 的最近公共祖先节点。 数据范围&#xff1a;树上节点数满足 1≤n≤105 , 节点值val满足区间 [0,n) 要求&#xff1a;时间复杂度 O(n) 注&#xff1a;本题保…...

如果说开启的TIM3定时器有ccr1,ccr2,ccr3,我想要关闭ccr2的PWM输出,怎么通过代码实现

目录 作用概述&#xff1a; 具体原理&#xff1a; 代码的操作细节&#xff1a; 实际效果&#xff1a; 示意全文&#xff1a; 小结&#xff1a; TIM3->CCER & ~TIM_CCER_CC2E; 作用概述&#xff1a; 作用是禁用 TIM3 的通道 2&#xff08;CCR2&#xff09;的捕获…...

高能数造全固态电池干法电极高品质原纤化技术:驱动干法和全固态电池制造新进程

技术背景 传统湿法电极制备工艺的局限:传统的湿法电极制备工艺需要使用大量的溶剂来溶解粘结剂和分散活性物质&#xff0c;后续还需要复杂的干燥工序来去除溶剂。这不仅增加了生产成本和能源消耗&#xff0c;溶剂的使用和处理还会带来环境污染和安全隐患。 新能源产业发展的需…...

AI驱动的制造工艺:系统化探索与创新

DeepSeek 技术全景 在当今 AI 技术蓬勃发展的时代,DeepSeek 已成为该领域中一颗耀眼的明星。自 2023 年 7 月 17 日成立以来,这家由知名私募巨头幻方量化孕育而生的公司,迅速在 AI 领域崭露头角 。DeepSeek 的目标是开发顶尖的大语言模型(LLM),并利用数据蒸馏技术打造更精…...

Mac 平台获取地区标识符号

以下是添加了详细中文注释的代码版本&#xff0c;解释每一行代码的作用&#xff1a; #include <CoreFoundation/CoreFoundation.h> #include <vector> #include <string> #include <iostream>// 将 Core Foundation 的字符串(CFStringRef)转换为标准 …...

PyTorch 实战:从 0 开始搭建 Transformer

导入必要的库 python import math import torch import torch.nn as nn from LabmL_helpers.module import Module from labml_n.utils import clone_module_List from typing import Optional, List from torch.utils.data import DataLoader, TensorDataset from torch imp…...

Java 显式锁与 Condition 的使用详解

Java 显式锁与 Condition 的使用详解 在多线程编程中&#xff0c;线程间的协作与同步是核心问题。Java 提供了多种机制来实现线程同步&#xff0c;除了传统的 synchronized 关键字外&#xff0c;ReentrantLock 和 Condition 是更灵活且功能强大的替代方案。本文将详细介绍显式…...

【MySQL】存储引擎 - CSV详解

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

LeetCode算法题(Go语言实现)_62

题目 有两种形状的瓷砖&#xff1a;一种是 2 x 1 的多米诺形&#xff0c;另一种是形如 “L” 的托米诺形。两种形状都可以旋转。 给定整数 n &#xff0c;返回可以平铺 2 x n 的面板的方法的数量。返回对 109 7 取模 的值。 平铺指的是每个正方形都必须有瓷砖覆盖。两个平铺不…...

矿井设备通信破局:ModbusTCP转DeviceNet网关应用实践

矿井设备通信破局&#xff1a;ModbusTCP转DeviceNet网关应用实践 在500米深的金属矿井中&#xff0c;传统人工操控采掘设备存在高风险、低效率问题。某矿业集团引入海希无线遥控器远程控制掘进机&#xff0c;却因通信协议冲突陷入困局&#xff1a;海希遥控器采用DeviceNet协议…...

GrassRoot备份项目

Windows服务项目 Grass.cs using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http.Headers; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Time…...

多级路由器如何避免IP冲突

在多级路由器架构中&#xff0c;避免IP冲突的核心在于合理规划子网、正确配置路由器角色与功能。以下是综合多个搜索结果的解决方案及操作步骤&#xff1a; 一、划分不同子网段 修改LAN口IP地址 主路由器默认LAN口IP为192.168.1.1&#xff0c;次级路由器需更改为不同网段&#…...

VGGNet详解

VGGNet 由牛津大学视觉几何组&#xff08;Visual Geometry Group&#xff09;在2014年提出&#xff0c;凭借极简的 33卷积核堆叠设计 成为经典模型&#xff0c;影响了后续大量网络架构。 1. 网络结构 VGGNet 的核心思想是 通过多层小卷积核&#xff08;33&#xff09;替代大卷…...

TDengine 在新能源行业应用

简介 在当前可再生能源迅速发展的浪潮中&#xff0c;分布式光伏和可再生能源的装机容量已经达到相当可观的规模。尽管新能源的发展得到政策的鼎力扶持&#xff0c;但其并网后对电网的运行调度、供电可靠性以及系统的安全稳定带来诸多新挑战。 分布式光伏&#xff0c;即分布式…...

[人机交互]设计,原型建立和构造

一.建立和构造原型 1.1理解用户需要和技术之间的关系 用户需要和技术之间是一个鸡和蛋的问题 • 用户对产品的理解建立在 与该产品交互 的基础上 • 用户只有在熟悉后&#xff0c;才能 评价 是否需要&#xff0c;及 进一步 的需要 • 构造最终产品需要大量资源 • 原型化 是 …...

C#生成二维码和条形码

C# 实现二维码和条形码生成&#xff1a;从入门到实战 文章目录 C# 实现二维码和条形码生成&#xff1a;从入门到实战一、引言二、准备工作2.1 开发环境搭建2.2 引入相关库 三、生成条形码3.1 条形码基本概念3.2 使用[ZXing.Net](https://ZXing.Net)生成条形码3.2.1 核心代码实现…...

2025.5.8总结(中期审视)

今日记录&#xff1a; 晚上&#xff0c;主管找我聊了关于中期绩效审视的问题。 首先就是让我汇报上半年的工作进展&#xff0c;汇报完后&#xff0c;感觉体现不出自己的工作量&#xff0c;这确实考验个人的汇报能力。 汇报完工作后&#xff0c;主管开始给我提了一些建设性的…...

Pyinstaller编译EXE及反编译

文章目录 适用范围示例文件编译EXE反编译EXE准备工具编译pycdc反编译 反编译得到的文件相关资源下载 适用范围 实测 python3.9可以反编译。从pycdc源代码看&#xff0c;似乎支持到python 3.13。 示例文件 demo.py import sys from PyQt5 import QtWidgets, QtCore, QtGui c…...

3.2.3 掌握RDD转换算子 - 3. 扁平映射算子 - flatMap()

在本节课中&#xff0c;我们深入学习了Spark RDD的flatMap()算子。flatMap()与map()类似&#xff0c;但每个元素可以返回0到多个元素&#xff0c;最终将所有结果合并为一个RDD。通过案例演示&#xff0c;我们首先对单词文件进行了统计&#xff0c;通过map()将每行文本转换为单词…...

深入解析 C# 常用数据结构:特点、区别与优缺点分析

在软件开发中&#xff0c;选择合适的数据结构是提高代码效率和性能的关键。在 C# 中&#xff0c;我们常用的数据结构包括 List、Array、Dictionary<TKey, TValue>、HashSet、Queue、Stack 和 LinkedList。每种数据结构有不同的特点、优缺点和适用场景。本文将结合代码&am…...

LeetCode第284题 - 窥视迭代器

题目 解答一 package leetcode.editor.cn; //leetcode submit region begin(Prohibit modification and deletion) // Java Iterator interface reference: // https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.htmlimport java.util.Iterator; import java.ut…...

克里金模型+多目标优化+多属性决策!Kriging+NSGAII+熵权TOPSIS!

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 克里金模型多目标优化多属性决策&#xff01;KrigingNSGAII熵权TOPSIS&#xff01;&#xff01;matlab2023b语言运行&#xff01; 1.克里金模型&#xff08;Kriging Model&#xff09;是一种基于空间统计学的插值方法…...

驱动开发硬核特训 · Day 30(下篇): 深入解析 lm48100q I2C 音频编解码器驱动模型(基于 i.MX8MP)

作者&#xff1a;嵌入式Jerry 视频教程请关注 B 站&#xff1a;“嵌入式Jerry” 一、背景与目标 在本篇中&#xff0c;我们围绕 TI 的 lm48100q 音频编解码器 展开&#xff0c;深入讲解其作为 I2C 外设如何集成至 Linux 内核音频子系统&#xff08;ASoC&#xff09;&#xff0…...

【RAG】indexing 中的 Hierarchical Indexing(分层索引)

Hierarchical Indexing&#xff08;分层索引&#xff09; 关键词解析&#xff1a; Splits (分割): 原始文档被分割成较小的块。Cluster (聚类): 将语义上相似的文档块分组在一起。Summaries (摘要): 为每个聚类或更高层次的节点生成摘要。RAPTOR (Recursive Abstractive Proc…...

【LeetCode 42】接雨水(单调栈、DP、双指针)

题面&#xff1a; 思路&#xff1a; 能接雨水的点&#xff0c;必然是比两边都低&#xff08;小&#xff09;的点。有两种思路&#xff0c;一种是直接计算每个点的最大贡献&#xff08;也就是每个点在纵向上最多能接多少水&#xff09;&#xff0c;另一种就是计算每个点在横向上…...

【软件设计师:数据库】13.数据库控制与安全

一、数据库语言SQL SQL是结构化查询语言(Structured Query Language)的缩写,其功能包括数据查询、数据操纵、数据定义和数据控制四个部分。 SQL 语言简洁、方便实用、功能齐全,已成为目前应用最广的关系数据库语言。SQL既是自含式语言(联机交互),又是嵌入式语言(宿主语…...

PWN基础-ROP技术-ret2syscall-64位程序栈溢出利用

前置 ret2syscall 的基础我们就不做过多讲解了 利用思路与 32 位类似&#xff0c;只是传参的寄存器是&#xff1a; rdi -> rsi -> rdx -> rcx -> r8 -> r9 我们这里只用到前三个就可以了&#xff0c;以及 rax 还有一个区别就是&#xff1a; 32 位系统调用最…...

基于大模型预测的产钳助产分娩全方位研究报告

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与方法 二、产钳助产分娩概述 2.1 产钳助产定义与历史 2.2 适用情况与临床意义 三、大模型预测原理与数据基础 3.1 大模型技术原理 3.2 数据收集与处理 3.3 模型训练与验证 四、术前预测与准备 4.1 大模型术前风险预…...

二叉树结构的深入学习

目录 1. 节点结构 1.1.值&#xff08;val&#xff09; 1.2.左右孩子节点 2.本质 3.类型 4.遍历方式 树是一种递归的数据结构。具有一个根节点和多个子节点&#xff0c;形成邻接关系&#xff0c;每个节点可以有零个或多个子节点。 树的定义是递归的&#xff0c;由根节点的…...

SVT-AV1源码学习-EbMotionEstimation.h 学习

#ifndef EbMotionEstimation_h //防止文件呗重复包含的宏定义开始标记 #define EbMotionEstimation_h 定义头文件标识符 #include "definitions.h" //包含定义文件 #include "coding_unit.h" //包含编码单元相关文件 #include "me_process.h" //…...

代理服务器

1.准备3台虚拟机 1台当做代理服务器&#xff1b;2台当做真实访问服务器&#xff1b;可以再来一台虚拟机当客户机&#xff0c;也可以使用主机来当客户机。 依次配置服务器 真实服务器&#xff08;配置文件无需更改&#xff09;&#xff1a; 代理服务器&#xff1a; 35 ups…...

数值分析——条件数

1. 条件数的定义与计算 条件数&#xff08;Condition Number&#xff09;用于量化矩阵或函数对输入误差的敏感程度&#xff0c;反映问题的“良态”或“病态”特性。 矩阵条件数的定义 对于一个非奇异方阵 A&#xff0c;其条件数定义为&#xff1a; κ(A)∥A∥⋅∥A−1∥ 其…...

C++ STL 入门:map 键值对容器

C STL 入门&#xff1a;map 键值对容器 一、核心特性与适用场景 map 是 C STL 提供的关联式键值容器&#xff0c;基于红黑树实现&#xff0c;具备以下核心特征&#xff1a; 特性表现形式底层原理键唯一性不允许重复键值红黑树节点键值唯一约束自动排序元素按键升序排列红黑树…...

ESP32-CAM开发板学习(一)

一、Arduino IDE搭建ESP32开发环境 1、安装 Arduino IDE 软件&#xff0c;在官网下载压缩包解压直接使用 官网链接: Arduino IDE 2、修改软件语言&#xff0c;单击左上角 File → Preferences…&#xff0c;把Language改成中文(简体)&#xff0c;保存 3、安装esp32开发板库…...

Arm核的Ubuntu系统上安装Qt

Arm核的Ubuntu系统上安装Qt 一、准备工作 确保可以连接网络 二、安装gcc 1、判断gcc是否安装 命令行输入:gcc -v 2、如果没有安装 输入命令安装: sudo apt install gcc 三、安装g++ 1、判断g++是否安装 命令行输入:g++ -v...

C++GO语言微服务和服务发现

目录 01 03-go-micro简介 02 04-服务发现的简单认识 03 05-consul的安装 04 06-consul常用的命令 05 07-注册服务到consul并验证 06 08-consul健康检查 07 09-consul结合grpc使用-上&#xff08;只实现grpc远程调用&#xff09; 08 10-consul结合grpc使用-中&#xff08…...