1 功能需求

(1)手机端打开摄像头实时预览;
(2)手机端作为服务端,PC端作为客户端连接;
(3)连接成功后PC端可以同时预览手机端的摄像头采集的图像;
(4)PC端点击拍照可以控制手机端拍摄一张照片,并将照片传给PC端。

2 功能模块

(1)安卓手机打开摄像头并实现预览和拍照功能;
(2)手机端开启监听,并在连接成功后将摄像头采集的数据传给PC;
(3)手机端读取PC发送的命令指令,执行相应的操作。

3 开发流程

3.1 开启摄像头实现预览
(1) 获取摄像头权限,并添加自动对焦属性。
  在应用程序的manifest.xml文件中添加使用手机摄像头权限。由于程序需要使用socket的通信过程,将手机作为服务端,因此需要添加网络权限。

<uses-permission android:name="android.permission.CAMERA" /><uses-feature android:name="android.hardware.camera" />  <uses-feature android:name="android.hardware.camera.autofocus" /><uses-permission Android:name="android.permission.INTERNET"/>

2)实现相机的实时预览。
   安卓系统使用SurfaceView即可完成预览功能。在布局文件中添加SurfaceView。activity_main.xml布局文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_light"
android:orientation="vertical" ><SurfaceViewandroid:id="@+id/surview"android:layout_width="fill_parent"android:layout_height="fill_parent"android:scaleType="fitCenter" /></LinearLayout>

(3)在MainActivity 的onCreate中初始化SurfceView。初始化方法如下:

