opencv 安卓uinput 虚拟触控 opencv界面设计_qt

该demo项目包含的知识:菜单栏设置,菜单栏图标设置,菜单栏打开新窗口(界面打开新窗口可以借鉴),以及将新窗口设置成表格,label各种设置,button各种设置,几种信号槽连接,打开图片,显示图片,状态栏,opencv图片转换(mat-Qimage),打开相机,以及处理图片的几种函数(灰度图,二值化,高斯滤波,边缘检测,像素输出)

可以将代码复制编译后在慢慢看。

 interface_vs.h:

#pragma once
#include <QtWidgets/QMainWindow>
 #include "ui_interface_vs.h"
 #include <QMenuBar>
 #include <QLabel>
 #include <QPushButton>
 #include <QSlider>
 #include<opencv2/opencv.hpp>
 #include "opencv2/imgproc/imgproc.hpp"
 #include "opencv2/highgui/highgui.hpp"
 #include "opencv2/videoio/videoio.hpp"#include <QWidget>
 #include "newWindowClass.h"using namespace cv;
 using namespace std;
 #pragma once
 #pragma execution_character_set("utf-8")
 class interface_vs : public QMainWindow
 {
     Q_OBJECT

public:
 

interface_vs(QWidget *parent = nullptr);
     ~interface_vs();
     //菜单栏
     QMenuBar *pMenuBar;
     //菜单项
     QMenu *pMenuTest;
     QMenu *pMenuTest1;
     QMenu *pMenuTest2;
     //子菜单
     QAction *pActionTest;
     QAction *pActionTest1;
     QAction *pActionTest2;
     QAction *pActionTest3;
     QAction *pActionTest4;
     QAction *pActionTest5;
     QAction *pActionTest6;    QLabel *label;
     QLabel *label1;
     QLabel *label2;
     QLabel *label3;    QPushButton * pButton;
     QPushButton * pButton1;
     QPushButton * pButton2;    QSlider *slider;
    QPushButton * pButton3;
     QPushButton * pButton4;
     QPushButton * pButton5;
     QPushButton * pButton6;
     QPushButton * pButton7;    QPushButton *btn_language;

    

public slots:

//菜单响应函数
     void OnActionTest();
     void OnActionTest1();    void ClickButton();
     void ClickButton1();
     void ClickButton2();
     void SliderChangeImageshow(int a);    void OpenCamera();
     void print_px_value(Mat& im);
     void ShowPoint();
     void New_algorithm();
     void cvt();
     void vi();
     void newWindow();
 private:
     newWindowClass * nw;
     Ui::interface_vsClass ui;
 };

interface_vs.cpp:

