【android opencv学习笔记】Day 17: 目标追踪(MeanShift)
均值漂移MeanShift目标追踪实现在计算机视觉中目标追踪是核心技术之一而MeanShift均值漂移是无需训练模型、轻量高效的经典追踪算法。它结合直方图反向投影能在连续帧/不同图片中自动锁定目标位置广泛应用于移动端人脸、物体、肤色追踪场景。核心技术原理1. 算法整体流程MeanShift 目标追踪分为两大阶段全程依赖HSV 色调直方图抗光照干扰比 BGR 更稳定训练阶段在参考图中框选目标ROI→ 提取目标的色调H直方图作为特征模板追踪阶段对新图像做直方图反向投影 → 生成目标概率图 → MeanShift 迭代搜索 → 锁定目标位置。2. 关键原理详解1为什么用 HSV 色彩空间BGR 空间受亮度影响极大光线变化会直接导致追踪失效HSV 空间将颜色色调 H、饱和度S、亮度V分离仅用色调 H描述目标颜色彻底屏蔽亮度干扰追踪更稳定。2色调直方图特征模板只提取目标的色调通道直方图并过滤低饱和度像素避免灰色/白色干扰生成唯一的目标颜色特征。3直方图反向投影用训练好的色调直方图对新图像逐像素计算匹配概率像素越接近目标颜色 → 概率越高图像越亮像素与目标颜色差异大 → 概率越低图像越暗。4MeanShift 均值漂移核心这是算法的灵魂从初始窗口位置开始计算窗口内概率图的重心颜色密度最高的点将窗口中心移动到重心位置重复迭代直到窗口位置不再变化收敛最终窗口位置 目标真实位置。简单理解让窗口自动“飘向”目标颜色最密集的区域。项目完整实现1. Kotlin 上层代码MainActivity.kt负责图片加载、调用 C 算法、结果展示格式与你的肤色检测代码完全对齐。packagecom.nicoli.helloroidetectskinimportandroid.graphics.Bitmapimportandroid.graphics.BitmapFactoryimportandroid.os.Bundleimportandroid.widget.ImageViewimportandroidx.appcompat.app.AppCompatActivityclassMainActivity:AppCompatActivity(){companionobject{init{// 加载 C 原生库System.loadLibrary(native-lib)}}// 1. 训练传入人脸样本图学习肤色特征privateexternalfuntrainSkinFeature(sampleBitmap:Bitmap)// 2. 追踪传入全身图输出带人脸框的结果privateexternalfuntrackHumanFace(srcBitmap:Bitmap,outBitmap:Bitmap)overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 1. 加载两张图片 // 样本图整张都是人脸valfaceSampleBitmapFactory.decodeResource(resources,R.drawable.face_sample)// 全身待测图valfullBodyBitmapFactory.decodeResource(resources,R.drawable.full_body)// 2. 创建输出位图 valresultBitmapBitmap.createBitmap(fullBody.width,fullBody.height,Bitmap.Config.ARGB_8888)// 3. 执行算法流程 trainSkinFeature(faceSample)// 学习肤色trackHumanFace(fullBody,resultBitmap)// 追踪人脸// 4. 显示结果 findViewByIdImageView(R.id.iv_sample_face).setImageBitmap(faceSample)findViewByIdImageView(R.id.iv_full_body).setImageBitmap(fullBody)findViewByIdImageView(R.id.iv_result).setImageBitmap(resultBitmap)}}2. C 算法核心代码native-lib.cpp包含格式转换、色调直方图提取、反向投影、MeanShift 追踪所有 API 均适配 Android。// JNI 通信头文件#includejni.h// OpenCV 核心库#includeopencv2/opencv.hpp// Android Bitmap 操作#includeandroid/bitmap.h// 通道拆分容器#includevectorusingnamespacecv;usingnamespacestd;// 全局变量保存训练好的肤色直方图Mat g_skinHist;// // 工具函数1Android Bitmap → OpenCV Mat// 作用将安卓图像格式转为OpenCV可处理的矩阵// MatbitmapToMat(JNIEnv*env,jobject bitmap){AndroidBitmapInfo info;void*pixels;// 获取图片信息AndroidBitmap_getInfo(env,bitmap,info);// 锁定像素内存AndroidBitmap_lockPixels(env,bitmap,pixels);// 构建 RGBA 格式矩阵Matrgba(info.height,info.width,CV_8UC4,pixels);Mat bgr;// RGBA → BGROpenCV 默认格式cvtColor(rgba,bgr,COLOR_RGBA2BGR);// 解锁内存AndroidBitmap_unlockPixels(env,bitmap);returnbgr;}// // 工具函数2OpenCV Mat → Android Bitmap// 作用将处理后的图像转回安卓可显示格式// voidmatToBitmap(JNIEnv*env,constMatsrcMat,jobject dstBitmap){AndroidBitmapInfo info;void*pixels;AndroidBitmap_getInfo(env,dstBitmap,info);AndroidBitmap_lockPixels(env,dstBitmap,pixels);Mat rgba;// 灰度图 / 彩色图分别转 RGBAif(srcMat.channels()1)cvtColor(srcMat,rgba,COLOR_GRAY2RGBA);elsecvtColor(srcMat,rgba,COLOR_BGR2RGBA);// 复制像素数据memcpy(pixels,rgba.data,info.width*info.height*4);AndroidBitmap_unlockPixels(env,dstBitmap);}// // 【核心】从整张人脸图提取肤色直方图// MatgetSkinHueHist(constMatsrc){Mat hsv;// BGR 转 HSV肤色检测必须用cvtColor(src,hsv,COLOR_BGR2HSV);// ✅ 整张图都是人脸直接作为样本Mat faceSrchsv;// 拆分 H/S/V 三个通道vectorMatchs;split(faceSrc,chs);Mat mask;// 过滤低饱和度杂色灰色/白色干扰threshold(chs[1],mask,35,255,THRESH_BINARY);// 直方图参数inthistSize18;floathueRange[]{0,180};constfloat*ranges[]{hueRange};intchannels[]{0};// 只使用 H 通道Mat hist;// 计算一维色调直方图calcHist(faceSrc,1,channels,mask,hist,1,histSize,ranges);// 归一化到 0~255normalize(hist,hist,0,255,NORM_MINMAX);returnhist;}// // JNI 接口1训练肤色特征// externCJNIEXPORTvoidJNICALLJava_com_nicoli_helloroidetectskin_MainActivity_trainSkinFeature(JNIEnv*env,jobject thiz,jobject sampleBitmap){Mat sampleMatbitmapToMat(env,sampleBitmap);// 训练并保存肤色直方图g_skinHistgetSkinHueHist(sampleMat);}// // JNI 接口2MeanShift 人脸追踪// externCJNIEXPORTvoidJNICALLJava_com_nicoli_helloroidetectskin_MainActivity_trackHumanFace(JNIEnv*env,jobject thiz,jobject srcBitmap,jobject outBitmap){Mat srcbitmapToMat(env,srcBitmap);Mat hsv;cvtColor(src,hsv,COLOR_BGR2HSV);// 反向投影生成肤色概率图floathueRange[]{0,180};constfloat*ranges[]{hueRange};intch[]{0};Mat backProj;calcBackProject(hsv,1,ch,g_skinHist,backProj,ranges,1.0);// 初始搜索窗口画面上半部分RectsearchRect(src.cols*0.3f,src.rows*0.05f,src.cols*0.4f,src.rows*0.35f);// MeanShift 停止条件TermCriteriacriteria(TermCriteria::EPS|TermCriteria::MAX_ITER,15,2);// 执行追踪meanShift(backProj,searchRect,criteria);// 复制原图并绘制绿色框Mat resultsrc.clone();rectangle(result,searchRect,Scalar(0,255,0),3);// 输出结果matToBitmap(env,result,outBitmap);}3. 布局文件activity_main.xml展示训练图、追踪图、结果图?xml version1.0 encodingutf-8?LinearLayoutxmlns:androidhttp://schemas.android.com/apk/res/androidandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:orientationverticalandroid:padding8dp!-- 1. 人脸样本图整张都是人脸 --ImageViewandroid:idid/iv_sample_faceandroid:layout_widthmatch_parentandroid:layout_height0dpandroid:layout_weight1android:scaleTypefitCenterandroid:adjustViewBoundstrue/!-- 2. 全身原图 --ImageViewandroid:idid/iv_full_bodyandroid:layout_widthmatch_parentandroid:layout_height0dpandroid:layout_weight1android:layout_marginTop4dpandroid:scaleTypefitCenterandroid:adjustViewBoundstrue/!-- 3. 追踪结果带绿色人脸框 --ImageViewandroid:idid/iv_resultandroid:layout_widthmatch_parentandroid:layout_height0dpandroid:layout_weight1android:layout_marginTop4dpandroid:scaleTypefitCenterandroid:adjustViewBoundstrue//LinearLayout项目优势与拓展方向1. 核心优势轻量无模型无需深度学习无权重文件包体极小抗光照干扰基于 HSV 色调亮度变化不影响效果高性能C 底层运算适配 Android 移动端易集成与你的肤色检测代码结构统一直接合并项目。2. 拓展方向实时相机追踪改为相机预览流实现实时目标追踪CamShift 升级支持目标缩放/旋转适配更复杂场景多目标追踪同时追踪多个物体手动框选 ROI支持手指拖拽选择追踪目标。总结本文完整实现了OpenCV MeanShift 目标追踪在 Android 平台的落地从原理讲解到全项目代码全覆盖且完全对齐你的项目结构。MeanShift 作为轻量化追踪算法非常适合移动端无模型、高性能的图像需求无论是物体追踪、人脸锁定还是肤色跟踪都能快速适配使用。