private void initSurfaceView() {surfaceView = (SurfaceView) findViewById(R.id.surview);surfaceHolder = surfaceView.getHolder();surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);surfaceHolder.setKeepScreenOn(true);surfaceHolder.addCallback(new Callback() {@Overridepublic void surfaceDestroyed(SurfaceHolder arg0) {}@Overridepublic void surfaceCreated(SurfaceHolder arg0) {// 开启摄像头startCamera(curCameraIndex);}@Overridepublic void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,int arg3) {}});surfaceView.setFocusable(true);surfaceView.setBackgroundColor(TRIM_MEMORY_BACKGROUND);}

4)SurfceView创建完成时调用开启摄像头的方法startCamera()。startCamera方法如下,其中代码含义已在注释程序中注明。

// 根据索引初始化摄像头
@SuppressLint("NewApi")
public void startCamera(int cameraIndex) {// 先停止摄像头stopCamera();// 再初始化并打开摄像头if (camera == null) {//打开手机摄像头,cameraIndex为摄像头索引,0代表后置摄像头,1代表前置摄像头。camera = Camera.open(cameraIndex);//创建摄像头操作工具类cameraUtil = new CameraUtil(camera, callback);//开启预览cameraUtil.initCamera(srcFrameHeight, srcFrameWidth, surfaceHolder);Log.e(TAG, "打开相机");}
}// 停止并释放摄像头
public void stopCamera() {if (camera != null) {camera.setPreviewCallback(null);camera.stopPreview();camera.release();camera = null;}
}//摄像头开启预览后采集到的数据回调接口
PreviewCallback callback = new PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {Size size = camera.getParameters().getPreviewSize();try {if (times == 0) {YuvImage image = new YuvImage(data, ImageFormat.NV21,size.width, size.height, null);if (image != null) {// 将YuvImage对象转为字节数组ByteArrayOutputStream outputStream = new ByteArrayOutputStream();image.compressToJpeg(new Rect(0, 0, size.width,size.height), 100, outputStream);byte[] srcData = outputStream.toByteArray();int len = srcData.length;// 字节数组转为BitmapBitmap src = BitmapFactory.decodeByteArray(srcData, 0,len);src = BitmapUtil.rotate(src, 90);// 压缩Bitmap,并获取压缩后的字节数组,即可获取预览数据文件// outdata数据即是待发送给PC端的数据byte[] outdata = BitmapUtil.transImage(src,srcFrameWidth / 4, srcFrameHeight / 4);int datalen = outdata.length;if (isOpen) {// 写入头sendData(new byte[] { (byte) 0xA0 });// 写入数组长度sendData(intToByteArray(datalen));// 写入数据值sendData(outdata);}// 回收Bitmapif (!src.isRecycled()) {src.recycle();}}}} catch (Exception e) {e.printStackTrace();}}
};

(5)CameraUtil为设置摄像头的辅助类,代码如下:

public class CameraUtil {//摄像头类Camera camera;int cameraIndex;//预览视图宽和高int srcFrameWidth;int srcFrameHeight;SurfaceHolder surfaceHolder;PreviewCallback callback;public CameraUtil(Camera camera, PreviewCallback callback) {this.camera = camera;this.callback = callback;}//摄像头预览初始化public void initCamera(final int srcFrameWidth, final int srcFrameHeight, final SurfaceHolder surfaceHolder) {this.srcFrameHeight = srcFrameHeight;this.srcFrameWidth = srcFrameWidth;this.surfaceHolder = surfaceHolder;Camera.Parameters params = camera.getParameters();//params.setPreviewSize(srcFrameWidth / 4, srcFrameHeight / 4);//设置预览格式params.setPreviewFormat(ImageFormat.NV21);params.setPreviewFrameRate(30);//设置图像质量params.setJpegQuality(100);params.setPictureFormat(ImageFormat.JPEG);//设置预览方向params.set("orientation", "portrait");params.set("rotation", 90);//设置对焦模式为自动连续对焦params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦camera.setParameters(params);camera.setDisplayOrientation(90);// 设置显示图像的SurfaceViewtry {camera.setPreviewDisplay(surfaceHolder);} catch (IOException e) {e.printStackTrace();}camera.setPreviewCallback(callback);//启动预览camera.startPreview();camera.autoFocus(new AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean result, Camera camera) {// 自动对焦完成时回调if (result) {initCamera(srcFrameWidth, srcFrameHeight, surfaceHolder);camera.cancelAutoFocus();}}});}@SuppressLint("NewApi") public void startCamera(int cameraIndex) {this.cameraIndex = cameraIndex;// 先停止摄像头stopCamera();// 再初始化并打开摄像头if (camera == null) {camera = Camera.open(cameraIndex);initCamera(srcFrameWidth, srcFrameHeight, surfaceHolder);       }}//停止预览public void stopCamera() {if (camera != null) {camera.setPreviewCallback(null);camera.stopPreview();camera.release();camera = null;}}
}

6)BitmapUtil为图片操作辅助类

public class BitmapUtil {// Bitmap按照一定大小转为字节数组,以便写入socket进行发送public static byte[] transImage(Bitmap bitmap, int width, int height) {// bitmap = adjustPhotoRotation(bitmap, 90);try {int bitmapWidth = bitmap.getWidth();int bitmapHeight = bitmap.getHeight();float scaleWidth = (float) width / bitmapWidth;float scaleHeight = (float) height / bitmapHeight;Matrix matrix = new Matrix();matrix.postScale(scaleWidth, scaleHeight);// 创建压缩后的BitmapBitmap resizeBitemp = Bitmap.createBitmap(bitmap, 0, 0,bitmapWidth, bitmapHeight, matrix, false);// 压缩图片质量ByteArrayOutputStream outputStream = new ByteArrayOutputStream();resizeBitemp.compress(CompressFormat.JPEG, 80, outputStream);// 转为字节数组byte[] byteArray = outputStream.toByteArray();outputStream.close();// 回收资源if (!bitmap.isRecycled()) {bitmap.recycle();}if (!resizeBitemp.isRecycled()) {resizeBitemp.recycle();}return byteArray;} catch (Exception ex) {ex.printStackTrace();}return null;}//图片旋转操作public static Bitmap rotate(Bitmap bitmap, float degree) {Matrix matrix = new Matrix();// matrix.setScale(0.5f, 0.5f);// 缩小为原来的一半matrix.postRotate(degree);// 旋转45度 == matrix.setSinCos(0.5f, 0.5f);bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, true);return bitmap;}
}

3.2 连接手机与PC

手机与PC的连接实际上是使用socket通信完成的。将手机端作为服务器开启监听。PC端发起连接,通过adb命令的端口转换将PC的连接请求发送至手机,建立手机与PC的连接。主要操作流程如下:

(1) onCreate中注册广播的监听
注册广播监听主要是接收PC端调用adb发送的广播信息,用于通知手机端开启监听和停止监听。

IntentFilter filter = new IntentFilter();filter.addAction("NotifyServiceStart");filter.addAction("NotifyServiceStop");registerReceiver(receiver, filter);

(2) 接收系统广播

public class ServiceBroadcastReceiver extends BroadcastReceiver {private static final String START_ACTION = "NotifyServiceStart";private static final String STOP_ACTION = "NotifyServiceStop";@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (START_ACTION.equalsIgnoreCase(action)) {// 启动服务Log.e(TAG, "收到广播信息启动监听");new Thread() {public void run() {if (serverSocket != null) {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}//服务端启动监听doListen();};}.start();} else if (STOP_ACTION.equalsIgnoreCase(action)) {}}
}

(3)启动监听
手机端作为服务端,启动监听。注意对于网络的操作不能在主线程中。启动监听的方法doListen()如下:

// 启动服务器端监听private void doListen() {serverSocket = null;try {serverSocket = new ServerSocket(SERVER_PORT);while (true) {Socket socket = serverSocket.accept();Log.e(TAG, "监听到设备连接,启动通信线程");//PC端连接成功,启动通信线程ThreadReadWriterIOSocketthreadSocket = new ThreadReadWriterIOSocket(socket);new Thread(threadSocket).start();}} catch (IOException e) {Log.e(TAG, "服务端监听失败");e.printStackTrace();}}

4)ThreadReadWriterIOSocket为负责通信的线程。
ThreadReadWriterIOSocket是实现了Runnable接口的一个线程,其主要任务就是接收PC端发送的命令信息,并将命令信息通过EventBus框架发送至主界面。

public class ThreadReadWriterIOSocket implements Runnable {private static String TAG = "ThreadReadWriterIOSocket";private Socket client;private BufferedOutputStream out;private BufferedInputStream in;boolean isConnecting = false;private String cmd = "";public ThreadReadWriterIOSocket(Socket client) {this.client = client;}@Overridepublic void run() {Log.e(TAG, "有客户端连接上");isConnecting = true;try {// 获取输入输出流out = new BufferedOutputStream(client.getOutputStream());in  = new BufferedInputStream(client.getInputStream());// 循环等待,接受PC端的命令while (isConnecting) {try {if (!client.isConnected()) {break;}// 读取命令cmd = readCMDFromSocket(in);    Log.e(TAG, "读取到PC发送的命令" + cmd);/* 根据命令分别处理数据 */if (cmd.equals(Constant.CONNECT)) {// 收到连接命令EventBus.getDefault().post(new MessageEvent(Constant.START));out.flush();} else if (cmd.equalsIgnoreCase(Constant.DISCONNECT)) {// 断开命令EventBus.getDefault().post(new MessageEvent(Constant.STOP));out.flush();}else if (cmd.equals(Const    ant.TAKEPHOTO)) {//拍照命令EventBus.getDefault().post(new MessageEvent(Constant.TAKEPHOTO));out.flush();}in.reset();} catch (Exception e) {e.printStackTrace();}}out.close();in.close();} catch (Exception e) {e.printStackTrace();} }public void cancel() {isConnecting = false;}//发送数据public void writeData(byte[] data) {if (out != null) {try {out.write((data));} catch (IOException e) {Log.e(TAG, "输入输出异常");e.printStackTrace();}}}/* 读取命令 */public String readCMDFromSocket(InputStream in) {int MAX_BUFFER_BYTES = 2048;String msg = "";byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];try {int numReadedBytes = in.read(tempbuffer, 0, tempbuffer.length);msg = new String(tempbuffer, 0, numReadedBytes, "utf-8");tempbuffer = null;} catch (Exception e) {Log.e(TAG, "readCMDFromSocket读数异常" + e.toString());EventBus.getDefault().post(new MessageEvent(Constant.DISCONNECT));e.printStackTrace();}return msg;}
}

(5)主界面接收EventBus传递的命令信息
通信的通知过程采用EventBus实现,在主界面中注册EventBus。
onCreate中注册EventBus

EventBus.getDefault().register(this);

创建onMessageEvent方法实现事件处理函数,并将事件处理交给主线程。

@Subscribe(threadMode = ThreadMode.MAIN)public void onMessageEvent(MessageEvent event) {switch (event.message) {case Constant.DISCONNECT://断开命令Toast.makeText(MainActivity.this, "客户端断开", Toast.LENGTH_LONG).show();threadSocket.cancel();case Constant.START://收到连接命令isOpen = true;Toast.makeText(MainActivity.this, "客户端连接上", Toast.LENGTH_LONG).show();startCamera(curCameraIndex);break;case Constant.STOP://收到断开命令stopCamera();isOpen = false;break;case Constant.TAKEPHOTO://收到拍照命令if (isOpen) {camera.takePicture(new ShutterCallback() {@Overridepublic void onShutter() {}}, new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] arg0, Camera arg1) {}}, new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {// 向电脑端发送数据int datalen = data.length;if (isOpen) {// 写入数据头sendData(new byte[] { (byte) 0xA1 });// 写入数组长度sendData(intToByteArray(datalen));// 写入数据值sendData(data);}// 重新开启预览camera.stopPreview();camera.startPreview();}});}}}

6)当建立连接之后可以向PC端发送数据。
数据的发送需要调用ThreadReadWriterIOSocket的write方法,将图像信息数据发送给PC。发送方法sendData如下:

public void sendData(final byte[] data) {threadSocket.writeData(data);}

3.3 PC端发起连接

PC端作为客户端需要向服务端发起连接。PC的端的程序使用Java实现。主函数代码如下:

 public class Main {//用于通信的socketprivate static Socket socket;//输入输出数据流private static OutputStream out;private static InputStream in;public  static MyFrame frame;private static String CMD = "";public static void main(String[] args) {//创建界面frame = new MyFrame();//添加连接按钮JPanel panel2 = new JPanel();panel2.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 0));final JButton button = new JButton("连接");panel2.add(button);button.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent arg0) {new Thread() {public void run() {//点击连接按钮,PC端发起连接onConnect();};}.start();button.setEnabled(false);}});//创建断开按钮JButton button2 = new JButton("断开");button2.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent arg0) {//发送断开命令CMD = Constant.DISCONNECT;try {out.flush();out.write(CMD.getBytes());out.flush();                } catch (IOException e) {e.printStackTrace();}button.setEnabled(true);}});panel2.add(button2);//创建拍照按钮JButton button3 = new JButton("拍照");button3.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent arg0) {//发送拍照命令CMD = Constant.TAKEPHOTO;try {out.flush();out.write(CMD.getBytes());out.flush();                    } catch (IOException e) {   e.printStackTrace();}           }});panel2.add(button3);frame.add(panel2, BorderLayout.SOUTH);frame.setResizable(false);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);}//启动连接时使用adb发送系统广播protected static void onConnect() {try {//adb指令,发送NotifyServiceStop广播,手机接收到此广播停止监听Runtime.getRuntime().exec("adb shell am broadcast -a NotifyServiceStop");Thread.sleep(2000);//调用adb的端口转换命令,将PC端发起的tcp连接指向手机服务器Runtime.getRuntime().exec("adb forward tcp:12580 tcp:10086"); // 端口转换Thread.sleep(2000);//发送NotifyServiceStart广播,手机接收到此广播启动监听Runtime.getRuntime().exec("adb shell am broadcast -a NotifyServiceStart");Thread.sleep(3000);} catch (Exception e) {e.printStackTrace();}int len = 0;int degree = 0;try {//PC端发起TCP连接InetAddress serveraddr = null;serveraddr = InetAddress.getByName("127.0.0.1");System.out.println("TCP 1 " + "正在连接。。。。");socket = new Socket(serveraddr, 12580);System.out.println("TCP 2 " + "连接成功 ");// 获取输入输出流out = new BufferedOutputStream(socket.getOutputStream());in  = new BufferedInputStream(socket.getInputStream());// 发送连接命令CMD = Constant.CONNECT;out.write(CMD.getBytes());out.flush();boolean flag = true;    if (socket.isConnected()) {while (flag) {try {int head = in.read();if (head == 0xA0) {//获取数据头byte[] src = new byte[4];len = in.read(src);len = byteArrayToInt(src);//获取数据长度if (len > 0) {int cclen = 0;byte[] srcData = new byte[len];while (cclen < len) {int readlen = in.read(srcData, cclen, len- cclen);cclen += readlen;}//将图像数据转为ImageIcon对象ImageIcon icon = new ImageIcon(srcData);if (icon != null) {//显示图像frame.setIcon(icon, degree);}}}else if (head == 0xA1) {//获取数据头byte[] src = new byte[4];len = in.read(src);len = byteArrayToInt(src);//获取数据长度if (len > 0) {int cclen = 0;byte[] srcData = new byte[len];while (cclen < len) {int readlen = in.read(srcData, cclen, len- cclen);cclen += readlen;}//保存图片文件File file = new File("D:\\images\\" + System.currentTimeMillis() + ".jpg");if (!file.exists()) {file.mkdirs();}//存储图片                      ImageIcon icon = new ImageIcon(srcData);Image image = icon.getImage();BufferedImage bi = new BufferedImage(image.getWidth(null),image.getHeight(null),BufferedImage.TYPE_INT_RGB);Graphics2D g2d = bi.createGraphics();g2d.drawImage(image, 0, 0, null);g2d.dispose();ImageIO.write(bi,"jpg",file);}}} catch (Exception e) {}}}} catch (Exception e1) {System.out.println(" 连接出现异常:连接失败: " + e1.toString());}}public static int byteArrayToInt(byte[] b) {return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16| (b[0] & 0xFF) << 24;}}

MyFrame类:

public class MyFrame extends JFrame {private static final long serialVersionUID = 1L;public MyPanel panel;public MyFrame() {// 默认的窗体名称this.setTitle("USB连接显示");// 获得面板的实例panel = new MyPanel();this.add(panel);this.pack();this.setVisible(true);}//设置图片public void setIcon(ImageIcon incon, int drgree) {  panel.setImage(incon, drgree);}}

MyPanel类:

 public class MyPanel extends Panel {private static final long serialVersionUID = 1L;private Image bufferImage;private final Image screenImage = new BufferedImage(800, 600, 2);private final Graphics2D screenGraphic = (Graphics2D) screenImage.getGraphics();int degree = 90;private Image backgroundImage = screenImage;public MyPanel() {// 设定焦点在本窗体setFocusable(true);// 设定初始构造时面板大小,这里先采用图片的大小setPreferredSize(new Dimension(800, 600));// 绘制背景drawView();}public void setImage(ImageIcon icon, int degree) {//System.out.println("设置图片   偏转角度" + degree);this.degree = degree;backgroundImage = icon.getImage();drawView();repaint();}private void drawView() {int width  = getWidth();int height = getHeight();int x = width / 2 - backgroundImage.getWidth(null) /2;int y = height / 2 - backgroundImage.getHeight(null) / 2;screenGraphic.drawImage(backgroundImage, x , y, null);}@Overridepublic void update(Graphics g) {if (bufferImage == null) {bufferImage = this.createImage(this.getWidth(), this.getHeight());}//bufferImage = rotateImage(bufferimage, degree);Graphics gBuffer = bufferImage.getGraphics();// 获得图片缓冲区的画笔if (gBuffer != null){paint(gBuffer);}else{paint(g);}gBuffer.dispose();g.drawImage(bufferImage, 0, 0, null);}//显示图像public void paint(Graphics g) {g.drawImage(screenImage, 0, 0, null);}public static BufferedImage rotateImage(final BufferedImage bufferedimage,final int degree) {int w = bufferedimage.getWidth();int h = bufferedimage.getHeight();int type = bufferedimage.getColorModel().getTransparency();BufferedImage img;Graphics2D graphics2d;(graphics2d = (img = new BufferedImage(w, h, type)).createGraphics()).setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);graphics2d.rotate(Math.toRadians(degree), w / 2, h / 2);graphics2d.drawImage(bufferedimage, 0, 0, null);graphics2d.dispose();return img;}}

两个工程均用到的Constant类

public class Constant {public static final String addrIp = "192.168.0.119";public static final int addrPort = 56168;public static final String CONNECT = "CONNECT";public static final String DISCONNECT = "DISCONNECT";public static final String TAKEPHOTO = "TAKEPHOTO";public static final String START = "START";public static final String STOP = "STOP";}

需要源码的同学可以找我,怎么找我呢。接着看:
针对Android程序员,除了上面的知识体系,我也分享一份私货,分享我从网络上收录整理的 Android学习PDF+架构视频+面试文档+源码笔记 ,还有Android开发面试专题资料,高级进阶架构资料供大家学习进阶
学习资料可以进群找我:434543138。当然都是免费的,备注来处就行。

安卓干货——PC 连接手机实现摄像头预览相关推荐

  1. Android调用前后摄像头同时工作,安卓系统 有没有手机前后摄像头同时使用,一边给别人拍一边可以自拍...

    安卓系统 有没有手机前后摄像头同时使用,一边给别人拍一边可以自拍以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 安卓系统 ...

  2. 【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )

    文章目录 安卓直播推流专栏博客总结 一. Android 端数据采集涉及到的相关概念 二. Camera 预览图像尺寸设置 三. 获取摄像头采集的数据格式 安卓直播推流专栏博客总结 Android R ...

  3. USB摄像头预览识别二维码

    博客: 安卓之家 掘金: jp1017 微博: 追风917 CSDN: 蒋朋的家 简书: 追风917 前言 二维码现在用的超级多,其实它就是一种编码,把字符串编码保存成一个图片,我们扫描图片得到字符串 ...

  4. Android 摄像头预览悬浮窗,可拖动,可显示在其他app上方

    市面上常见的摄像头悬浮窗,如微信.手机QQ的视频通话功能,有如下特点: 整屏页面能切换到一个小的悬浮窗 悬浮窗能运行在其他app上方 悬浮窗能跳回整屏页面,并且悬浮窗消失 我们探讨过用CameraX打 ...

  5. 推荐几个手机网站在线预览测试工具

    随着移动互联网的爆发,移动端的布局成了大多数企业的刚需,而手机网站也慢慢成为一个营销企业的标配,包括百度.当当网在内的很多网站,移动端流量已经超过PC端流量,越来越的企业都在建立和完善自己的手机网站, ...

  6. PS设计稿如何在手机中快速预览?

    这是一个广告,不好意思浪费你一两分钟的时间看一下. Design Play设计稿预览这是一个很小众的软件,它有APP和插件组成使用,说实话,我在已经使用了四年ps情况下,才知道可以实时在手机中实时预览 ...

  7. 基于camera2 untiy悬浮窗摄像头预览分析

    前言 在某机系统上 unity 导出的android 工程 使用camerax 悬浮窗口 bindToLifecycle 加上预览界面就显示不出来(手机上没问题,原因没找到),不得以 改用camera ...

  8. 探索iOS之多摄像头预览架构

    在iOS13.0开始支持多摄像头预览AVCaptureMultiCamSession,然后iOS15.0增加支持摄像头画中画预览.在使用之前,我们通过isMultiCamSupported()判断是否 ...

  9. 使用实时摄像头预览的iOS对象检测(六)

    目录 介绍 应用布局 捕获相机反馈 相机反馈预览 完成相机预览配置 结论 总目录 将ONNX对象检测模型转换为iOS Core ML(一) 解码Core ML YOLO对象检测器(二) 使用数组操作解 ...

  10. C# 海康DVR客户端开发系列(3)—— 连接DVR和图像预览

    前言 一直没有稳定的DVS供我测试用,朋友那边也是频频宕掉,所以延误至今,所幸还是出来了.此外非常遗憾的是没能用WPF实践成功,关键是IntPtr句柄设置不对,没法显示出来,为了保证进度也只好暂时放弃 ...

最新文章

  1. Linux的Unicon资料
  2. C#数据结构与算法揭秘15
  3. 指定wsus服务器,步骤 2:在服务器上安装 WSUS
  4. Java黑皮书课后题第10章:10.4(MyPoint类)设计一个名为MyPoint的类,代表一个以x坐标和y坐标表示的点
  5. 随机信号的傅里叶分析
  6. CF 1529E. Trees of Tranquillity
  7. 使用Docker 安装Elasticsearch、Elasticsearch-head、IK分词器 和使用
  8. adb 更新 android sdk,[转载]安装Android时SDK AVD MANAGER时更新报错的解决办法
  9. thinkphp三级分销小程序源码_山东谷道微信小程序商城源码带后台 公众号平台三级分销系统...
  10. Qt使用udp的Broadcast实现广播图片
  11. 域猫(域名分享平台)
  12. Selenium实现QQ空间说说自动点赞
  13. Lightroom Classic CC 2019 for Mac永久破解激活方法(含lr cc 2019破解补丁)
  14. 把图标变成圆形的html_css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局...
  15. 植物大战僵尸修改金币以及关卡
  16. 后端程序员生产力工具合集
  17. 麦肯锡全球研究院 人机共存的新纪元: 自动化、就业和生产力
  18. 下载visual studio 2022 Professional 离线包
  19. 解决Pycharm出现的Debug无法正常运行(Frames are not available)的问题
  20. 攻防世界——pwn_forgot

热门文章

  1. 不知道如何写好作文?写好作文的十大技巧赶紧收好
  2. mybatis替换大于号或者小于号
  3. OSPF协议的四种网络类型
  4. 3.3.2 WTL应用向导安装和项目建立
  5. CV学习笔记【1】:transforms
  6. 反思:太阳岛风景区的前生今世
  7. android7.1刷supersu,Android怎么刷SuperSU 安卓系统刷SuperSU Pro教程
  8. 2014年5月欧洲地区SAT写作真题及解题技巧
  9. p2psear正在连接服务器,P2PSearcher无法连接到网络,也无法连接到服务器
  10. 山西省计算机教师招聘试卷,山西教师招聘考试试题(一)