news 2026/4/28 17:23:32

【android opencv学习笔记】Day 4: 像素操作之椒盐噪声

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【android opencv学习笔记】Day 4: 像素操作之椒盐噪声

OpenCV 像素操作之噪点

在计算机视觉的世界里,图像本质上是由数值构成的矩阵,而像素就是这个矩阵的基本单元。无论是修改图像、添加特效,还是做复杂的特征提取,都离不开对像素的直接操作。今天我们就从原理到实战,拆解OpenCV中像素访问的核心知识,帮你彻底搞懂这部分内容。


图像与像素

要操作像素,首先得理解图像在OpenCV中的存储方式:

  • 灰度图像:是单通道矩阵,每个像素是一个8位无符号数(uchar),0代表黑色,255代表白色,数值越大越亮。
  • 彩色图像:是三通道矩阵,每个像素对应B、G、R三个通道的数值(OpenCV默认存储顺序为蓝、绿、红),每个通道同样是8位无符号数,三个通道的组合决定了最终的颜色。
  • 特殊图像:部分场景(如医学影像)会使用16位通道或浮点型通道,OpenCV也支持CV_32U/CV_32S/CV_32F等类型,用于存储中间计算结果或高精度数据。

OpenCV用cv::Mat结构来统一管理这些图像矩阵,它既支持基础的灰度/彩色图像,也能兼容各种特殊数据类型,是所有像素操作的基础载体。


像素访问的核心方法:

访问cv::Mat中像素的最基础方法,就是at<类型>(行, 列)模板函数,它的核心逻辑是:

  1. image.rows获取图像高度(行数),image.cols获取图像宽度(列数);
  2. image.at<类型>(j, i)访问第j行、第i列的像素,注意行号在前、列号在后
  3. 必须指定和图像类型匹配的模板参数,否则会出现类型不匹配的错误,且at()不会自动做类型转换。

1. 灰度图像的像素访问

灰度图像是单通道矩阵,模板参数为uchar,直接赋值即可修改像素值:

// 访问灰度图像第j行、第i列的像素,设为白色image.at<uchar>(j,i)=255;

2. 彩色图像的像素访问

彩色图像每个像素是一个包含3个通道的向量,OpenCV中用cv::Vec3b表示三通道8位无符号数向量,通过索引[0]/[1]/[2]分别访问蓝、绿、红通道:

// 访问彩色图像第j行、第i列的像素,三个通道都设为255(白色)image.at<cv::Vec3b>(j,i)[0]=255;// 蓝色通道image.at<cv::Vec3b>(j,i)[1]=255;// 绿色通道image.at<cv::Vec3b>(j,i)[2]=255;// 红色通道

OpenCV还定义了其他向量类型,比如cv::Vec2b(双通道8位)、cv::Vec4b(四通道8位),以及浮点型/整型向量(如cv::Vec2fcv::Vec3i),适配不同的图像和数据场景。


实战案例:给图像添加椒盐噪声

我们用一个经典的“椒盐噪声”案例,把像素访问的知识落地到代码中。椒盐噪声是图像中随机出现的黑白噪点,这里我们实现一个函数,给图像添加白色噪点。

1. 实现思路

  1. 随机生成图像内的像素坐标;
  2. 根据图像类型(灰度/彩色),将对应像素设为白色;
  3. 循环执行指定次数,生成噪点效果。

2. 完整代码实现

C++ 核心代码(native-lib.cpp)
// 引入JNI头文件,用于Java和C++的交互#include<jni.h>// 引入OpenCV核心头文件,提供图像处理功能#include<opencv2/opencv.hpp>// 使用OpenCV的命名空间,简化代码调用usingnamespacecv;/** * 给图像添加全屏密集椒盐噪声(白点) * @param image 输入/输出的图像矩阵(ARGB格式) * @param noiseCount 噪声点数量(数值越大,白点越多) */voidaddSaltNoise(Mat&image,intnoiseCount){// 循环生成大量白点,遍历次数 = 图像总像素数,实现全屏覆盖for(intk=0;k<image.cols*image.rows;k++){// 随机生成列坐标 x(范围:0 到 图像宽度-1)inti=rand()%image.cols;// 随机生成行坐标 y(范围:0 到 图像高度-1)intj=rand()%image.rows;// 判断图像通道数:Android Bitmap是 ARGB_8888,共4个通道if(image.channels()==4){// 访问第j行、第i列的像素,将BGR通道设为255(纯白色)image.at<Vec4b>(j,i)[0]=255;// 蓝色通道 Bimage.at<Vec4b>(j,i)[1]=255;// 绿色通道 Gimage.at<Vec4b>(j,i)[2]=255;// 红色通道 R// 透明度通道 A 保持不变,不修改}}}/** * JNI 接口函数:供Android Java层调用 * 功能:接收原始像素数据 -> OpenCV处理 -> 返回处理后像素 * @param env JNI环境变量,用于操作Java数组 * @param thiz Java层的MainActivity对象引用 * @param pixels_ Java传递过来的图像像素数组(int[]) * @param width 图像宽度 * @param height 图像高度 * @return 处理完成后的像素数组(int[]) */extern"C"JNIEXPORT jintArray JNICALLJava_com_example_pixelop_MainActivity_processImageNative(JNIEnv*env,jobject thiz,jintArray pixels_,jint width,jint height){// 1. 将Java的int数组 转换为 C++可操作的int指针jint*pixels=env->GetIntArrayElements(pixels_,NULL);// 2. 将像素数据包装成OpenCV的Mat对象// CV_8UC4 表示:8位无符号数 + 4通道(对应Android的ARGB_8888)Matmat(height,width,CV_8UC4,pixels);// 3. 调用核心函数:给图像添加全屏白点噪声addSaltNoise(mat,1000000);// 4. 创建一个新的Java int数组,用于存放处理后的结果jintArray result=env->NewIntArray(width*height);// 5. 将C++处理后的像素数据 复制回 Java数组env->SetIntArrayRegion(result,0,width*height,pixels);// 6. 释放JNI数组资源(必须释放,防止内存泄漏)env->ReleaseIntArrayElements(pixels_,pixels,0);// 7. 将处理结果返回给Java层returnresult;}

