改图像参数失败,多半不是权限问题,是「值超范围」——而设备对超范围的处理并不一致。 正确顺序永远是三步:读当前值、读允许范围、用代码判断合法性再写。
上一节是「设备身上有什么接口」,这一节开始进「参数怎么改」。
图像配置是 imaging service 的地盘,注意它是 ver20(07.01.03 那张表里的例外之一)。
一、先读范围,再改值
GetImagingSettings 给当前值,GetOptions 给允许范围,两个响应要对着看:
| 参数 | 当前值 | 最小值 | 最大值 | 合法吗 |
|---|---|---|---|---|
| Brightness | 50 | -100 | 100 | 是 |
| Contrast | 50 | — | — | — |
| Sharpness | 50 | 0 | 100 | 是 |
| ColorSaturation | 50 | — | — | — |
最后那一列是用代码算的:float(lo) <= float(cur) <= float(hi),一行判断。
这个检查必须做在前面,因为 ONVIF 对超范围的处理不一致:
有的设备回 Fault,有的静默夹到边界值,还有的直接重启。
「静默夹到边界」最坑——你设了 200,设备接受 200 但实际用 100,
回读还是 100,你以为成功了。
所以改完必须回读一次,不能拿「没报错」当成功。
第二、四行是 —,因为这台设备的 GetOptions 里没给这两个参数的范围——
没给范围不等于没有范围,只说明「设备没告诉你」,这时候只能试、然后再回读。
二、曝光三个字段,单位各不相同
| 字段 | 本例的值 | 单位 |
|---|---|---|
Mode |
AUTO |
AUTO 时下面两个由设备定 |
ExposureTime |
16 | 微秒(16 就是万分之 1.6 秒) |
Gain |
6 | dB(增益,大了噪点多) |
第二行那个单位是「微秒」,不是「毫秒」也不是「秒」——
16 微秒在白天是正常的,但要是在夜里报 16 微秒,画面肯定黑。
所以「曝光时间」这个数要结合光线看:夜里的正常值是几万微秒(几十毫秒)。
第三行的增益有个取舍:曝光长了会拖影(运动模糊),增益大了会噪点。
自动曝光就是在两者之间找平衡,而平衡点取决于「你要清晰还是要流畅」。
这三个字段里,只有 Mode 是安全可改的:
手动改 ExposureTime 和 Gain 要在 Mode=MANUAL 下才有意义,
AUTO 下改了也会被设备覆盖。很多人「改了没反应」就是这个原因,不是设备不听话。
三、Set 是整块回写
这条规则和下一节的媒体配置完全一样,所以在这里就要建立起来:
| 做法 | 后果 | 对不对 |
|---|---|---|
| 只传要改的字段 | 没传的被当成清空或重置 | 错 |
| 整块回写 | 没动的字段原样带回去 | 对 |
第一行的后果比想象中严重: 有的设备会把没传的字段重置成出厂值——于是你只想改个亮度,结果分辨率也变了。 这种故障最难查,因为「改了 A 结果 B 变了」不符合直觉。 正确做法是三步:① 读完整块;② 只改目标字段;③ 整块写回去。 这个「读-改-写」模式在 ONVIF 里到处都是(媒体、网络、OSD 都是这个套路), 它不优雅,但有一个好处:不会误删你没注意到的字段。
四、为什么 imaging 是 ver20
imaging 是少数几个 ver20 的 service,和 ptz 一样:
| service | 版本 | 说明 |
|---|---|---|
device |
ver10 |
从第一版就没大改过 |
media |
ver10 |
接口稳定 |
imaging |
ver20 |
2.0 版有不兼容改动 |
ptz |
ver20 |
两套并存,老设备只认 ver10 |
第三行的改动主要在「聚焦」和「曝光」的参数结构上,老客户端的解析代码在新设备上会拿不到值。
所以接设备时要看 GetServices 里报的是哪个版本,不能按「都是 ver10」的假设写死。
第四行更麻烦:ptz 的两套是并存而不是替换,
所以代码里得准备两个命名空间常量,按 GetServices 的结果选一个用(07.02.12 会用到)。
五、动手 Demo:图像配置,先查范围再改值
Demo 先读当前值,再读允许范围,然后逐项判断「当前值合法吗」。 最后改一个参数再回读,验证「设备是不是真按你说的改了」。
_a, b, _n = S.call(port, "imaging", "GetImagingSettings",
"<timg:GetImagingSettings>"
"<timg:VideoSourceToken>VideoSource_1"
"</timg:VideoSourceToken></timg:GetImagingSettings>")
r = ET.fromstring(b)
cur = {}
for k in ("Brightness", "Contrast", "Sharpness", "ColorSaturation"):
e = r.find(".//{%s}%s" % (S.NS_TT, k))
cur[k] = e.text if e is not None else "-"
_a, b2, _n = S.call(port, "imaging", "GetOptions",
"<timg:GetOptions><timg:VideoSourceToken>VideoSource_1"
"</timg:VideoSourceToken></timg:GetOptions>")
r2 = ET.fromstring(b2)
for k, v in cur.items():
lo = r2.find(".//{%s}%s/{%s}Min" % (S.NS_TT, k, S.NS_TT))
hi = r2.find(".//{%s}%s/{%s}Max" % (S.NS_TT, k, S.NS_TT))
ok = "-"
if lo is not None and hi is not None and v != "-":
ok = "是" if float(lo.text) <= float(v) <= float(hi.text) else "否"
print(k, v, lo.text if lo is not None else "-",
hi.text if hi is not None else "-", ok)
实跑输出:
--------------------------------------------------------------------
图像配置,先查范围再改值
--------------------------------------------------------------------
**很多人改图像参数失败,不是权限问题,是「值超范围」。**
这一节先读当前值,再读允许范围,**用代码判断合法性**。
**当前值和允许范围对照:**
参数 当前值 最小值 最大值 合法吗
------------------------------------------------------------------
Brightness 50 -100 100 **是**
Contrast 50 — — —
Sharpness 50 0 100 **是**
ColorSaturation 50 — — —
**最后那一列是用代码算的**: **`float(lo) <= float(cur) <= float(hi)`,一行判断。**
**这个检查必须做在前面**, **因为 ONVIF 对超范围的处理不一致**:
**有的设备回 Fault,有的设备静默夹到边界值,还有的直接重启。**
**「静默夹到边界」最坑**——
**你设了 200,设备接受 200 但实际用 100,回读还是 100,你以为成功了。**
**曝光那两个字段最容易搞混:**
字段 值 单位
--------------------------------------------------------------------------
`Mode` AUTO **`AUTO` 时下面两个由设备自己定**
`ExposureTime` 16 **微秒**(16 就是 1/62500 秒)
`Gain` 6 **dB**(增益,大了噪点多)
**第二行那个单位是「微秒」,不是「毫秒」也不是「秒」**——
**16 微秒在白天是正常的,但要是在夜里报 16 微秒,画面肯定黑。**
**所以「曝光时间」这个数要结合光线看**: **夜里的正常值是几万微秒(几十毫秒)。**
**第三行的增益有个取舍**:
**曝光长了会拖影(运动模糊),增益大了会噪点。**
**自动曝光就是在两者之间找平衡,而平衡点取决于「你要清晰还是要流畅」。**
这台设备的 GetOptions 只给了 Brightness 和 Sharpness 的范围,另外两个返回 —,
所以「合法吗」那一列也是 —,不是「否」。
「没数据」和「不合法」必须分开写——糊在一起就会得出「当前值非法」的错误结论。
判断用的是链式比较 lo <= v <= hi,这是 Python 的特性,等价于 lo <= v and v <= hi,
但它只求值一次 v,所以在有副作用时更安全。
换成 C 就要写两个 &&。
最后一个「改完回读」我没做——因为模拟设备的 SetImagingSettings 是「记账式」的,
写完回读一定能读回来,这个验证没有信息量。
真设备上这一步必须做,因为「静默夹到边界」这个行为只有回读才看得见。
30 秒实验
- 把
Brightness的当前值改成 200,看「合法吗」那一列变成否。 - 把
float(lo.text) <= float(v) <= float(hi.text)拆成两句比较,看结果是否一致。 - 打印整个
GetOptions的响应,数一数它到底给了几个参数的范围。
常见误区
- 以为改参数失败是权限问题。 多半是值超范围,而且设备对超范围的处理不一致, 有的静默夹边界。
- 以为
ExposureTime的单位是毫秒。 是微秒,白天 16 微秒正常,夜里要几万微秒。 - 只传要改的字段去 Set。 没传的字段可能被重置,必须整块回写。
自测三问(点开对答案)
1. 改图像参数的正确顺序是什么? —— **读当前值 → 读允许范围 → 判断合法性 → 整块写回 → 回读验证。** **少了回读这一步,就发现不了「静默夹边界」。** 2. `AUTO` 模式下改 `ExposureTime` 有意义吗? —— **没有。** `AUTO` 时曝光由设备自己定,**改了会被覆盖**,要手动必须先切 `MANUAL`。 3. 为什么 `imaging` 是 `ver20` 而 `media` 是 `ver10`? —— 因为 **2.0 版对聚焦和曝光的参数结构做了不兼容改动**,**只能开新命名空间**; `media` 一直没大改,所以还是 `ver10`。下一节讲媒体配置:一个 Profile 里到底套了几层,以及「改分辨率」为什么不是改 URL。
- ☐ (本节完,下一节 07.02.09 媒体配置)