#include "interface_vs.h"
 #include "opencv2/opencv.hpp"
 #include "opencv2/imgproc/imgproc.hpp"
 #include "opencv2/highgui/highgui.hpp"
 #include "opencv2/videoio/videoio.hpp"
 #include <QDialog>
 #include <QFileDialog>
 #include <QDebug>
 #include <QMessageBox>
 #include <qvector.h>
 #include <QHeaderView>QString image_address;
 Mat After_processing;//处理后的图片
 interface_vs::interface_vs(QWidget *parent)
     : QMainWindow(parent)
 {
     ui.setupUi(this);
     //指定菜单栏
 #ifdef USE_DEFAULT_MENU_BAR
     //添加菜单栏(此处添加为的为QMainWindow自带的菜单)
     pMenuBar = this->menuBar();
 #else
     //添加自定义菜单
     pMenuBar = new QMenuBar(this);
 #endif    //定义菜单项
     //(&n)代表快捷方式,当窗口获得焦点时按alt+n即可打开“菜单”菜单项
     pMenuTest = new QMenu("菜单", this);
     pMenuTest1 = new QMenu("test1",this);
     pMenuTest2 = new QMenu("test2",this);    //定义子菜单
     //(&s)为子菜单快捷键,当打开该菜单项后,按下‘s’键即可响应
     pActionTest = new QAction("打开新窗口", this);
     //新建一个带图标的菜单项,图标使用资源文件中的资源
     pActionTest1 = new QAction(QIcon("C:\\Users\\25125\\Desktop\\1.jpg"), "打开图片", this);
     //pActionTest2 = new QAction("测试2",this);
     pActionTest3 = new QAction("exit", this);
     pActionTest4 = new QAction("测试", this);
     pActionTest5 = new QAction("测试", this);
     pActionTest6 = new QAction("测试", this);
     //将菜单项添加到子菜单
     pMenuTest->addAction(pActionTest);
     pMenuTest->addAction(pActionTest1);
     //在菜单项之间添加分割线
     pMenuTest->addSeparator();
     //pMenuTest->addAction(pActionTest2);
     pMenuTest->addAction(pActionTest3);    pMenuTest1->addAction(pActionTest4);
    pMenuTest2->addAction(pActionTest5);
     pMenuTest2->addAction(pActionTest6);    //将子菜单添加到菜单栏
     pMenuBar->addMenu(pMenuTest);
     pMenuBar->addMenu(pMenuTest1);
     pMenuBar->addMenu(pMenuTest2);    this->setMaximumSize(1600, 1200);
     this->setMinimumSize(1200, 1000);
     this->setWindowTitle("菜单项");    QToolBar *toolbar = new QToolBar(this);
     addToolBar(Qt::TopToolBarArea, toolbar);
     toolbar->addAction("新建");
     toolbar->addSeparator();
     toolbar->addAction("打开");
     toolbar->addSeparator();
     toolbar->addAction("关闭");    //显示图片label
     label = new QLabel(this);
     label->move(5, 100);                                //将标签移动到(0,100)位置
     label->resize(200, 160);                            //设定标签大小
     label->setStyleSheet("border:1px solid black");
     //显示文字label
     label1 = new QLabel(this);
     label1->setText(tr("显示图片"));                    //文本label
     label1->move(50, 280);
     //setCentralWidget(label); label居中
     //显示处理后图片的label
     label2 = new QLabel(this);                         //处理后label显示
     label2->move(500, 100);
     label2->resize(200, 160);
     label2->setStyleSheet("border:1px solid black");    label3 = new QLabel(this);
     label3->move(800, 100);
     label3->resize(200, 100);
     label3->setStyleSheet("border:1px solid black");
     
     //label3->setWordWrap(true);
     //opencv高斯处理
     pButton = new QPushButton(this);
     pButton->move(220, 100);
     pButton->setText("高斯处理");
     //二值化
     pButton1 = new QPushButton(this);
     pButton1->move(220, 140);
     pButton1->setText("二值化(80)");
     //滑动条设置二值化值 
     slider = new QSlider(Qt::Horizontal, this);    //Qt::Horizontal设置为水平的滑动条
     //设置滑动条样式
     slider->setStyleSheet("QSlider::groove:horizontal{height:12px; left:0px; right:0px; border:0px; border-radius:6px; background:rgb(242,242,242);}    \
                            QSlider::handle:horizontal{width:24px; background:#1644B0; border-radius:12px; margin:-6px 0px;}  \
                            QSlider::sub-page:horizontal{background:#4C85FB; border:0px; border-radius:6px;}");
     slider->move(220, 180);//设置滑动条位置
     slider->setRange(0, 255);//设置滑动条取值范围
     slider->setFixedWidth(255);//设置滑动条长度
     //opencv边缘检测
     pButton2 = new QPushButton(this);
     pButton2->move(220, 220);
     pButton2->setText("canny");    //opencv打开相机
     pButton3 = new QPushButton(this);
     pButton3->setText("打开相机");
     pButton3->move(5, 400);
         //给按钮设置图案
         QIcon icon(".\\ico\\U2.png");    //创建icon对象,保存相对应的图片路径。
         btn_language = new QPushButton(this);    //创建button对象
         btn_language->move(120, 400);
         btn_language->setFixedSize(100, 40); //设置按钮宽高
         //btn_language->setIcon(icon);//将图片填充进按钮中
         btn_language->setIcon(icon);//将图片填充进按钮中
         btn_language->setIconSize(QSize(btn_language->width(), btn_language->height()));//让图片的尺寸和按钮对应
         QImage image = QImage("C:\\MyGit\\mms_v2\\mms_v2\\mms_v2\\ico\\U2.png");
         label->setPixmap(QPixmap::fromImage(image));
         label->setScaledContents(true);//图片自适应大小    pButton4 = new QPushButton(this);
     pButton4->move(220, 250);
     pButton4->setText("显示像素");    pButton5 = new QPushButton(this);
     pButton5->setText("New_algorithm");
     pButton5->move(220, 300);
     pButton5->adjustSize();    pButton6 = new QPushButton(this);
     pButton6->setText("灰度图");
     pButton6->move(220, 330);
     pButton6->adjustSize();    pButton7 = new QPushButton(this);
     pButton7->setText("暂计数");
     pButton7->move(220, 370);
     pButton7->adjustSize();
     //QPixmap Images("C:/Users/25125/Pictures/Saved Pictures/hp/aa.jpg");
     //QPalette Palette = this->palette();//设置背景图
     //Palette.setBrush(QPalette::Background, Images);
     //this->setPalette(Palette);     
 #ifndef USE_DEFAULT_MENU_BAR
     //当不使用QMainWindow自带的菜单栏时,必须要加上此行
     setMenuBar(pMenuBar);
 #endif    //添加菜单响应函数
     connect(pActionTest, &QAction::triggered, this, &interface_vs::newWindow);
     connect(pActionTest3, &QAction::triggered, this, &interface_vs::OnActionTest);
     connect(pActionTest1, &QAction::triggered, this, &interface_vs::OnActionTest1);//按钮响应函数
     connect(pButton, SIGNAL(clicked()), this, SLOT(ClickButton()));
     connect(pButton1, SIGNAL(clicked()), this, SLOT(ClickButton1()));
     connect(pButton2, SIGNAL(clicked()), this, SLOT(ClickButton2()));//滑动条响应函数
     connect(slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChangeImageshow(int)));
     connect(pButton3, SIGNAL(clicked()), this, SLOT(OpenCamera()));
     connect(pButton4, SIGNAL(clicked()), this, SLOT(ShowPoint()));
     connect(pButton5, SIGNAL(clicked()), this, SLOT(New_algorithm()));
     connect(pButton6, SIGNAL(clicked()), this, SLOT(cvt()));
     connect(pButton7, SIGNAL(clicked()), this, SLOT(vi()));
 } //菜单exit响应函数
 void interface_vs::OnActionTest()
 {
     QApplication *app;
     app->quit();
 } void interface_vs::newWindow() {
     nw = new newWindowClass();
     //设置几行几列
     nw->setRowAndCol(3, 5);
     //设置表格中每一行的表头
     QStringList header;
     header << "1" << "2" << "3" << "4" << "5";
     nw->setHorizontalHeaderLabels(header);
     //3行5列
     for (int i = 0; i < 3; i++)
         for (int j = 0; j < 5; j++)
         {
             QTableWidgetItem *item = new QTableWidgetItem(QIcon(":/image/search.png"), "td");
             nw->setItem(i, j, item);
         }    nw->setMinimumSize(600, 400);
     nw->setWindowTitle("表格");
     nw->show();}
 //灰度图
 void interface_vs::cvt() {
     Mat srcimage = imread(image_address.toStdString());//原始图片的路径来自于打开图片
     //变为灰度图
     cvtColor(srcimage, srcimage, COLOR_BGR2GRAY);
     After_processing = srcimage;
     QImage twoImage = QImage((const unsigned char*)(srcimage.data), srcimage.cols, srcimage.rows, QImage::Format_Grayscale8);
     twoImage = twoImage.scaled(label2->width(), label2->height());
     label2->setPixmap(QPixmap::fromImage(twoImage));
 }//该函数是之前项目设计算法时用到,可以不看
 //遍历像素,连续的存入vector<int>中 
 void interface_vs::vi() {
     Mat srcimage = imread(image_address.toStdString());//原始图片的路径来自于打开图片
     Mat originImg;
     //变为灰度图
     cvtColor(srcimage, originImg, COLOR_BGR2GRAY);     vector<vector<int>> vv;
     int t_count = 0;//暂存数
     int rowNumber = originImg.rows;  //行数
     int colNumber = originImg.cols * originImg.channels();  //列数 x 通道数=每一行元素的个数
     //双重循环,遍历所有的像素值
     for (int i = 0; i < rowNumber; i++)  //行循环
     {
         //每张图片进来都会先找自己的暂存数,所以可以另外写一个函数做下面的操作 需要的参数:i,originImg,colNumber,返回值是vector<vector<int>>类型             vector<int> ve;
             uchar* data = originImg.ptr<uchar>(i);  //获取第i行的首地址
             for (int j = 0; j < colNumber; j++)   //列循环
             {
                 //data[j] = data[j] / div * div + div / 2;
                 int a = 0;
                 if ((int)data[j] >= 0 && (int)data[j] < 200) {//比较拿到黑色像素的
                     if (ve.empty()) {
                         ve.push_back(j);//记录位置而不是像素值
                         continue;
                     }
                     else {
                         for (int k = 0; k < ve.size(); k++) {
                             if (j == ve.at(k) + 1) {
                                 ve.push_back(j);
                                 a = 1;
                             }
                         }
                         if (a) {
                             a = 0;
                             continue;
                         }
                         vv.push_back(ve);
                         t_count++;//暂存计数
                         ve.clear();
                         ve.push_back(j);                    }
                 }
             }  //行处理结束
                 }


        
  

//遍历vector
     QString text;
     
     for (int k = 0; k < vv.size(); k++) {
         text += "(";
         for (int t = 0;t<vv.at(k).size();t++) {
             text += QString::number(vv.at(k).at(t));
         }
         text += ")";
     }
     label3->setText("暂计数:" + QString::number(t_count)+":"+ text);
     label3->adjustSize();
 } void interface_vs::New_algorithm(){
     Mat srcimage = imread(image_address.toStdString());//原始图片的路径来自于打开图片
     Mat originImg, binaryImage;
     //变为灰度图
     cvtColor(srcimage, originImg, COLOR_BGR2GRAY);
     QString text;    vector<int> ve;
     vector<vector<int>> vv;
     int count = 0;//计数
     //遍历像素
     int rowNumber = originImg.rows;  //行数
     int colNumber = originImg.cols * originImg.channels();  //列数 x 通道数=每一行元素的个数
     //双重循环,遍历所有的像素值
     for (int i = 0; i < rowNumber; i++)  //行循环
     {        uchar* data = originImg.ptr<uchar>(i);  //获取第i行的首地址
         for (int j = 0; j < 1; j++)   //列循环
         {
             //data[j] = data[j] / div * div + div / 2;
             if ((int)data[j] > 0 && (int)data[j] < 255) {//比较拿到黑色像素的
                 ve.push_back((int)data[j]);
                 count++;
             }
         }  //行处理结束
     }
     //遍历vector
     for (int k = 0; k < ve.size(); k++) {
         text += QString::number(ve.at(k))+",";
     }
     label3->setText(QString::number((int)ve.size())+";"+QString::number(count)+":"+text);
     label3->adjustSize();
 } //菜单打开图片响应函数
 void interface_vs::OnActionTest1()
 {    QString filename = QFileDialog::getOpenFileName(this, tr("Open Image"), QDir::homePath(), tr("(*.jpg)\n(*.bmp)\n(*.png)"));   //打开图片文件,选择图片
     image_address = filename;
     qDebug() << "filename:" << filename;
     QImage image = QImage(filename);   //图片初始化
     qDebug() << "image:" << image;
     if (!image.isNull())
     {
         label->setPixmap(QPixmap::fromImage(image));
         label->setScaledContents(true);//图片自适应大小
         //label->show();
         ui.statusBar->showMessage(tr("Open Image Success!"), 3000); //打开成功时显示的内容    }
     else
     {
         ui.statusBar->showMessage(tr("Save Image Failed!"), 3000);
         return;
     }    //     QDialog dlg;
     //     dlg.setWindowTitle("测试菜单响应");
     //     dlg.exec();
 }void interface_vs::print_px_value(Mat& im)
 {
     QString text;
     int rowNumber = im.rows;  //行数
     int colNumber = im.cols * im.channels();  //列数 x 通道数=每一行元素的个数
     text = QString::number(rowNumber) + "行;" + QString::number(colNumber)+"列";
     //双重循环,遍历所有的像素值
     for (int i = 0; i < rowNumber; i++)  //行循环
     {
         uchar* data = im.ptr<uchar>(i);  //获取第i行的首地址
         text += "<br>"+QString::number(i) + ":";
         for (int j = 0; j < colNumber; j++)   //列循环
         {
             //data[j] = data[j] / div * div + div / 2;
             text += QString::number((int)data[j])+",";
         }  //行处理结束
     }
     label3->setText(text);
     label3->adjustSize();   //自适应大小
 } //高斯滤波按钮响应函数
 void interface_vs::ClickButton() {
     //读取原始图片
     if (image_address == "") {
         QMessageBox::critical(NULL, "警告", "未打开任何图片", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
         return ;
     }    Mat srcimage = imread(image_address.toStdString());//原始图片的路径来自于打开图片
     //Mat转QImage 颜色
     cvtColor(srcimage, srcimage, COLOR_BGR2RGB);
     //Mat转QImage 像素  oldlabel放置原图
     QImage disimage = QImage(srcimage.data, srcimage.cols, srcimage.rows, srcimage.cols*srcimage.channels(), QImage::Format_RGB888);    Mat retimage;
     //高斯模糊
     GaussianBlur(srcimage, retimage, Size(3, 5), 0, 0);//高斯核的大小必须是奇数(size中两数的乘积必须为奇数)
     //GaussianBlur(retimage, retimage, Size(9, 9), 0, 0);
     After_processing = retimage;
     //Mat转QImage 像素  newlabel放置图像处理后图片
     QImage disimage2 = QImage(retimage.data, retimage.cols, retimage.rows, retimage.cols*retimage.channels(), QImage::Format_RGB888);
     disimage2 = disimage2.scaled(label2->width(), label2->height());
     label2->setPixmap(QPixmap::fromImage(disimage2));
 }//二值化
 void interface_vs::ClickButton1() {
     //读取原始图片
     if (image_address == "") {
         QMessageBox::critical(NULL, "警告", "未打开任何图片", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
         return;
     }
     Mat srcimage = imread(image_address.toStdString());//原始图片的路径来自于打开图片    Mat originImg, binaryImage;
     cvtColor(srcimage, originImg, COLOR_BGR2GRAY);
     //imshow("src",originImg);
 //       QImage orImg = QImage((const unsigned char*)(originImg.data),
 //       originImg.cols, originImg.rows, QImage::Format_Grayscale8);
        //QImage orImg = QImage(originImg.data,originImg.cols,originImg.rows,originImg.cols*originImg.channels(),QImage::Format_RGB888);
     threshold(originImg, binaryImage, 80, 255.0, THRESH_BINARY);
     After_processing = binaryImage;
     QImage twoImage = QImage((const unsigned char*)(binaryImage.data), binaryImage.cols, binaryImage.rows, QImage::Format_Grayscale8);
     twoImage = twoImage.scaled(label2->width(), label2->height());
     label2->setPixmap(QPixmap::fromImage(twoImage));
     //               //存储二值化后的图像
     //                Mat binaryImage;
     //               //-------初始的二值化阈值为80
     //               threshold(binaryImage, srcimage, 80, 255.0, THRESH_BINARY);
     //               //QImage twoImage = QImage((const unsigned char*)(binaryImage.data),binaryImage.cols, binaryImage.rows, QImage::Format_Grayscale8);
     //               QImage twoImage = QImage(binaryImage.data,binaryImage.cols,binaryImage.rows,binaryImage.cols*binaryImage.channels(),QImage::Format_RGB888);
     //               label2->setPixmap(QPixmap::fromImage(twoImage));}
 //滑动条控制二值化
 void interface_vs::SliderChangeImageshow(int a) {
     int thres = a;
     qDebug() << thres;
     if (image_address == "") {
         QMessageBox::critical(NULL, "警告", "未打开任何图片", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
         return;
     }
     Mat srcimage = imread(image_address.toStdString());
     Mat binaryImage;
     cvtColor(srcimage, srcimage, COLOR_BGR2GRAY);
     threshold(srcimage, binaryImage, thres, 255.0, THRESH_BINARY);
     After_processing = binaryImage;
     QImage twoImage = QImage((const unsigned char*)(binaryImage.data), binaryImage.cols, binaryImage.rows, QImage::Format_Grayscale8);
     twoImage = twoImage.scaled(label2->width(), label2->height());
     label2->setPixmap(QPixmap::fromImage(twoImage));}
 //边缘检测
 void interface_vs::ClickButton2() {
     if (image_address == "") {
         QMessageBox::critical(NULL, "警告", "未打开任何图片", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
         return;
     }
     Mat srcimage = imread(image_address.toStdString());
     Mat edgeImg;
     Canny(srcimage, edgeImg, 30, 80);
     After_processing = edgeImg;
     QImage twoImage = QImage((const unsigned char*)(edgeImg.data), edgeImg.cols, edgeImg.rows, QImage::Format_Grayscale8);
     label2->setPixmap(QPixmap::fromImage(twoImage));
 }//使用opencv调用摄像头
 void  interface_vs::OpenCamera() {
     Mat frame;
     VideoCapture cap(0);//摄像头调用    int race = 0;
     while (cap.read(frame))
     {        if (race == 1) {
             break;
         }
         imshow("frame", frame);
         race = 1;
         waitKey(1000);
     }     namedWindow("Vidio", 1);
 }//显示处理后图片的像素
 void interface_vs::ShowPoint() {
     if (After_processing.data == 0) {
         QMessageBox::critical(NULL, "警告", "没有未处理后的图片", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
         return;
     }
     print_px_value(After_processing);
 }interface_vs::~interface_vs()
 {}

newWindowClass.h:

#pragma once
#include <QWidget>
 #include "ui_newWindowClass.h"#include <QTableWidget>
 #pragma execution_character_set("utf-8")
 class newWindowClass : public QTableWidget
 {
     Q_OBJECTpublic:
     newWindowClass(QWidget *parent = nullptr);
     ~newWindowClass();
     QTableWidget *tableWidget;     void setRowAndCol(int row, int col);
private:
     Ui::newWindowClassClass ui;
 };


 

newWindowClass.cpp

#include "newWindowClass.h"
#include <QMessageBox>
 #include <QHeaderView>
 #include <QMouseEvent>
 #include <QDebug>newWindowClass::newWindowClass(QWidget *parent)
     : QTableWidget(parent)
 {
     this->setFont(QFont("微软雅黑", 12));     this->setShowGrid(false);    //不显示网格线
     this->setSelectionBehavior(QAbstractItemView::SelectRows);   //设置整行选择
     this->setMouseTracking(true);    //开启捕获鼠标功能
     this->setEditTriggers(QAbstractItemView::NoEditTriggers);    //禁止编辑表格
     this->verticalHeader()->setVisible(false);//纵向表头可视化
     this->horizontalHeader()->setStyleSheet("QHeaderView::section {""background-color:#383838;}");
     this->setStyleSheet("background-color:#1a1a1a;color:#fff;gridline-color: rgb(0, 0, 0)");//设置当前颜色
     this->horizontalHeader()->setDefaultSectionSize(120);  //设置默认宽度  不设置的话宽度也会平均分配 但不会按照窗口宽度分配
     this->verticalHeader()->setDefaultSectionSize(30);   //设置一行默认高度//    this->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//自适应所有列,让它布满空间。设置了就列就不能拉伸
 //    this->horizontalHeader()->resizeSections(QHeaderView::Stretch);//列可以拉伸    this->horizontalHeader()->setStretchLastSection(true);//最后一列铺满
    this->setAlternatingRowColors(true);// 设置隔行背景色
     this->setPalette(QPalette(QColor(70, 70, 70)));//参数还可写成QPalette(Qt::blue) QPalette(QColor(36,36,36))
     this->setStyleSheet("selection-background-color:#0e4b72");  //设置选中行颜色 //    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);//设置水平滚动条
 //    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);//设置垂直滚动条}
void newWindowClass::setRowAndCol(int row, int col) {
     this->setRowCount(row);   //设置行数为10
     this->setColumnCount(col); //设置列数为5
 }newWindowClass::~newWindowClass()
 {}


 

效果展示:

opencv 安卓uinput 虚拟触控 opencv界面设计_#include_02

 

opencv 安卓uinput 虚拟触控 opencv界面设计_opencv_03

 

opencv 安卓uinput 虚拟触控 opencv界面设计_opencv_04