OpenCV for Android
QQ 1274510382Wechat JNZ_aming商业互捧QQ群538250800技术搞事QQ群599020441技术合作QQ群152889761加入我们QQ群649347320纪年科技aming网络安全 ,深度学习,嵌入式,机器强化,生物智能,生命科学。OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作
QQ 1274510382
Wechat JNZ_aming
商业互捧 QQ群538250800
技术搞事 QQ群599020441
技术合作 QQ群152889761
加入我们 QQ群649347320
纪年科技aming
网络安全 ,深度学习,嵌入式,机器强化,生物智能,生命科学。
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,
可以运行在Linux、Windows、Android和Mac OS操作系统上。
它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,
同时提供了Python、Ruby、MATLAB等语言的接口,
实现了图像处理和计算机视觉方面的很多通用算法。
具有模块化结构,这就意味着开发包里面包含多个共享库或者静态库。
下面是可使用的模块:
核心功能(Core functionality) - 一个紧凑的模块,定义了基本的数据结构,包括密集的多维Mat数组和被其他模块使用的基本功能。
图像处理(Image processing) - 一个图像处理模块,它包括线性和非线性图像滤波,几何图形转化(重置大小,放射和透视变形,通用基本表格重置映射),色彩空间转换,直方图等。
影像分析(video) - 一个影像分析模块,它包括动作判断,背景弱化和目标跟踪算法。
3D校准(calib3d) - 基于多视图的几何算法,平面和立体摄像机校准,对象姿势判断,立体匹配算法,和3D元素的重建。
平面特征(features2d) - 突出的特征判断,特征描述和对特征描述的对比。
对象侦查(objdetect) - 目标和预定义类别实例化的侦查(例如:脸、眼睛、杯子、人、汽车等等)。
highgui - 一个容易使用的用户功能界面。
视频输入输出(videoio) - 一个容易使用的视频采集和视频解码器。
GPU - 来自不同OpenCV模块的GPU加速算法。
一些其他的辅助模块,比如FLANN和谷歌的测试封装,Python,android等绑定和其他。
二:Android OpenCV简介
为Android开发OpenCV程序, 有两种方式,
第一种用java形式的OpenCV库,
第二种用C++形式的OpenCV库, 这两种库都在OpenCV官方提供的SDK中。
我们可以从官网下载 http://OpenCV.org/releases.html。
第一种形式, 需要在开发环境中导入 OpenCV 的 jre包,
还需要安装 Android OpenCV Manager, 有的书上例子也是用这种方式写的。
Android OpenCV Manager是用来管理OpenCV 库的, 减少APP内存占用, 支持特定硬件优化,
定期更新库等, 具体看它的介绍(http://docs.OpenCV.org/2.4/platforms/android/service/doc/index.html)。
第二种形式, 需要使用C++的OpenCV库, 以jni的形式来调用。
底层NDK的方式开发可以使用这种。
三:Android OpenCV 开发的准备
1.先准备Android studio开发环境
2.到官网(https://OpenCV.org/releases.html)下载sdk,
然后import library到工程里面,添加到项目进行使用
四:OpenCV在Android上的简单使用
1.在OpenCV中存储图像
OpenCV中使用名为Mat的自定义对象存储图像,
该对象保存了行数,列数,数据等能唯一标识该图像的信息,
并能在需要的时候重新创建图像。不同的图像包含的数据信息也不同,
例如色彩不同的图片色彩多的比灰度多的图片包含信息多,
因为彩色图片是通过RGB三通道的,而灰度图片是单通道的。
2.OpenCV中的线性滤波器
越清晰的图片,图片的信息越丰富,进行图片操作的时候计算时间比包含信息少的图片要更长,为了解决这个问题,需要对图片进行模糊处理。
很多线性滤波器都采用了核(kernel)的数字向量。
核可以看作沿着像素滑动的窗口,并把计算结果输出给该像素。大概如下图
3.几种常见的模糊方法,均值模糊方法,高斯模糊方法,中值模糊方法
- 均值模糊方法:均值滤波是最简单的滤波,对核进行均值计算,
每一个邻域像素都有相同的权值。
在OpenCV for Android中的方法如下
// Bitmap转为Mat
Mat src = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4);
Utils.bitmapToMat(bitmap, src);
// 均值模糊方法 Imgproc.blur(src, src, new Size(3,3));
// Mat转Bitmap
Bitmap processedImage = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(src, processedImage);
- 高斯模糊方法:高斯核是通过高斯函数所获得的,
大概是临近像素对特定像素结果的影响比那些较远像素的影响要高。
Imgproc.GaussianBlur(src, src, new Size(3, 3), 0);(Mat核bitmap转换不写了)
函数声明为:
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double
sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT ) ;
功能:对输入的图像src进行高斯滤波后用dst输出。
参数:src和dst当然分别是输入图像和输出图像。
Ksize为高斯滤波器模板大小,
sigmaX和sigmaY分别为高斯滤波在横线和竖向的滤波系数。
borderType为边缘点插值类型。
理解:数字图像的滤波可以简单的这么理解,就是对原图像的每一个像素滤波,
那么对应这个像素滤波后的值是根据其相邻像素(包括自己那个点)
与一个滤波模板进行相乘即可。
所以具体到高斯滤波,我们只要知道这个高斯滤波的模板即可。
- 中值模糊方法:椒盐噪声是一种图片中常见的噪点,噪点随机发呢不在图片中,
中值模糊就是为了除去这些噪点,将核中的像素按照升序或者降序排列,
取中值作为输出结果。
Imgproc.medianBlur(src,src,3);(Mat核bitmap转换不写了)
锐化:锐化也可以看作是一种线性滤波操作,并且锚点像素有较高的权重,
而周围的像素权重较低。
因此,我们可以自定义下面一些这样的核。
步骤:先自定义一个核,然后呢进行锐化处理
Mat kenrl = new Mat(3, 3, CvType.CV_16SC1);
kenrl.put(0, 0, 0, -1, 0, -1, 5, -1, 0, -1, 0);
Imgproc.filter2D(src, src, src.depth(), kenrl);
五.检测图片的基本特征
1.边缘检测
边缘检测的一般步骤:
滤波——消除噪声
增强——使边界轮廓更加明显
检测——选出边缘点
- 高斯差分法检测边缘
算法原理:先将图片灰度化,然后进行两个半径的高斯模糊,
然后降高斯模糊后的两个图相减,再反转二值阈值化,再讲mat转成图片
代码过程:
第一步 将图像转为灰度图像
Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步 用两个不同的模糊半径对灰度图像执行高斯模糊(取得两幅高斯模糊图像)
Imgproc.GaussianBlur(grayMat, blur1, new Size(15, 15), 5);
Imgproc.GaussianBlur(grayMat, blur2, new Size(21, 21), 5);
第三步 将两幅高斯模糊图像做减法,得到一幅包含边缘点的结果图像
Mat diff = new Mat();
Core.absdiff(blur1, blur2, diff);
- Canny边缘检测器
是一种被广泛使用的算法,并被认为是边缘检测最优的算法,
该方法使用了比高斯差分算法更复杂的技巧,如多向灰度梯度和滞后阈值化。
-
平滑图像:通过使用合适的模糊半径执行高斯模糊来减少图像内的噪声。
-
计算图像的梯度:这里计算图像的梯度,并将梯度分类为垂直、水平和斜对角。
这一步的输出用于在下一步中计算真正的边缘。 -
非最大值抑制:利用上一步计算出来的梯度方向,
检测某一像素在梯度的正方向和负方向上是否是局部最大值,
如果是,则抑制该像素(像素不属于边缘)。
这是一种边缘细化技术,用最急剧的变换选出边缘点。 -
用滞后阈值化选择边缘:最后一步,检查某一条边缘是否明显到足以作为最终输出,
最后去除所有不明显的边缘。
第一步:使用很简单,首先将图像灰度化
Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步:然后调用Imgproc.Canny()方法即可
Imgproc.Canny(grayMat, cannyEdges, 10, 100);
- Sobel滤波器
Sobel滤波器也叫Sobel算子,与Canny边缘检测一样,需要计算像素的灰度梯度,只不过是换用另一种方式。
大致步骤:
1.将图像进行灰度化,
2.计算水平方向灰度梯度的绝对值,
3.计算数值方向灰度梯度的绝对值,
4计算最终的梯度
具体计算如下:
Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)
+(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)
+(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)
= [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)]
Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)
+0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)
+(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)
= [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)]
其中f(a,b), 表示图像(a,b)点的灰度值;
- 使用Sobel算计计算边缘的步骤:
第一步:将图像转为灰度图像
Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步:计算水平方向灰度梯度的绝对值
Imgproc.Sobel(grayMat, grad_x, CvType.CV_16S, 1, 0, 3, 1, 0);
Core.convertScaleAbs(grad_x, abs_grad_x);
第三步:计算垂直方法灰度梯度的绝对值
Imgproc.Sobel(grayMat, grad_y, CvType.CV_16S, 0, 1, 3, 1, 0);
Core.convertScaleAbs(grad_y, abs_grad_y);
第四步:计算最终梯度
Core.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 1, sobel);
- Harris角点检测
角点是两条边缘的交点或者在局部邻域中有多个显著边缘方向的点。
Harris角点检测是一种在角点检测中最常见的技术。
Harris角点检测器在图像上使用滑动窗口计算亮度的变化。
第一步:将图像转为灰度图像
Imgproc.cvtColor(src, grayMat, Imgproc.COLOR_BGR2GRAY);
第二步:找出harri角点
Imgproc.cornerHarris(grayMat,tempDst,2,3,0.04);
第三步:在新的图片上绘制角点
Random r = new Random();
for(int i = 0; i < tempDstNorm.cols(); i++){
for (int j = 0;j <tempDstNorm.rows(); j++){
double[] value = tempDstNorm.get(j,i);
if(value[0] > 250){
//决定了画出哪些角点,值越大选择画出的点就越少。如果程序跑的比较慢,就是由于值选取的太小,导致画的点过多
Imgproc.circle(corners, new Point(i,j),5,new Scalar(r.nextInt(255)),2);
}
}
}
测彩直方图:先调用
Imgproc.calcHist(matList.subList(0, 1), channels, new Mat(), hist_b, histSize, ranges, false);
然后再将得到的直方图会知道mat上然后转换成bitmap展示出来
对比两张图片的相似度
/**
* 比较来个矩阵的相似度
*
* @param srcMat
* @param desMat
*/
public void comPareHist(Mat srcMat, Mat desMat) {
srcMat.convertTo(srcMat, CvType.CV_32F);
desMat.convertTo(desMat, CvType.CV_32F);
double target = Imgproc.compareHist(srcMat, desMat, Imgproc.CV_COMP_CORREL);
Log.e(TAG, "相似度 : ==" + target);
Toast.makeText(this, "相似度 : ==" + target, Toast.LENGTH_LONG).show();
}
找出两张图片中的不同
Core.subtract(mat1, mat2, mat11);
Core.subtract(mat2, mat1, mat22);
Core.add(mat11, mat22, result);
// 二值化处理
Bitmap bmp = Bitmap.createBitmap(result.cols(), result.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(result,bmp);
六:检测目标
1.尺度不变特征变换 SIFT(scale Invariant Feature Transform)
SIFT算法的实现过程大致如下:
对源图像进行高斯模糊处理,
根据源图尺寸和相关设定参数生成图像的高斯金字塔和高斯差分金字塔(DOG尺度空间),
在DOG尺度空间中搜索特征点,计算尺度,构建特征描述子。
本文以SIFT的参数为主线来尝试说明SIFT算法的原理。
性质:对目标的旋转核缩放具有不变性
对三维视角和光照变化具有部分不变性
很大一部分的关键点(特征)可以从单幅图像中获取
SIFT遵循匹配文件局部特征的策略:
尺度空间极值检测,关键点定位,方向分配,关键点描述子
详细原理 https://blog.csdn.net/maweifei/article/details/59501184
https://blog.csdn.net/kksc1099054857/article/details/77894532
用法:
DescriptorMatcher descriptormatcher
DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
descriptormatcher.match(test, train, matches);
人脸识别
openCV自带的提供人脸识别等功能,提供人脸眼睛笑脸等识别功能
Rect[] obj = face.detectObject(mRgba, matOfRect);
for (Rect rect : obj) {
Imgproc.rectangle(mRgba, rect.tl(), rect.br(), face.getRectColor(), 3);
}
其中face等是通过相应的配置文件(xml文件)生成,直接将检测结果显示在预览图上
本次环境搭建是使用opencv提供的java接口。
- RxPermissions动态权限
- 可能用不到
- 基本搭建完毕
摄像头打开与预览
》权限
<uses-permission android:name="android.permission.CAMERA" /> <!-- 摄像头 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 内部存储 -->
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
》》 在MainActivity中继承CvCameraViewListener2接口
CvCameraViewListener2是使用OpenCV相机的核心监听
public class MainActivity extends Activity implements CvCameraViewListener2 {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onCameraViewStarted(int width, int height) {
// TODO Auto-generated method stub
}
@Override
public void onCameraViewStopped() {
// TODO Auto-generated method stub
}
@Override
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
// TODO Auto-generated method stub
return null;
}
}
onCameraViewStarted:相机启动时调用
onCameraViewStopped:相机销毁时调用
onCameraFrame: 相机工作时调用,参数是相机每一帧的图像,实时对比就在这个方法中进行
》》》 OnCreate中初始化相机,并添加开启相机的回调
/**
* CV相机
*/
private CameraBridgeViewBase mCVCamera;
/**
* 加载OpenCV的回调
*/
private BaseLoaderCallback mLoaderCallback;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化CV相机
mCVCamera = (CameraBridgeViewBase) findViewById(R.id.cv);
mCVCamera.setVisibility(CameraBridgeViewBase.VISIBLE);
// 设置相机监听
mCVCamera.setCvCameraViewListener(this);
// 连接到OpenCV的回调
mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
mCVCamera.enableView();
break;
default:
break;
}
}
};
}
》》》》界面加载完成时说明OpenCV已经初始化完成
所以重写onResume()方法,开启相机
@Override
protected void onResume() {
// 界面加载完成的时候向OpenCV的连接回调发送连接成功的信号
if (OpenCVLoader.initDebug()) {
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
super.onResume();
}
重写onPause方法,及时销毁相机
@Override
protected void onPause() {
super.onPause();
// 销毁OpenCV相机
if (mCVCamera != null)
mCVCamera.disableView();
}
编辑onCameraFrame方法,让相机的每一帧展示在界面上
@Override
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
return inputFrame.rgba();
}
package com.e.opcv;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraActivity;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class MainActivity extends CameraActivity implements CvCameraViewListener2{
private static final String TAG = "OCVSample::Activity";
//opencv接口标志
/**
* CV相机
*/
private CameraBridgeViewBase mOpenCvCameraView;
//缓存相机每帧输入的数据
private Mat mRgba;
private Button button;
private boolean mIsJavaCamera = true;
private MenuItem mItemSwitchCamera = null;
/**
* 加载OpenCV的回调
* 通过OpenCV管理Android服务,初始化OpenCV
*/
private BaseLoaderCallback mLoaderCallback ;
public MainActivity() {
Log.i(TAG, "Instantiated new " + this.getClass());
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
if(msg.what == 1){
button.performClick();
}
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
// //初始化并设置预览部件 // 初始化CV相机
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
// 设置相机监听
mOpenCvCameraView.setCvCameraViewListener(this);
//拍照按键
button = (Button) findViewById(R.id.deal_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mRgba != null) {
if(!mRgba.empty()) {
Mat inter = new Mat(mRgba.width(), mRgba.height(), CvType.CV_8UC4);
Log.e("Mat","...............1...............");
//将四通道的RGBA转为三通道的BGR,重要!!
Imgproc.cvtColor(mRgba, inter, Imgproc.COLOR_RGBA2BGR);
Log.e("Mat","...............2...............");
File sdDir = null;
//判断是否存在机身内存
boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
if(sdCardExist) {
//获得机身储存根目录
sdDir = Environment.getExternalStorageDirectory();
Log.e("Mat","...............3...............");
}
//将拍摄准确时间作为文件名
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String filename = sdf.format(new Date());
String savepath=sdDir + "/Pictures/OpenCV/";
File f=new File(savepath);
if(!f.exists()){
f.mkdirs();
}
String filePath = sdDir + "/Pictures/OpenCV/" + filename + ".png";
Log.e("Mat","..............."+filePath+"...............");
//将转化后的BGR矩阵内容写入到文件中
Imgcodecs.imwrite(filePath, inter);
Toast.makeText(MainActivity.this, "图片保存到: "+ filePath, Toast.LENGTH_SHORT).show();
}
}
}
});
// moveTaskToBack(true);
// new test().start();
// 连接到OpenCV的回调
mLoaderCallback = new BaseLoaderCallback(this){
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
}
private class test extends Thread{
@Override
public void run() {
super.run();
while (true){
try {
Thread.sleep(2000);
Log.e("begin","kaishi.........");
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}catch (Exception e){
Log.e("error",e.getMessage());
}
}
}
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
// 界面加载完成的时候向OpenCV的连接回调发送连接成功的信号
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
super.onResume();
}
@Override
public List<? extends CameraBridgeViewBase> getCameraViewList() {
return Collections.singletonList(mOpenCvCameraView);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
//对象实例化及基本属性的设置,包括长度、宽度和图像类型标志
public void onCameraViewStarted(int width, int height) {
Log.e("Mat","...............4...............");
mRgba = new Mat(height, width, CvType.CV_8UC4);
}
/**图像处理都写在这里!!!**/
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba(); //一定要有!!!不然数据保存不进MAT中!!!
//直接返回输入视频预览图的RGB数据并存放在Mat数据中
Log.e("Mat","...............5...............");
return mRgba;
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
//结束时释放
public void onCameraViewStopped() {
Log.e("Mat","...............6...............");
mRgba.release();
// mTmp.release();
}
}
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:opencv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<org.opencv.android.JavaCameraView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:id="@+id/tutorial1_activity_java_surface_view"
opencv:show_fps="true"
opencv:camera_id="any" />
</FrameLayout>
package com.e.opcv;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraActivity;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.WindowManager;
import java.util.Collections;
import java.util.List;
public class MainActivity extends CameraActivity implements CvCameraViewListener2{
private static final String TAG = "OCVSample::Activity";
private CameraBridgeViewBase mOpenCvCameraView;
private boolean mIsJavaCamera = true;
private MenuItem mItemSwitchCamera = null;
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
public MainActivity() {
Log.i(TAG, "Instantiated new " + this.getClass());
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
@Override
public List<? extends CameraBridgeViewBase> getCameraViewList() {
return Collections.singletonList(mOpenCvCameraView);
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
public void onCameraViewStarted(int width, int height) {
}
public void onCameraViewStopped() {
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
return inputFrame.rgba();
}
}
修改 protected void deliverAndDrawFrame(CvCameraViewFrame frame)函数为:
//新方法
Matrix matrix = new Matrix(); // I rotate it with minimal process
//matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,(canvas.getHeight() - mCacheBitmap.getHeight()) / 2);
//matrix.postRotate(90f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2);
//float scale = (float) canvas.getWidth() / (float) mCacheBitmap.getHeight();
//matrix.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 );
//canvas.drawBitmap(mCacheBitmap, matrix, new Paint());
if (getDisplay().getRotation() == Surface.ROTATION_0) {
matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,(canvas.getHeight() - mCacheBitmap.getHeight()) / 2);
matrix.postRotate(90f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2);
float scale = (float) canvas.getWidth() / (float) mCacheBitmap.getHeight();
matrix.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 );
canvas.drawBitmap(mCacheBitmap, matrix, new Paint());
} else if (getDisplay().getRotation() == Surface.ROTATION_90) {
float scale = (float) canvas.getWidth() / (float) mCacheBitmap.getHeight();
matrix.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 );
canvas.drawBitmap(mCacheBitmap, matrix, new Paint());
} else if (getDisplay().getRotation() == Surface.ROTATION_180) {
matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,(canvas.getHeight() - mCacheBitmap.getHeight()) / 2);
matrix.postRotate(270f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2);
float scale = (float) canvas.getWidth() / (float) mCacheBitmap.getHeight();
matrix.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 );
canvas.drawBitmap(mCacheBitmap, matrix, new Paint());
} else if (getDisplay().getRotation() == Surface.ROTATION_270) {
matrix.postRotate(180f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2);
float scale = (float) canvas.getWidth() / (float) mCacheBitmap.getHeight();
matrix.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 );
canvas.drawBitmap(mCacheBitmap, matrix, new Paint());
}
拍照
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:opencv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<org.opencv.android.JavaCameraView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:id="@+id/tutorial1_activity_java_surface_view"
opencv:show_fps="true"
opencv:camera_id="any" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom|center_horizontal">
<Button
android:id="@+id/deal_btn"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginBottom="20dp"
android:text="拍照处理"/>
</RelativeLayout>
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.e.opcv">
<uses-permission android:name="android.permission.CAMERA" />
<!--摄像头-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--内部存储-->
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
package com.e.opcv;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraActivity;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class MainActivity extends CameraActivity implements CvCameraViewListener2{
private static final String TAG = "OCVSample::Activity";
//opencv接口标志
private CameraBridgeViewBase mOpenCvCameraView;
//缓存相机每帧输入的数据
private Mat mRgba;
private Button button;
private boolean mIsJavaCamera = true;
private MenuItem mItemSwitchCamera = null;
/**通过OpenCV管理Android服务,初始化OpenCV**/
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
public MainActivity() {
Log.i(TAG, "Instantiated new " + this.getClass());
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
if(msg.what == 1){
button.performClick();
}
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
// //初始化并设置预览部件
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
//拍照按键
button = (Button) findViewById(R.id.deal_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mRgba != null) {
if(!mRgba.empty()) {
Mat inter = new Mat(mRgba.width(), mRgba.height(), CvType.CV_8UC4);
Log.e("Mat","...............1...............");
//将四通道的RGBA转为三通道的BGR,重要!!
Imgproc.cvtColor(mRgba, inter, Imgproc.COLOR_RGBA2BGR);
Log.e("Mat","...............2...............");
File sdDir = null;
//判断是否存在机身内存
boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
if(sdCardExist) {
//获得机身储存根目录
sdDir = Environment.getExternalStorageDirectory();
Log.e("Mat","...............3...............");
}
//将拍摄准确时间作为文件名
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String filename = sdf.format(new Date());
String savepath=sdDir + "/Pictures/OpenCV/";
File f=new File(savepath);
if(!f.exists()){
f.mkdirs();
}
String filePath = sdDir + "/Pictures/OpenCV/" + filename + ".png";
Log.e("Mat","..............."+filePath+"...............");
//将转化后的BGR矩阵内容写入到文件中
Imgcodecs.imwrite(filePath, inter);
Toast.makeText(MainActivity.this, "图片保存到: "+ filePath, Toast.LENGTH_SHORT).show();
}
}
}
});
// moveTaskToBack(true);
// new test().start();
}
private class test extends Thread{
@Override
public void run() {
super.run();
while (true){
try {
Thread.sleep(2000);
Log.e("begin","kaishi.........");
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}catch (Exception e){
Log.e("error",e.getMessage());
}
}
}
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
/***强制横屏***/
if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
//横屏后才加载部件,否则会FC
if(!OpenCVLoader.initDebug()) {
Log.d(TAG, "OpenCV library not found!");
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
@Override
public List<? extends CameraBridgeViewBase> getCameraViewList() {
return Collections.singletonList(mOpenCvCameraView);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
//对象实例化及基本属性的设置,包括长度、宽度和图像类型标志
public void onCameraViewStarted(int width, int height) {
Log.e("Mat","...............4...............");
mRgba = new Mat(height, width, CvType.CV_8UC4);
}
/**图像处理都写在这里!!!**/
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba(); //一定要有!!!不然数据保存不进MAT中!!!
//直接返回输入视频预览图的RGB数据并存放在Mat数据中
Log.e("Mat","...............5...............");
return mRgba;
// return inputFrame.rgba();
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
//结束时释放
public void onCameraViewStopped() {
Log.e("Mat","...............6...............");
mRgba.release();
// mTmp.release();
}
}
/***强制横屏***/
if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
//横屏后才加载部件,否则会FC
if(!OpenCVLoader.initDebug()) {
Log.d(TAG, "OpenCV library not found!");
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
package com.e.opcv;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class Main2Activity extends Activity implements SurfaceHolder.Callback, Camera.ShutterCallback, Camera.PictureCallback {
public static final String FILE_PATH = "filePath";
private Camera mCamera;
private SurfaceView mPreview;
private Button ibSnapPhoto, ibCancelPhoto;
private String pictureFilePath;
/**
* 外部调用接口
* @param context
* @param filePath 拍摄照片的存储地址
*/
public static void callMe(Context context, String filePath){
Intent intent = new Intent(context,Main2Activity.class);
intent.putExtra(FILE_PATH, filePath);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
//mPrevie为摄像机预览图层
mPreview = (SurfaceView) findViewById(R.id.surface_view);
mPreview.getHolder().addCallback(this);
mPreview.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
ibSnapPhoto = (Button) findViewById(R.id.snap_photo);
ibCancelPhoto = (Button) findViewById(R.id.cancel_photo);
ibSnapPhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onSnapPhoto();
}
});
ibCancelPhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onCancelPhoto();
}
});
pictureFilePath = getIntent().getStringExtra(FILE_PATH);
//开启Camera
mCamera = Camera.open();
}
@Override
protected void onPause() {
super.onPause();
mCamera.stopPreview();
}
@Override
protected void onDestroy() {
super.onDestroy();
mCamera.release();
}
/**
* 点击按钮拍照
*/
private void onSnapPhoto(){
//拍照
mCamera.takePicture(this, null, null, this);
}
/**
* 点击按钮取消
*/
private void onCancelPhoto(){
mCamera.stopPreview();
finish();
}
@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
savePictureToFile(data, pictureFilePath);
} catch (IOException e) {
Log.d("wcj", "originalImage保存失败!");
e.printStackTrace();
}
//重新启动预览
mCamera.startPreview();
}
@Override
public void onShutter() {
Toast.makeText(Main2Activity.this, "click!", Toast.LENGTH_SHORT).show();
}
//以下为Surface三种回调方法
@Override
public void surfaceCreated(SurfaceHolder holder) {
//当surfaceView建立时,在上面绑定预览显示界面
try{
mCamera.setPreviewDisplay(mPreview.getHolder());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters params = mCamera.getParameters();
List<Camera.Size> sizes = params.getSupportedPreviewSizes();
Camera.Size selectedSize =getBestSupportPreviewSize(sizes, getScreenSize());
//设定摄像机预览界面尺寸
params.setPreviewSize(selectedSize.width, selectedSize.height);
mCamera.setParameters(params);
mCamera.setDisplayOrientation(90);
mCamera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
/**
* 保存照片到存储器
*
* @param data
* @param _file
* @throws IOException
*/
public static void savePictureToFile(byte[] data, String _file) throws IOException {
BufferedOutputStream os = null;
try {
File file = new File(_file);
// String _filePath_file.replace(File.separatorChar +
// file.getName(), "");
int end = _file.lastIndexOf(File.separator);
String _filePath = _file.substring(0, end);
File filePath = new File(_filePath);
if (!filePath.exists()) {
filePath.mkdirs();
}
file.createNewFile();
os = new BufferedOutputStream(new FileOutputStream(file));
os.write(data);
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 寻找最大的预览图片尺寸(与屏幕分辨率适配)
*
* @param previewSizes 所有支持的预览图片大小
* @return
*/
public static Camera.Size getBestSupportPreviewSize(List<Camera.Size> previewSizes, Camera.Size screenSize) {
double screenRatio = screenSize.width * 1.0 / screenSize.height;
Camera.Size maxSize = previewSizes.get(0);
for (Camera.Size size : previewSizes) {
double sizeRatio = size.width * 1.0 / size.height;
if (size.width < 2000 && sizeRatio > screenRatio - 0.1 && sizeRatio < screenRatio + 0.1)
maxSize = (size.width > maxSize.width) ? size : maxSize;
}
return maxSize;
}
private Camera.Size getScreenSize() {
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int width = metric.widthPixels; // 宽度(PX)
int height = metric.heightPixels; // 高度(PX)
return mCamera.new Size(height, width);
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<SurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/surface_view"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/snap_photo"
android:text="SNAP"
android:layout_alignParentBottom="true"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/cancel_photo"
android:text="CANCEL"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"/>
</RelativeLayout>
实时图像处理之表格切割
灰化、
Canny边缘检测、
Hist直方图计算、
Sobel边缘检测、
SEPIA(色调变换)、
ZOOM放大镜、
PIXELIZE像素化
https://blog.csdn.net/u012556077/article/details/79072340
》图像信息获取保存、处理和显示:
1.在OpenCV中一般都是使用Mat类型来存储图像等矩阵信息,
所以我们可以声明一个Mat对象用来作为实时帧图像的缓存对象:
//缓存相机每帧输入的数据
private Mat mRgba;
对象实例化以及基本属性的设置,包括:长度、宽度和图像类型标志:
public void onCameraViewStarted(int width, int height) {
// TODO Auto-generated method stub
mRgba = new Mat(height, width, CvType.CV_8UC4);
}
对象赋值
/**
* 图像处理都写在此处
*/
@Override
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
switch (Cur_State) {
case 1:
//灰化处理
Imgproc.cvtColor(inputFrame.gray(), mRgba, Imgproc.COLOR_GRAY2RGBA,4);
break;
default:
//显示原图
mRgba = inputFrame.rgba();
break;
}
//返回处理后的结果数据
return mRgba;
}
由于用对象存储图像数据的话,
数据会保存到内存中,所以结束的时候需要进行数据释放,
不然可能导致崩溃:
@Override
public void onCameraViewStopped() {
// TODO Auto-generated method stub
mRgba.release();
}
private Mat mRgba,mTmp; ;
//对象实例化及基本属性的设置,包括长度、宽度和图像类型标志
public void onCameraViewStarted(int width, int height) {
Log.e("Mat","...............4...............");
mRgba = new Mat(height, width, CvType.CV_8UC4);
///
mTmp = new Mat(height, width, CvType.CV_8UC4);
}
//Canny边缘检测 ///二值化
mRgba = inputFrame.rgba();
Imgproc.Canny(inputFrame.gray(), mTmp, 80, 100);
Imgproc.cvtColor(mTmp, mRgba, Imgproc.COLOR_GRAY2RGBA, 4);
package com.e.opcv;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraActivity;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class MainActivity extends CameraActivity implements CvCameraViewListener2{
private static final String TAG = "OCVSample::Activity";
//opencv接口标志
/**
* CV相机
*/
private CameraBridgeViewBase mOpenCvCameraView;
//缓存相机每帧输入的数据
private Mat mRgba,mTmp; ;
private Button button;
private boolean mIsJavaCamera = true;
private MenuItem mItemSwitchCamera = null;
/**
* 加载OpenCV的回调
* 通过OpenCV管理Android服务,初始化OpenCV
*/
private BaseLoaderCallback mLoaderCallback ;
public MainActivity() {
Log.i(TAG, "Instantiated new " + this.getClass());
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
if(msg.what == 1){
button.performClick();
}
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
// //初始化并设置预览部件 // 初始化CV相机
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
// 设置相机监听
mOpenCvCameraView.setCvCameraViewListener(this);
//拍照按键
button = (Button) findViewById(R.id.deal_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mRgba != null) {
if(!mRgba.empty()) {
Mat inter = new Mat(mRgba.width(), mRgba.height(), CvType.CV_8UC4);
Log.e("Mat","...............1...............");
//将四通道的RGBA转为三通道的BGR,重要!!
Imgproc.cvtColor(mRgba, inter, Imgproc.COLOR_RGBA2BGR);
Log.e("Mat","...............2...............");
File sdDir = null;
//判断是否存在机身内存
boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
if(sdCardExist) {
//获得机身储存根目录
sdDir = Environment.getExternalStorageDirectory();
Log.e("Mat","...............3...............");
}
//将拍摄准确时间作为文件名
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String filename = sdf.format(new Date());
String savepath=sdDir + "/Pictures/OpenCV/";
File f=new File(savepath);
if(!f.exists()){
f.mkdirs();
}
String filePath = sdDir + "/Pictures/OpenCV/" + filename + ".png";
Log.e("Mat","..............."+filePath+"...............");
//将转化后的BGR矩阵内容写入到文件中
Imgcodecs.imwrite(filePath, inter);
Toast.makeText(MainActivity.this, "图片保存到: "+ filePath, Toast.LENGTH_SHORT).show();
}
}
}
});
// moveTaskToBack(true);
// new test().start();
// 连接到OpenCV的回调
mLoaderCallback = new BaseLoaderCallback(this){
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
}
private class test extends Thread{
@Override
public void run() {
super.run();
while (true){
try {
Thread.sleep(2000);
Log.e("begin","kaishi.........");
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}catch (Exception e){
Log.e("error",e.getMessage());
}
}
}
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
// 界面加载完成的时候向OpenCV的连接回调发送连接成功的信号
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
super.onResume();
}
@Override
public List<? extends CameraBridgeViewBase> getCameraViewList() {
return Collections.singletonList(mOpenCvCameraView);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
//对象实例化及基本属性的设置,包括长度、宽度和图像类型标志
public void onCameraViewStarted(int width, int height) {
Log.e("Mat","...............4...............");
mRgba = new Mat(height, width, CvType.CV_8UC4);
///
mTmp = new Mat(height, width, CvType.CV_8UC4);
}
/**图像处理都写在这里!!!**/
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
//灰度
Imgproc.cvtColor(inputFrame.gray(), mRgba, Imgproc.COLOR_GRAY2RGBA,4);
//Canny边缘检测 ///二值化
mRgba = inputFrame.rgba();
Imgproc.Canny(inputFrame.gray(), mTmp, 80, 100);
Imgproc.cvtColor(mTmp, mRgba, Imgproc.COLOR_GRAY2RGBA, 4);
// mRgba = inputFrame.rgba(); //一定要有!!!不然数据保存不进MAT中!!!
//直接返回输入视频预览图的RGB数据并存放在Mat数据中
Log.e("Mat","...............5...............");
return mRgba;
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
//结束时释放
public void onCameraViewStopped() {
Log.e("Mat","...............6...............");
mRgba.release();
// mTmp.release();
}
}
https://pqpo.me/2017/09/11/opencv-border-recognition/
》》形态学处理保留直线 霍夫直线检测
- 霍夫直线检测的作用——计算得到输入图像
(一般是二值化的边缘检测结果图像)中包含的所有直线的数目与位置
在取得图像边缘的基础上,
对一些特定的几何形状边缘,
如直线、圆,通过图像霍夫变换把图像从平面坐标空间变换到霍夫坐标空间,
就可以通过求取霍夫空间的局部极大值方法
(其实就是霍夫空间中的曲线交集点),
得到极坐标空间对应参数方程中直线的两个参数(r,θ),
从而计算得到边缘图像中的所有直线(基于平面坐标)的数目与位置。
参考文献:
https://www.jianshu.com/p/ae5b49b4e5a2
https://www.sohu.com/a/279670020_823210
https://www.cnblogs.com/cheermyang/p/5348820.html
https://vimsky.com/examples/detail/java-method-org.opencv.imgproc.Imgproc.HoughLinesP.html
(…霍夫直线检测 Imgproc.HoughLinesP )
http://www.what21.com/sys/view/media_img_1536741795822.html
( 图形图像上的拉普拉斯平滑(Imgproc.line) )
数学形态学中运算
有 膨胀(或扩张)、腐蚀(或侵蚀)、开启、闭合、骨架抽取、极线腐蚀、击中击不中变换、Top-hat变换、颗粒分析、流域变换、形态学梯度等,
其中腐蚀与扩张就是我们今天所有讲的要点。
opencv中对腐蚀和扩张有相对的函数去实现,多用于图像的取噪、分割出独立的图像元素,在图像中连接相邻的元素、寻找图像中明显的极大值或极小值区、求图像的梯度。
————————————————
https://blog.csdn.net/xuyankuanrong/article/details/79145730
( android使用opencv图片腐蚀与扩张 )
https://blog.csdn.net/m1109048058/article/details/76640714
(膨胀与腐蚀)
package com.e.opcv;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraActivity;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class MainActivity extends CameraActivity implements CvCameraViewListener2{
private static final String TAG = "OCVSample::Activity";
//opencv接口标志
/**
* CV相机
*/
private CameraBridgeViewBase mOpenCvCameraView;
//缓存相机每帧输入的数据
private Mat mRgba,mTmp;
private Button button;
private boolean mIsJavaCamera = true;
private MenuItem mItemSwitchCamera = null;
/**
* 加载OpenCV的回调
* 通过OpenCV管理Android服务,初始化OpenCV
*/
private BaseLoaderCallback mLoaderCallback ;
public MainActivity() {
Log.i(TAG, "Instantiated new " + this.getClass());
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
if(msg.what == 1){
button.performClick();
}
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
// //初始化并设置预览部件 // 初始化CV相机
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
// 设置相机监听
mOpenCvCameraView.setCvCameraViewListener(this);
//拍照按键
button = (Button) findViewById(R.id.deal_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mRgba != null) {
if(!mRgba.empty()) {
Mat inter = new Mat(mRgba.width(), mRgba.height(), CvType.CV_8UC4);
Log.e("Mat","...............1...............");
//将四通道的RGBA转为三通道的BGR,重要!!
Imgproc.cvtColor(mRgba, inter, Imgproc.COLOR_RGBA2BGR);
Log.e("Mat","...............2...............");
File sdDir = null;
//判断是否存在机身内存
boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
if(sdCardExist) {
//获得机身储存根目录
sdDir = Environment.getExternalStorageDirectory();
Log.e("Mat","...............3...............");
}
//将拍摄准确时间作为文件名
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String filename = sdf.format(new Date());
String savepath=sdDir + "/Pictures/OpenCV/";
File f=new File(savepath);
if(!f.exists()){
f.mkdirs();
}
String filePath = sdDir + "/Pictures/OpenCV/" + filename + ".png";
Log.e("Mat","..............."+filePath+"...............");
//将转化后的BGR矩阵内容写入到文件中
Imgcodecs.imwrite(filePath, inter);
Toast.makeText(MainActivity.this, "图片保存到: "+ filePath, Toast.LENGTH_SHORT).show();
}
}
}
});
// moveTaskToBack(true);
// new test().start();
// 连接到OpenCV的回调
mLoaderCallback = new BaseLoaderCallback(this){
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
}
private class test extends Thread{
@Override
public void run() {
super.run();
while (true){
try {
Thread.sleep(2000);
Log.e("begin","kaishi.........");
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}catch (Exception e){
Log.e("error",e.getMessage());
}
}
}
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
// 界面加载完成的时候向OpenCV的连接回调发送连接成功的信号
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
super.onResume();
}
@Override
public List<? extends CameraBridgeViewBase> getCameraViewList() {
return Collections.singletonList(mOpenCvCameraView);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
//对象实例化及基本属性的设置,包括长度、宽度和图像类型标志
public void onCameraViewStarted(int width, int height) {
Log.e("Mat","...............4...............");
mRgba = new Mat(height, width, CvType.CV_8UC4);
///
mTmp = new Mat(height, width, CvType.CV_8UC4);
}
/**图像处理都写在这里!!!**/
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
//灰度
Imgproc.cvtColor(inputFrame.gray(), mRgba, Imgproc.COLOR_GRAY2RGBA,4);
//Canny边缘检测 ///二值化
mRgba = inputFrame.rgba();
Imgproc.Canny(inputFrame.gray(), mTmp, 80, 100);
Imgproc.cvtColor(mTmp, mRgba, Imgproc.COLOR_GRAY2RGBA, 4);
Mat dilateImage = mRgba.clone();
Mat erodeImage = mRgba.clone();
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3,3));
//膨胀
Imgproc.dilate(mRgba, dilateImage, element, new Point(-1, -1), 1);
//腐蚀
Imgproc.erode(mRgba, erodeImage, element, new Point(-1, -1), 1);
// mRgba = inputFrame.rgba(); //一定要有!!!不然数据保存不进MAT中!!!
//直接返回输入视频预览图的RGB数据并存放在Mat数据中
Log.e("Mat","...............5...............");
return mRgba;
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
//结束时释放
public void onCameraViewStopped() {
Log.e("Mat","...............6...............");
mRgba.release();
// mTmp.release();
}
}
更多推荐
所有评论(0)