MainActivity.java

packagecom.example.pixelop;importandroid.graphics.Bitmap;importandroid.graphics.BitmapFactory;importandroid.os.Bundle;importandroid.widget.ImageView;importandroidx.appcompat.app.AppCompatActivity;importjava.io.ByteArrayOutputStream;publicclassMainActivityextendsAppCompatActivity{static{System.loadLibrary("pixelop");}publicnativebyte[]processImageNative(byte[]imageData,intwidth,intheight);@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageViewimgOrigin=findViewById(R.id.img_origin);ImageViewimgResult=findViewById(R.id.img_result);// 读取原图BitmaporiginBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.test);imgOrigin.setImageBitmap(originBitmap);// Bitmap转字节数组ByteArrayOutputStreamstream=newByteArrayOutputStream();originBitmap.compress(Bitmap.CompressFormat.JPEG,100,stream);byte[]imageData=stream.toByteArray();// 调用Native处理byte[]resultData=processImageNative(imageData,originBitmap.getWidth(),originBitmap.getHeight());// 显示结果BitmapresultBitmap=BitmapFactory.decodeByteArray(resultData,0,resultData.length);imgResult.setImageBitmap(resultBitmap);}}
布局文件(activity_main.xml)
<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><ImageViewandroid:id="@+id/img_origin"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="20dp"/><ImageViewandroid:id="@+id/img_result"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout>

3. 关键细节说明

  • 值传递的坑:这里image是值传递,但cv::Mat默认共享图像数据,所以函数内的修改会直接影响原图像,不需要额外传引用。
  • 随机坐标:用rand() % colsrand() % rows确保生成的坐标在图像范围内。
  • 类型判断:通过image.type()区分灰度(CV_8UC1)和彩色(CV_8UC3)图像,避免模板类型不匹配的错误。

避坑指南与扩展

  1. 模板类型必须匹配at()的模板参数必须和图像的实际类型一致,比如CV_8UC3的图像用at<uchar>会直接报错,编译时不会有提示,运行时才会崩溃。
  2. 行号列号别搞反at<类型>(j, i)中,j是行号(y轴),i是列号(x轴),和我们平时“先列后行”的习惯相反,写代码时一定要注意。
  3. 效率问题at()方法简单直观,但在处理大图像时,逐像素访问的效率不如直接用指针遍历。如果需要处理数十万像素,后续可以学习指针访问或OpenCV的并行处理方法。
  4. 特殊类型支持:除了8位图像,at()也支持其他类型,比如CV_32F的浮点图像,模板参数用float即可,写法是image.at<float>(j, i)

总结

像素操作是OpenCV的基本功,掌握了at()方法和cv::Mat_的使用,你就能实现从简单的噪点添加,到复杂的图像滤镜、特征标记等各种功能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 17:21:19

【.net core】剔除JSON对象中值为null的字段

方法&#xff1a;/// <summary>/// 递归剔除JObject中值为null的字段/// </summary>static void RemoveNullProperties(JToken token){if (token.Type JTokenType.Object){var jObj (JObject)token;var propertiesToRemove jObj.Properties().Where(p >p.Val…

作者头像 李华
网站建设 2026/4/28 17:21:11

卡梅德生物技术快报|抗体标记:单域抗体标记技术:生物医学成像探针的设计、构建与工程化实现

摘要单域抗体凭借分子量小、稳定性高、组织穿透性强、免疫原性低等优势&#xff0c;成为生物医学成像探针的优选载体。抗体标记是将单域抗体转化为功能性成像探针的核心环节&#xff0c;直接决定探针的靶向性、信号强度、体内分布与临床转化潜力。本文围绕单域抗体抗体标记的技…

作者头像 李华
网站建设 2026/4/28 17:19:27

药厂生产线设备安装:从洁净合规到稳定投产的完整解析

一、什么是药厂生产线设备安装&#xff1f;药厂生产线设备安装&#xff0c;是指在制药企业的新建厂房、洁净车间、改造车间或扩产项目中&#xff0c;围绕药品生产工艺要求&#xff0c;对制粒、混合、压片、胶囊填充、灌装、冻干、包装、灭菌、纯化水、配液、输送、检测、自动化…

作者头像 李华
网站建设 2026/4/28 17:12:25

避开这3个坑!LIN总线节点配置与诊断的常见误区及解决方案

避开这3个坑&#xff01;LIN总线节点配置与诊断的常见误区及解决方案 在汽车电子系统的开发中&#xff0c;LIN总线因其低成本、高可靠性的特点&#xff0c;已成为车身控制模块(BCM)、车门模块、座椅控制等场景的首选通信方案。然而&#xff0c;随着节点数量的增加和功能复杂度的…

作者头像 李华