Java+OpenCV实现年龄性别识别时出现Vector Subscript out of range错误
问题分析与修复
核心错误原因
你遇到的Vector Subscript out of range错误,根源是对OpenCV DNN人脸检测模型输出的detections矩阵维度理解错误:
- TensorFlow版本的人脸检测模型输出是4维矩阵,形状为
[1, 1, N, 7],其中N是检测到的候选框数量,每个候选框包含7个数据:[image_id, label, confidence, x1, y1, x2, y2] - 你当前的代码错误地用2维索引访问4维矩阵,并且循环条件错误地使用了矩阵的height(实际为1),导致越界访问。
另外还有两个次要问题:
- 未判断
cap.read(frame)是否成功,若摄像头读取失败会生成空Mat,后续操作会报错 cap.release()放在while(true)循环内部,第一次循环就释放了摄像头,后续循环无法读取帧
修复后的代码
package AgeGenderCV; import java.util.ArrayList; import java.util.List; import org.opencv.core.Core; import org.opencv.core.Point; import org.opencv.core.Mat; import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.dnn.Dnn; import org.opencv.dnn.Net; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import org.opencv.videoio.VideoCapture; public class AgeGenderCV { private static final String[] GENDER_LIST = {"Male", "Female"}; private static final String[] AGE_LIST = {"(0-2)", "(4-6)", "(8-12)", "(15-20)", "(25-32)", "(38-43)", "(48-53)", "(60-100)"}; private static final double[] MODEL_MEAN_VALUES = {78.4263377603, 87.7689143744, 114.895847746}; private static final double CONFIDENCE_THRESHOLD = 0.7; public void ProcessImage(){ // Load networks Net faceNet = Dnn.readNetFromTensorflow("opencv_face_detector_uint8.pb", "opencv_face_detector.pbtxt"); Net ageNet = Dnn.readNetFromCaffe("age_deploy.prototxt", "age_net.caffemodel"); Net genderNet = Dnn.readNetFromCaffe("gender_deploy.prototxt", "gender_net.caffemodel"); // Open camera stream VideoCapture cap = new VideoCapture(0); // 检查摄像头是否成功打开 if (!cap.isOpened()) { System.err.println("无法打开摄像头"); return; } while (true) { Mat frame = new Mat(); // 检查帧是否读取成功 if (!cap.read(frame) || frame.empty()) { System.err.println("无法读取帧,退出循环"); break; } List<Rect> bboxes = new ArrayList<>(); Size size = new Size(300,300); Scalar scalar = new Scalar(104, 117, 123); Mat blob = Dnn.blobFromImage(frame, 1.0, size, scalar, false, false); faceNet.setInput(blob); Mat detections = faceNet.forward(); // 获取候选框数量:detections是[1,1,N,7],第三维的大小是N int numDetections = detections.size(2); for (int i = 0; i < numDetections; i++) { // 正确访问4维矩阵的元素:[0, 0, i, 2]对应confidence double confidence = detections.get(new int[]{0, 0, i, 2})[0]; if (confidence > CONFIDENCE_THRESHOLD) { // 提取坐标 double x1 = detections.get(new int[]{0, 0, i, 3})[0] * frame.cols(); double y1 = detections.get(new int[]{0, 0, i, 4})[0] * frame.rows(); double x2 = detections.get(new int[]{0, 0, i, 5})[0] * frame.cols(); double y2 = detections.get(new int[]{0, 0, i, 6})[0] * frame.rows(); Rect bbox = new Rect((int)x1, (int)y1, (int)(x2-x1), (int)(y2-y1)); bboxes.add(bbox); Imgproc.rectangle(frame, new Point(x1, y1), new Point(x2, y2), new Scalar(0, 255, 0), 2); } } if (bboxes.isEmpty()) { System.out.println("未检测到人脸,继续下一帧"); continue; } for (Rect bbox : bboxes) { Mat face = new Mat(frame, bbox); Size sizeb = new Size(227,227); Scalar scalarb = new Scalar(MODEL_MEAN_VALUES); blob = Dnn.blobFromImage(face, 1.0, sizeb, scalarb, false); // 性别检测 genderNet.setInput(blob); Mat genderPreds = genderNet.forward(); int genderIdx = Core.minMaxLoc(genderPreds).maxLoc.x > 0 ? 1 : 0; String gender = GENDER_LIST[genderIdx]; System.out.println("性别 : " + gender + ", 置信度 = " + genderPreds.get(0, genderIdx)[0]); // 年龄检测 ageNet.setInput(blob); Mat agePreds = ageNet.forward(); agePreds = agePreds.reshape(1, 1); Core.MinMaxLocResult mm = Core.minMaxLoc(agePreds); int ageIdx = (int) mm.maxLoc.x; System.out.println("年龄 : " + AGE_LIST[ageIdx] + ", 置信度 = " + agePreds.get(0, ageIdx)[0]); } } // 循环结束后释放摄像头 cap.release(); } public static void main(String args[]) { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); AgeGenderCV example = new AgeGenderCV(); example.ProcessImage(); } }
关键修改点
- 修正detections矩阵访问:使用
detections.get(new int[]{0, 0, i, 2})来访问4维矩阵的元素,获取候选框的置信度和坐标 - 正确获取候选框数量:通过
detections.size(2)获取第三维的大小(即检测到的候选框总数) - 添加摄像头和帧检查:判断
cap.isOpened()和cap.read(frame)是否成功,避免空矩阵操作 - 调整cap.release()位置:移到while循环外部,确保循环结束后再释放摄像头
- 修正性别检测索引:通过
minMaxLoc获取最大置信度的索引,避免直接用genderPreds.get(0,0)[0]导致的类型转换错误
内容的提问来源于stack exchange,提问作者Tom C
相关产品推荐
相关产品推荐

