一、官方网站下载sdk:https://opencv.org
1、悬停Library后点击Releases
2、点击Android,打开网页后会自动下载
打开后得到以下内容
二、打开Android新建项目进行测试,依次点击File -> New -> New Project
- 选择
Empty Activity
,点击Next
Name
栏输入OpenCVTest
(命名自定义),点击Finish
三、将OpenCV SDK导入项目
- 将视图切换到
Project
,右键项目总目录,选择New -> Module
然后选择Android Library
,将模块命名为opencv-test
(自定义命名)
最后点击Finish
- 删除目录下所有内容
- 复制
sdk
目录下的内容到模块opencv-test
下
- 打开
opencv-test
目录下的build.gradle
文件,删除图中kotlin
那一行,然后点击Sync Now
- 显示
BUILD SUCCESSFUL
即可
四、将opencv-test模块依赖到app模块中
- 打开
app
模块下的build.gradle
,在dependencies
中加入以下内容,点击Sync Now
implementation 'com.rabbitmq:amqp-client:5.12.0'
implementation project(path: ':opencv-test')
- 在
app
模块下的src/main/java
的包目录下新建Activity
:OpencvTestActivity
public class OpencvTestActivity extends Activity {
ConnectionFactory factory;
//定义线程池处理上帝视角引起的程序卡死问题
private final ThreadPoolExecutor threadPoolExecutor = new
ThreadPoolExecutor(3, 10, 1, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
private boolean mQuit = false;//当为true的时候退出画图线程
private boolean closeCompleteFlag = false;//当为true的时候表示通道和连接都关闭
//播放视频页面(前、后、左、右)
private SurfaceHolder surfaceHolder;
private static final String TAG = "opencv启动日志";
//消费rabbitmq数据的线程
Runnable basicConsumeRunnable;
volatile String picture;
//画图的runnable
Runnable drawRunnable;
//全局定义各视角连接和通道,便于退出Activity后关闭
Connection connection;
Channel channel;
//关闭通道和连接的runnable
Runnable closeRunnable;
private final String TAG_THREAD = "线程池";
private static final String TAG_RABBITMQ = "RabbitMQ";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_opencv);
//初始化opencv相关变量
initOpencv();
playVideo();
}
private void initOpencv() {
SurfaceView mySurfaceView = findViewById(R.id.surfaceViewTest);
surfaceHolder = mySurfaceView.getHolder();
setupConnectionFactory();
}
//上帝视角相关函数以及线程的开启和关闭 Start
@SuppressLint("HandlerLeak")
private void playVideo() {
//用于从线程中获取数据,更新ui
Handler incomingMessageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
picture = msg.getData().getString("msg");
}
};
//视频号
basicConsumeRunnable = getBasicConsumeRunnable(incomingMessageHandler);
threadPoolExecutor.execute(basicConsumeRunnable);
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
drawRunnable = getDrawRunnable();
threadPoolExecutor.execute(drawRunnable);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
});
}
/**
* 关闭channel和connection的runnable,网络请求不能在主线程挂起
*
* @return 运行的runnable
*/
private Runnable getCloseRunnable(Channel channel, Connection connection){
return () -> closeChannelAndConnection(channel, connection);
}
//画图线程
private Runnable getDrawRunnable() {
return () -> {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
//矩形区域:左上角、右下角(坐标)(老铁稳)
Rect mSrcRect = new Rect(0, 0, 6500, 1080);;
Rect mDestRect = new Rect(0, 0, 7200, 1080);
while (!mQuit) {
// Lock the canvas for drawing
Canvas canvas = surfaceHolder.lockCanvas();
if (canvas == null) {
Log.i("WindowSurface", "Failure locking canvas");
continue;
}
if (picture != null) {
Bitmap bmp = returnBitMap(picture);
// Bitmap scaled = scaleBitmap(bmp, 0.95f);
canvas.drawBitmap(Objects.requireNonNull(bmp), mSrcRect, mDestRect, paint);
}
// Update graphics
// All done
surfaceHolder.unlockCanvasAndPost(canvas);
}
Log.d(TAG_THREAD, "画图线程已关闭");
};
}
//消费者线程
private Runnable getBasicConsumeRunnable(Handler incomingMessageHandler) {
return () -> basicConsume(incomingMessageHandler);
}
//收消息(从发布者那边订阅消息)
private void basicConsume(final Handler handler) {
try {
Log.d(TAG, "basicConsume: 已连接");
connection = factory.newConnection();
Log.d(TAG, "basicConsume: 通道已建立");
channel = connection.createChannel();
//实现Consumer的最简单方法是将便捷类DefaultConsumer子类化。可以在basicConsume 调用上传递此子类的对象以设置订阅:
channel.basicConsume("video_6", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body);
long deliveryTag = envelope.getDeliveryTag();
channel.basicAck(deliveryTag, false);
//从message池中获取msg对象更高效
Message uiMsg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg", msg);
uiMsg.setData(bundle);
Log.d(TAG, "handleDelivery: msg" + msg);
handler.sendMessage(uiMsg);
}
});
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
//关闭通道和连接
private void closeChannelAndConnection(Channel channel, Connection connection) {
//关闭通道
if (channel != null){
if (channel.isOpen()) {
try {
channel.close();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
Log.d(TAG_RABBITMQ, "channel关闭状态:" + !channel.isOpen());
}
//关闭连接
if (connection != null) {
if (connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d(TAG_RABBITMQ, "connection关闭状态:" + !connection.isOpen());
}
Log.d(TAG_THREAD, "通道线程已关闭");
closeCompleteFlag = true;//设置为true表示关闭完成
}
//关闭正在运行的所有线程
private void closeMyActivity() {
//设置为true可以关闭画图线程
mQuit = true;
//关闭channel和connection通道的线程
closeRunnable = getCloseRunnable(channel, connection);
threadPoolExecutor.execute(closeRunnable);
//closeCompleteFlag为true表示通道关闭完成
while (!closeCompleteFlag) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
threadPoolExecutor.shutdownNow();//杀死所有运行结束的线程
Log.d(TAG_THREAD, "线程池排队数量:" + threadPoolExecutor.getQueue().size());
Log.d(TAG_THREAD, "正在运行的线程数量:" + threadPoolExecutor.getActiveCount());
Log.d(TAG_THREAD, "执行完成的线程数量" + threadPoolExecutor.getCompletedTaskCount());
}
@Override
protected 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);
}
}
//openCV4Android 需要加载用到
private final BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
if (status == LoaderCallbackInterface.SUCCESS) {
Log.i(TAG, "OpenCV loaded successfully");
} else {
super.onManagerConnected(status);
}
}
};
private Bitmap returnBitMap(String message) {
byte[] frame = Base64.decode(message.getBytes(), Base64.DEFAULT);
try{
Mat mat = Imgcodecs.imdecode(new MatOfByte(frame),Imgcodecs.IMREAD_UNCHANGED);
Bitmap bmp = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mat,bmp);
return bmp;
}catch (Exception e){
return null;
}
}
/**
* 连接设置
*/
private void setupConnectionFactory() {
factory = new ConnectionFactory();
factory.setHost("47.242.239.206");
factory.setPort(5672);
factory.setUsername("zdx19981006");
factory.setPassword("19981006");
}
// 缩放函数
private Bitmap scaleBitmap(Bitmap origin, float ratio) {
if (origin == null) {
return null;
}
int width = origin.getWidth();
int height = origin.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(ratio, ratio);
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
if (newBM.equals(origin)) {
return newBM;
}
origin.recycle();
return newBM;
}
@Override
public void onBackPressed() {
//选择确定会杀死所有线程
closeMyActivity();
super.onBackPressed();
}
//销毁Activity回调方法
@Override
protected void onDestroy() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPoolExecutor.shutdownNow();
Log.d(TAG_THREAD, "线程池排队数量:" + threadPoolExecutor.getQueue().size());
Log.d(TAG_THREAD, "正在运行的线程数量:" + threadPoolExecutor.getActiveCount());
Log.d(TAG_THREAD, "执行完成的线程数量" + threadPoolExecutor.getCompletedTaskCount());
super.onDestroy();
}
}
- 对应的
xml
文件:activity_main_opencv.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<SurfaceView
android:id="@+id/surfaceViewTest"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
- 在
AndroidManifest.xml
中修改主启动类进行测试
<!--测试opencv-->
<activity android:name=".OpencvTestActivity"
tools:ignore="IntentFilterExportedReceiver">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
五、启动程序,查看日志
- 连接手机,打开开发者选项进行调试
- 点击
Logcat
,输入opencv启动日志
检索日志 - 出现以下日志即成功集成opencv