ImageMask的使用介绍
ImageMask是DTS中新增的一种特殊数据类型。其核心功能是通过空间过滤提升AI分析的精度。支持用户在图片上通过矢量绘制任意形状的掩膜区域,对AI分析服务的分析结果做二次的过滤使用,筛选出在自己关注区域内的分析结果,提高分析结果的准确度。内置WGS84地理坐标与像素坐标双向转换模块,支持遥感影像等地理空间数据的掩膜分析场景,适用于自动驾驶路径规划、卫星图像分析等垂直领域。
创建
- 选择目标产品,点击编辑进入产品信息详情页面->选择产品Profile Tab页面。
- 点击编辑草稿,选择添加一个属性。
- 输入属性名称和标识符“area”,选择数据类型为ImageMask(图片掩膜)
- 完成后点击退出编辑。
- 在产品Profile所有编辑完成后,需点击发布 。发布后的产品Profile才能被DTS识别。

编辑
- 选择目标产品下的实体。
- 点击编辑进入实体详情页。
- 选择“实体状态”Tab页下的“属性值”标签页;
- 选择“area”后的编辑功能,进入掩膜绘制页面。

-
选择从本地电脑中上传一张图片或者从实体属性中选择某个属性值作为图片来源。
注意 :默认不会保留临时选择的图片,【保存到属性】方可持久化存储,图片将更新至对应属性值。未保存的图片在退出后自动清除。 -
设置图片来源后进入绘制;
-
默认进入即为绘制状态,左键单击开始,双击结束,绘制时使用Escape键回退删除上一个点;
-
在图片上绘制任意形状的掩膜区域,默认绘制为正掩膜,即只对此区域内的数据感兴趣。可修改为负掩膜,即排除此区域内的数据。

-
默认绘制的坐标系为图像坐标系,坐标原点在左上角。如需将绘制的掩膜转换到自定义图像坐标系或地理坐标系下,可设置目标坐标系的参数。用于之后的脚本中进行坐标转换。

