Android Kotlin中OpenCV视频角点检测遇Assertion failed错误求解
错误原因及解决方案
错误根源
报错Assertion failed (src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4)的核心原因:
- Harris角点检测输出类型不兼容:
Imgproc.cornerHarris()的输出cornerMat是32位浮点型矩阵(CV_32FC1),但Utils.matToBitmap()仅支持8位单通道/三通道/四通道的Mat,无法直接转换为Bitmap。 - 额外问题:代码中
srcMat初始化为CV_8UC1,但从彩色Bitmap转换而来的Mat实际是CV_8UC3(BGR格式),类型不匹配会导致数据异常。
修复步骤及代码修改
1. 修正Mat初始化类型
srcMat:无需提前指定类型,直接用空Mat,Utils.bitmapToMat会自动匹配Bitmap的类型(彩色Bitmap对应CV_8UC3)。cornerMat:初始化为32位浮点型,匹配Harris检测的输出要求。
2. 转换Harris输出为可显示的8位图像
对cornerMat做归一化和类型转换,将浮点值缩放到0-255的8位范围:
- 用
Core.normalize()将矩阵值归一到0-1区间; - 用
Imgproc.convertScaleAbs()将归一化后的浮点值转换为8位无符号整数(CV_8UC1)。
3. 其他代码优化
- 处理
frameNum为空的边界情况; - 控制循环帧率,避免处理速度过快;
- 及时释放Mat资源,防止内存泄漏;
- 避免重复复用同一个Bitmap对象,创建新Bitmap用于显示结果。
修改后的完整代码
class MainActivity : AppCompatActivity() { @RequiresApi(Build.VERSION_CODES.P) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 初始化OpenCV,建议放在Application中避免重复初始化 if (!OpenCVLoader.initDebug()) { Log.e("OpenCV", "初始化失败") return } val videoView = findViewById<VideoView>(R.id.videoView) val imgView = findViewById<ImageView>(R.id.imageView) val uri = Uri.parse("android.resource://" + packageName + "/" + R.raw.video_name) videoView.setVideoURI(uri) videoView.start() val retriever = MediaMetadataRetriever() val context = applicationContext retriever.setDataSource(context, uri) val frameNumStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT) val frameNum = frameNumStr?.toInt() ?: run { Log.e("VideoFrame", "无法获取视频帧数") return@onCreate } thread { try { // 复用Mat,减少内存开销 val srcMat = Mat() val grayMat = Mat() val cornerMat = Mat() val normalizedMat = Mat() val displayMat = Mat() // 假设视频帧率为30fps,控制每帧处理间隔 val frameDelay = (1000 / 30).toLong() for (i in 0 until frameNum) { val bitframe = retriever.getFrameAtIndex(i) ?: continue val f_width = bitframe.width val f_height = bitframe.height // Bitmap转Mat,自动匹配类型 Utils.bitmapToMat(bitframe, srcMat) // 灰度化 Imgproc.cvtColor(srcMat, grayMat, Imgproc.COLOR_BGR2GRAY) // Harris角点检测,输出为CV_32FC1 Imgproc.cornerHarris(grayMat, cornerMat, 3, 5, 0.04) // 归一化到0-1范围 Core.normalize(cornerMat, normalizedMat, 0.0, 1.0, Core.NORM_MINMAX) // 转换为8位无符号整数(CV_8UC1) Imgproc.convertScaleAbs(normalizedMat, displayMat) // 创建新Bitmap用于显示结果,避免覆盖原帧Bitmap val resultBitmap = Bitmap.createBitmap(f_width, f_height, Bitmap.Config.ARGB_8888) Utils.matToBitmap(displayMat, resultBitmap) // 更新UI Handler(Looper.getMainLooper()).post { imgView.setImageBitmap(resultBitmap) } // 控制帧率,避免处理过快 Thread.sleep(frameDelay) } // 释放Mat资源 srcMat.release() grayMat.release() cornerMat.release() normalizedMat.release() displayMat.release() retriever.release() } catch (e: Exception) { e.printStackTrace() } } } }
关键修改点说明
- Mat类型匹配:
cornerMat初始化为CV_32FC1,符合Harris检测的输出要求; - 归一化与类型转换:通过
Core.normalize()和Imgproc.convertScaleAbs()将浮点矩阵转为可显示的8位图像; - 内存优化:复用Mat对象,避免循环中频繁创建;
- 帧率控制:添加
Thread.sleep()使处理速度与视频帧率匹配; - 边界处理:增加对
frameNum和bitframe为空的判断,避免空指针异常。
内容的提问来源于stack exchange,提问作者y_ok
相关产品推荐
相关产品推荐

