You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过JavaCV DNN的RetinaFace模型获取人脸及关键点信息

问题解决:JavaCV DNN解析RetinaFace输出获取人脸及关键点

RetinaFace的输出包含3种尺度(stride8/16/32)的检测结果,每个尺度对应三类输出:边界框预测、关键点预测、分类概率。你当前用net.forward()获取的是合并输出,更清晰的方式是按输出层名称单独获取每个层的结果,这样更容易解析。

步骤1:按层名称获取单独输出

替换原有的Mat output = net.forward();,改为逐个获取每个输出层的结果:

// 遍历所有输出层名称,逐个获取结果
for (long i = 0; i < outLayerNames.size(); i++) {
    String layerName = outLayerNames.get(i).getString();
    Mat layerOutput = net.forward(layerName);
    LOGGER.info("Layer {} output shape: {}x{}x{}x{}", 
                layerName, 
                layerOutput.size(0), 
                layerOutput.size(1), 
                layerOutput.size(2), 
                layerOutput.size(3));
}

每个输出层的维度含义:

  • face_rpn_cls_prob_reshape_strideXX:维度为[1, 2, H, W],其中2代表背景/人脸的概率,H和W是该尺度下的特征图尺寸(stride32对应20x20,stride16对应40x40,stride8对应80x80)
  • face_rpn_bbox_pred_strideXX:维度为[1, 4, H, W],4代表边界框的偏移量(dx, dy, dw, dh)
  • face_rpn_landmark_pred_strideXX:维度为[1, 10, H, W],10代表5个关键点的x/y偏移量(左眼、右眼、鼻子、左嘴角、右嘴角)

步骤2:解析分类概率,筛选人脸候选框

对每个尺度的分类概率层,遍历每个特征点,筛选出人脸概率高于阈值(比如0.7)的候选框:

float confidenceThreshold = 0.7f;
// 以stride32为例,其他尺度逻辑一致
Mat clsOutput = net.forward("face_rpn_cls_prob_reshape_stride32");
// 转换为float数组方便访问
float[] clsData = new float[(int)clsOutput.total()];
clsOutput.get(0, 0, clsData);

int H = (int)clsOutput.size(2);
int W = (int)clsOutput.size(3);
int stride = 32;

for (int y = 0; y < H; y++) {
    for (int x = 0; x < W; x++) {
        // 获取人脸概率(索引为1,0是背景)
        float score = clsData[1 * H * W + y * W + x];
        if (score > confidenceThreshold) {
            // 计算候选框的原始坐标(基于特征图的锚点)
            float anchorX = (x + 0.5f) * stride;
            float anchorY = (y + 0.5f) * stride;
            // 后续需要结合bbox_pred的偏移量计算真实框坐标
        }
    }
}

步骤3:解析边界框偏移量,计算真实人脸框

对应每个候选框,从face_rpn_bbox_pred_strideXX中获取偏移量,转换为真实的人脸框坐标:

Mat bboxOutput = net.forward("face_rpn_bbox_pred_stride32");
float[] bboxData = new float[(int)bboxOutput.total()];
bboxOutput.get(0, 0, bboxData);

// 接上面的循环
float dx = bboxData[0 * H * W + y * W + x];
float dy = bboxData[1 * H * W + y * W + x];
float dw = bboxData[2 * H * W + y * W + x];
float dh = bboxData[3 * H * W + y * W + x];

// 计算真实框坐标(基于锚点和偏移量)
float boxCenterX = anchorX + dx * 16; // 16是RetinaFace默认锚框基础宽度
float boxCenterY = anchorY + dy * 16;
float boxWidth = (float)Math.exp(dw) * 16;
float boxHeight = (float)Math.exp(dh) * 16;

// 转换为左上角和右下角坐标
float x1 = boxCenterX - boxWidth / 2;
float y1 = boxCenterY - boxHeight / 2;
float x2 = boxCenterX + boxWidth / 2;
float y2 = boxCenterY + boxHeight / 2;

// 转换回原始图像尺寸
float scaleX = (float)image.cols() / 640;
float scaleY = (float)image.rows() / 640;
x1 *= scaleX;
y1 *= scaleY;
x2 *= scaleX;
y2 *= scaleY;

步骤4:解析关键点坐标

从face_rpn_landmark_pred_strideXX中获取5个关键点的偏移量,计算真实坐标:

Mat landmarkOutput = net.forward("face_rpn_landmark_pred_stride32");
float[] landmarkData = new float[(int)landmarkOutput.total()];
landmarkOutput.get(0, 0, landmarkData);

// 接上面的循环
float[] landmarks = new float[10];
for (int k = 0; k < 5; k++) {
    float lx = landmarkData[2*k * H * W + y * W + x];
    float ly = landmarkData[(2*k+1) * H * W + y * W + x];
    landmarks[2*k] = anchorX + lx * 16;
    landmarks[2*k+1] = anchorY + ly * 16;
    // 转换回原始图像尺寸
    landmarks[2*k] *= scaleX;
    landmarks[2*k+1] *= scaleY;
}
// landmarks数组依次是:左眼x、左眼y、右眼x、右眼y、鼻子x、鼻子y、左嘴角x、左嘴角y、右嘴角x、右嘴角y

步骤5:非极大值抑制(NMS)去重

不同尺度会检测到重复的人脸框,需要用NMS去除重叠框:

// 收集所有符合条件的人脸框(x1,y1,x2,y2,score),转换为OpenCV格式
List<Rect> boxes = new ArrayList<>();
List<Float> scores = new ArrayList<>();
// (通过上面步骤收集所有符合阈值的框)

// 执行NMS
MatOfRect boxesMat = new MatOfRect(boxes.toArray(new Rect[0]));
MatOfFloat scoresMat = new MatOfFloat(scores.toArray(new Float[0]));
MatOfInt indices = new MatOfInt();
float nmsThreshold = 0.3f;
opencv_imgproc.dnnNMSBoxes(boxesMat, scoresMat, confidenceThreshold, nmsThreshold, indices);

// 获取NMS后的结果
int[] idxArray = indices.toArray();
for (int idx : idxArray) {
    Rect finalBox = boxes.get(idx);
    float finalScore = scores.get(idx);
    // 对应的关键点也需要同步筛选
}

内容的提问来源于stack exchange,提问作者Jeffy Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 13:17:53