使用hypot()计算边界框对角线时直方图出现异常低谷的排查求助
使用hypot()计算边界框对角线时直方图出现异常低谷的排查求助
大家好,我遇到了一个百思不得其解的问题,想请各位帮忙分析一下:
我用第三方计算机视觉库从图像流中检测目标,这个库仅支持用float类型存储边界框的尺寸,不提供double这类更高精度的选项。我需要计算每个边界框的对角线长度,用的是C标准库<math.h>里的hypot()函数,之后把边界框的高度、宽度和对角线数据保存到json文件中。
最近我从保存的json文件读取数据,绘制边界框尺寸的直方图时,发现对角线的直方图总会在某个特定区间出现奇怪的低谷,而高度和宽度的直方图完全正常:
Sample 1
- 高度直方图:

- 宽度直方图:

- 对角线直方图:

低谷出现在图3的1.19到1.26区间
Sample 2
- 高度直方图:

- 宽度直方图:

- 对角线直方图:

低谷出现在图8的1.19到1.26区间
我初步怀疑这个低谷是由对角线的计算方式导致的,于是专门提取了低谷区间内的边界框数据,检查它们的高度和宽度分布,结果并没有发现无穷值、亚正常值这类异常:
Sample1 低谷区间内的尺寸分布
- 高度分布:

- 宽度分布:

Sample2 低谷区间内的尺寸分布
- 高度分布:

- 宽度分布:

更新1:补充代码与数据
为了方便大家复现问题,我整理了计算对角线并写入JSON的C代码,以及绘制直方图的Python代码,还有对应的数据集:
C代码:计算对角线并保存到JSON
代码逻辑步骤:
- 从第三方库获取边界框的高度和宽度(
float类型,单位为像素),转换为英寸(仍为float类型) - 调用
hypot()计算对角线长度(float类型,单位为英寸) - 将高度、宽度、对角线数据写入JSON文件
#include <math.h> #include <json-glib/json-glib.h> #define IMAGE_WIDTH_PIXEL 2304 #define PHYSICAL_WIDTH_INCH 8.0 float pixel_to_inch(float x) { return x * (PHYSICAL_WIDTH_INCH / IMAGE_WIDTH_PIXEL); } float calculate_diagonal(float height, float width) { return hypot(height, width); } void write_to_json(float height, float width, const char* filename) { // 像素转英寸 float height_inch = pixel_to_inch(height); float width_inch = pixel_to_inch(width); // 计算对角线 float diagonal_inch = calculate_diagonal(height_inch, width_inch); // 保存到JSON JsonObject * bboxObj = json_object_new(); json_object_set_double_member(bboxObj, "height", height_inch); json_object_set_double_member(bboxObj, "width", width_inch); json_object_set_double_member(bboxObj, "diagonal", diagonal_inch); JsonObject * rootObj = json_object_new(); json_object_set_object_member(rootObj, "bbox", bboxObj); JsonNode * rootNode = json_node_new(JSON_NODE_OBJECT); json_node_set_object(rootNode, rootObj); JsonGenerator *jsonGen = json_generator_new(); json_generator_set_root(jsonGen, rootNode); json_generator_to_file(jsonGen, filename, NULL); json_node_free(rootNode); json_object_unref(rootObj); g_object_unref(jsonGen); }
Python代码:绘制直方图
用于读取JSON数据并生成我展示的那些直方图:
import numpy as np import pandas as pd import json import matplotlib.pyplot as plt def read_metadata_v1(filepath): data = {} with open(filepath) as f: data = json.load(f) df = pd.json_normalize(data) df.rename(columns={'bbox.height': 'height', 'bbox.width': 'width', 'bbox.diagonal': 'diagonal'}, inplace=True) return df def plot_dim_v1(df, dim_type=None, step_size=None, figure_num=0, figsize=(10, 5)): dim_column_name = '{}'.format(dim_type) data = df[dim_column_name] binBoundaries = np.arange(0, data.max() + step_size, step_size) fig = plt.figure(figsize=figsize) counts, edges, bars = plt.hist(data, density=False, bins=binBoundaries, align='mid', facecolor='#7FB285', edgecolor='#463239') plt.title('Figure {}: Frequency of {} in bbox'.format(figure_num, dim_type.capitalize())) plt.ylabel('Frequency') plt.xlabel('{} (with bin width = {})'.format(dim_type.capitalize(), step_size)) plt.bar_label(bars, labels=[int(float(v)) if v > 0 else '' for v in bars.datavalues], padding=3, rotation=90, label_type='edge') plt.xticks(edges) plt.tick_params(axis='x', rotation=90) plt.margins(y=0.15) fig.tight_layout() plt.show() fig.savefig('Figure {}.png'.format(figure_num)) return edges df1 = read_metadata_v1("sample_data/sample1.json") plot_dim_v1(df1, "height", 0.07, 1) plot_dim_v1(df1, "width", 0.07, 2) diagonal_edges1 = plot_dim_v1(df1, "diagonal", 0.07, 3) df1_trough = df1.loc[(df1['diagonal'] > diagonal_edges1[17]) & (df1['diagonal'] < diagonal_edges1[18])] plot_dim_v1(df1_trough, "height", 0.07, 4) plot_dim_v1(df1_trough, "width", 0.07, 5) df2 = read_metadata_v1("sample_data/sample2.json") plot_dim_v1(df2, "height", 0.07, 6) plot_dim_v1(df2, "width", 0.07, 7) diagonal_edges2 = plot_dim_v1(df2, "diagonal", 0.07, 8) df2_trough = df2.loc[(df2['diagonal'] > diagonal_edges2[17]) & (df2['diagonal'] < diagonal_edges2[18])] plot_dim_v1(df2_trough, "height", 0.07, 9) plot_dim_v1(df2_trough, "width", 0.07, 10)
数据集说明
我准备了包含Sample1和Sample2的数据集,注意数据里只保存了转换为英寸后的尺寸,没有原始像素值。(下载提示:在下载弹窗底部点击“仅继续下载”即可无需注册获取文件)
更新2:修复直方图X轴范围
根据@lastchance的提示,我修改了直方图的分箱逻辑,把最大值也包含进最后一个分箱里,这个修改没有改变现有直方图的柱子,只是补充了包含最大值的缺失分箱:
# 旧的分箱代码 binBoundaries = np.arange(0, data.max(), step_size) # 更新后的分箱代码 binBoundaries = np.arange(0, data.max() + step_size, step_size)
我已经排查了数据本身的异常,也检查了直方图的绘制逻辑,但还是找不到低谷出现的原因。有没有人遇到过类似的问题?或者能给我一些排查方向的建议?
备注:内容来源于stack exchange,提问作者Monica.J