-
绘制完成,点击保存。
功能操作提示:
- 鼠标滚轮缩放;
- 按住鼠标右键移动;
- 选中
“拾取”功能,进入“拾取状态”。左键单击选中掩膜,使用Back scape键删除绘制的掩膜;左键单击选中顶点,鼠标拖拽修改顶点位置; - 取消选中
“拾取”功能,进入“绘制状态”。左键单击开始,双击结束,绘制时使用Escape键回退删除上一个点。默认为“绘制状态”。 - 选择
“适应图片到画布”功能,图片大小变为与默认画布大小一致。 - 选择
“切换图片”功能,可修改背景图片的来源,重置图片会重置所有掩膜区域和目标坐标系的参数。 - 选择
“目标坐标系”功能,可设置目标坐标系的参数。自定义原点,选择“绘制原点”后在图片上点击选择新的原点。在平面地图坐标系下,可采用地理坐标,通过输入原点坐标值和设置空间分辨率确定后续转换地理坐标的所需参数。设置空间分辨率,在图片上绘制一条线段。 - 选择
“清空掩膜”功能,删除当前绘制的所有掩膜。 - 选择
“查看Json描述”功能,可查看当前ImageMask的完整属性值内容; - 选择
“保存”功能,保存当前绘制的掩膜和目标坐标系参数;
使用
- 选择目标产品,点击编辑进入产品信息详情页面->选择消息解析脚本 Tab页面。
- 在实体映射(上行)脚本中,写入收到AI分析服务结果之后与ImageMask进行二次过滤的逻辑;
- 脚本编写完成后,保存脚本。
作者:北京-产品-yantings 创建时间:2025-05-28 14:08
最后编辑:北京-产品-yantings 更新时间:2026-06-22 10:29
/**
* 将第三方服务的自定义数据格式转换为数字孪生服务协议的数据格式,设备、属性上报、事件上报到DTS时调用。
* @param {object} body - 入参
* @param {object} body._bv_callback_message - 服务回调参数,回调时存在
* @param {string} body._bv_callback_message.productId - 产品编号
* @param {string} body._bv_callback_message.deviceId - 设备编号
* @param {string} body._bv_callback_message.identifier - 事件的唯一标识符,回调方式为事件上报时存在
* @param {object} body._bv_callback_message._bv_service_callback - 服务调用参数
* @param {string} body._bv_callback_message._bv_service_callback.requestId - 用于标记请求
* @param {string} body._bv_callback_message._bv_service_callback.identifier - 服务唯一标识
* @param {string} body._bv_callback_message._bv_service_callback.version - 消息格式版本
* @param {number} body._bv_callback_message._bv_service_callback.timestamp - 服务调用时间
* @param {number} flag - 入参,0 设备上报 1 属性上报 2 事件上报
* @returns {object} 出参
*/
function rawDataToProtocol(body, flag) {
var jsonMap = {};
var inputData = body;
if (flag === 0) {
/**
* @returns {string} return.deviceId - 设备编号(保证唯一),必传
* @returns {string} return.deviceName - 设备名称(最大长度255),必传
* @returns {number} return.deviceStatus - 设备状态 0:禁用 1:启用(默认),非必传
* @returns {number} return.deviceType - 设备类型 0:动态资源 1:静态资源,必传
* @returns {string} return.location - 设备位置纬经度(默认0 0 0 = latitude longitude elevation),非必传
* @returns {string} return.version - 版本号,默认default,非必传
* @returns {Object} return.payload - 扩展数据,产品属性,可用于更新设备属性值(该数据根据产品Profile中定义的属性进行过滤,已存在定义的数据会保留,其余数据则弃用),非必传
*/
return body;
}
if (flag === 1) {
/**
* @returns {string} return.deviceId - 设备编号(保证唯一),必传
* @returns {string} return.deviceSecret - 设备签名(不传时将系统匹配),非必传
* @returns {string} return.deviceName - 设备名称(最大长度255),非必传
* @returns {number} return.deviceStatus - 设备状态 0:禁用 1:启用(默认),非必传
* @returns {string} return.location - 设备位置纬经度(默认0 0 0 = latitude longitude elevation),非必传
* @returns {number} return.timestamp - 上行数据传输时间。时间戳格式为数值,值为自GMT 1970年1月1日0时0分到当前时间点所经过的毫秒数,非必传
* @returns {string} return.version - 版本号,默认default,非必传
* @returns {Object} return.payload - 扩展数据,产品属性,可用于更新设备属性值(该数据根据产品Profile中定义的属性进行过滤,已存在定义的数据会保留,其余数据则弃用),非必传
*/
if (inputData._bv_callback_message) {
var bv_callback_message = inputData._bv_callback_message;
var bv_service_callback = bv_callback_message._bv_service_callback;
var payload = inputData.payload ? inputData.payload : {};
if (bv_service_callback.identifier == "captureServerImage" || bv_service_callback.identifier == "captureServerVideo") {
if (inputData.result != 0) {
return;
}
if (bv_service_callback.identifier == "captureServerImage") {
var resourceUrl = FileStorage.cache(inputData.file, inputData.suffix, bv_callback_message.deviceId);
payload.deviceImage = resourceUrl;
}
if (bv_service_callback.identifier == "captureServerVideo") {
var resourceUrl = FileStorage.disk(inputData.file, inputData.suffix);
payload.deviceVideoUrl = resourceUrl;
}
}
if (bv_service_callback.identifier == "video_attractive_score") {
payload.score = inputData.score;
}
if (bv_service_callback.identifier == "captureImage") {
//payload.deviceImage = inputData.payload.deviceImage.replace("172.27.240.1","10.132.25.2");
}
jsonMap.deviceId = bv_callback_message.deviceId;
jsonMap.payload = payload;
return jsonMap;
} else {
return body;
}
}
if (flag === 2) {
/**
* @returns {string} return.deviceId - 设备编号(保证唯一),必传
* @returns {string} return.deviceSecret - 设备签名(不传时将系统匹配),非必传
* @returns {string} return.deviceName - 设备名称(最大长度255),非必传
* @returns {number} return.deviceStatus - 设备状态 0:禁用 1:启用(默认),非必传
* @returns {string} return.location - 设备位置纬经度(默认0 0 0 = latitude longitude elevation),非必传
* @returns {string} return.identifier - 事件的唯一标识符,必传
* @returns {number} return.timestamp - 上行数据传输时间。时间戳格式为数值,值为自GMT 1970年1月1日0时0分到当前时间点所经过的毫秒数,非必传
* @returns {string} return.version - 版本号,默认default,非必传
* @returns {Object} return.payload - 扩展数据,事件的输出参数(该数据根据产品Profile中定义的事件输出参数进行过滤,已存在定义的数据会保留,其余数据则弃用),非必传
*/
if (inputData._bv_callback_message) {
jsonMap['deviceId'] = inputData._bv_callback_message.deviceId;
jsonMap['identifier'] = inputData._bv_callback_message.identifier;
// 添加mask过滤
var entityAttribute = CoreApi.getEntityAttribute(inputData._bv_callback_message.productId, inputData._bv_callback_message.deviceId);
entityAttribute = JSON.parse(entityAttribute);
if (typeof entityAttribute === 'object' && entityAttribute !== null && entityAttribute.hasOwnProperty('TestImageMask') && entityAttribute.hasOwnProperty('deviceImage')) {
const mask = JSON.parse(entityAttribute.TestImageMask);
inputData = maskFilter(inputData, mask)
inputData.boundingBoxImage = drawBox(inputData, entityAttribute.deviceImage)
}
// 存在文件资源时将存储至网关服务下
if (inputData.heatMapImage) {
inputData.heatMapImageUrl = FileStorage.disk(inputData.heatMapImage, "jpg");
}
if (inputData.boundingBoxImage) {
inputData.boundingBoxImageUrl = FileStorage.disk(inputData.boundingBoxImage, "jpg");
}
jsonMap['payload'] = {
"analysisTime": inputData.analysisTime,
"objectNumber": inputData.objectNumber,
"objectBoundingBox": inputData.objectBoundingBox,
"boundingBoxImageUrl": inputData.boundingBoxImageUrl,
"heatMapImageUrl": inputData.heatMapImageUrl
}
}
return jsonMap;
}
}
//在图片上绘制分析结果框
function drawBox(box,image) {
const labels = [];
const boxes = [];
Object.keys(box.objectBoundingBox).forEach(function (key) {
const value = box.objectBoundingBox[key];
boxes.push(JtsGeometry.createCoordinatesXYXY(value));
labels.push(key);
});
return JtsGeometry.drawBox(image, boxes, "Number:" + box.objectNumber, labels);
}
/**
* 根据掩码(mask)过滤 box 对象中的边界框信息。
* @param {object} inputData - 包含边界框信息的对象,其中 objectBoundingBox 属性存储具体的边界框数据。
* @param {object} mask - 掩码对象,包含几何信息用于与边界框进行交集判断。
*/
function maskFilter(inputData, mask) {
if (typeof inputData.objectBoundingBox !== 'object' && inputData.objectBoundingBox === null) {
console.log("inputData.objectBoundingBox 不是有效的对象")
return inputData;
}
const in_mask = mask.properties.mask_type === "positive";
var objectBoundingBoxObj = {};
var objectNumber = 0;
for (const key in inputData.objectBoundingBox) {
let value = inputData.objectBoundingBox[key];
// value保留三位小数 double类型
for (let i = 0; i < value.length; i++) {
if (typeof value[i] !== 'number') {
value[i] = parseFloat(value[i].toString());
}
value[i] = parseFloat(value[i].toFixed(3));
}
// 使用 JtsGeometry 工具类将边界框坐标转换为多边形所需的坐标数组
const boxCoor = JtsGeometry.createCoordinatesXYXY(value);
// 创建边界框对应的多边形
let boxPolygon = JtsGeometry.createPolygon(boxCoor);
for (let i = 0; i < mask.geometry.annotations.length; i++) {
const maskCoor = JtsGeometry.createCoordinates(mask.geometry.annotations[i].coor);
const maskPolygon = JtsGeometry.createPolygon(maskCoor);
// 如果 in_mask 为 true 且边界框多边形和掩码多边形有交集
if (in_mask && JtsGeometry.isIntersects(maskPolygon, boxPolygon)) {
//console.log("box和mask有交集,交集面积为", JtsGeometry.intersectionArea(maskPolygon, boxPolygon))
objectBoundingBoxObj[key] = value;
objectNumber++;
break;
}
// 如果 in_mask 为 false 且边界框多边形和掩码多边形没有交集
if (!in_mask && !JtsGeometry.isIntersects(maskPolygon, boxPolygon)) {
//console.log("负掩码,box和mask没有交集即为有效区域")
objectBoundingBoxObj[key] = value;
objectNumber++;
break;
}
}
}
inputData.objectBoundingBox = objectBoundingBoxObj;
inputData.objectNumber = objectNumber;
return inputData;
}
作者:北京-产品-yantings 创建时间:2025-05-28 14:08 更新时间:2025-08-19 15:31 The above content shows the entire, complete file contents of the requested file.
作者:北京-产品-yantings 创建时间:2025-05-28 14:08
最后编辑:北京-产品-yantings 更新时间:2026-07-16 17:09
最后编辑:北京-产品-yantings 更新时间:2026-07-16 17:09