人脸识别各算法详解

article/2025/10/4 18:33:30

人脸识别各算法详解

最近,由于工作需要,为了找到一款高效的人脸识别算法,对各种人脸识别算法都研究了一番,以下记录的是各算法的理论基础。

一.MTCNN

本文章主要介绍MTCNN算法的流程,MTCNN主要由三个框架组成,分别是PNet,RNet,ONet。下面将分别介绍这三个部分。

理论基础:
在这里插入图片描述

PNet

Proposal Network (P-Net):该网络结构主要获得了人脸区域的候选窗口和边界框的回归向量。并用该边界框做回归,对候选窗口进行校准,然后通过非极大值抑制(NMS)来合并高度重叠的候选框。

因为实际的图片大小不一,所以PNet是一个全卷积网络,对于输入的图片大小可以是任意值;将图片输入PNet之前,有一个循环,每一次循环会将图片进行缩放,再输入PNet;这样形成一个图片金字塔,图片每次缩放因子是0.80(论文的初始值应该是0.709),当宽高小于12时候这张图片对应的循环结束,12是PNet的最小图片输入尺寸。下图表示的是PNet的结构

在这里插入图片描述
由上面这张图可以得到,一张12x12x3的图片最终的输出的结果是1x1x32的特征图,再分成三条支路,用于人脸分类、边框回归、人脸特征点定位。

这三条支路的损失函数分别是交叉熵(二分类问题常用)、平方和损失函数、5个特征点与标定好的数据的平方和损失。最后的总损失是三个损失乘上各自的权重比之和,在PNet里面三种损失的权重是1:0.5:0.5。将图片输入PNet后,得到了cls_cls_map, reg这两个数组,其中cls_cls_map是(H,W,2)的二维数组,就是非人脸和人脸的概率。PNet直接输出的边界框并不是传统回归中的边界坐标,而是预测人脸位置相对于输入图片的位置差,即为reg。

将cls_cls_map里面人脸的概率和一个事先设定的阈值相比较,如果大于这个阈值,就将这张图片对应的reg数组里面的预测值提取出来,通过逆运算得到原始像素坐标。对于 x * y 的输入,将产生大小为[(x−12)/2+1]∗[(y−12)2+1]的输出。因为池化层的步长是2,所以上述式子的分母为2。将reg的坐标通过此方法可还原预测边框值在原始图片的像素坐标。最后返回的数组是(x1,y1,x2,y2,score,reg),其中(x1,y1,x2,y2)是bbox在原始图片中的像素坐标。score是cls_cls_map对应的人脸概率。

完成这一步后,将使用非极大值抑制法(NMS)去掉一些重复框,这个算法的原理是将上一步返回的数组的score值最大的那一行元素提取出来,将剩下的所有的元素的score和一个设定好的阈值相比较,将score值大于阈值(0.5)的元素抛弃,再将剩下的元素重复之前的提取最大值并进行比较的操作。直到最后,这样就初步抛弃了那些重合度较高的人脸框。

因为(x1,y1,x2,y2)是在原图像中的像素坐标,reg是候选框区域相对于像素坐标的偏差,这样讲将原像素坐标加上偏差值,即可得到候选框的坐标。将初步筛选后的的bbox按照上面的方法refine,到此为止,PNet这一部分就结束了。输出的是候选框的4个坐标加上对应的score值。

RNet

Refine Network (R-Net):该网络结构还是通过边界框回归和NMS来去掉那些false-positive区域。只是由于该网络结构和P-Net网络结构有差异,多了一个全连接层,所以会取得更好的抑制false-positive的作用。

首先将PNet的输出resize成正方形,这主要是基于人脸一般都是正方形的。
再将PNet生成的bbox里的元素调整一下,生成(dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph)这样的数组;生成的元素的意义如下:

	1>. dx,dy:bbox的相对本身起点坐标(0,0)。2>. edx,edy:bbox的相对本身终点坐标(tmpw-1, tmph-1)。3>. x,y : 原始图片的bbox起点。4>. ex,ey:原始图片的bbox结束点。

在生成的过程还会有检测,避免bbox的坐标超出原始图片或者为负值;接下来遍历这个数组,将里面的bbox从原始图片里面抠出来,resize成24x24同时进行归一化。

完成了前面的操作后就是将24x24的图片喂入RNet了,下图表示的是RNet的结构:
在这里插入图片描述
可以看出RNet最后是采用的全连接层,这也是为什么喂入的图片统一成24x24大小。

由上面这张图可以得到,一张24x24x3的图片最终的输出的结果是3x3x64的特征图,再经历全连接层后分成三条支路,用于人脸分类、边框回归、人脸特征点定位。这三条支路的损失函数和PNet的一样,各损失的权重比也为1:0.5:0.5。

将图片输入RNet后,得到了cls_scores, reg这两个数组,cls_scores表示非人脸和人脸的概率,reg表示bbox的回归信息。同样将cls_scores中人脸的概率与实现设定的阈值比较,将大于阈值的图片对应的bbox提取出来,过滤掉一部分非人脸的bbox。

接着再次调用NMS,抛弃掉大量的重叠率高的人脸框,经过两次的筛选,剩下的bbox的数量就少了很多。最后进行RNet的最后一步操作,就是回归信息reg来调整bbox的坐标,大致就是将bbox的4个坐标乘上bbox的宽或者高,其中x和宽相乘,y和高相乘。最后就是返回调整后的四个坐标

ONet

Output Network (O-Net):该层比R-Net层又多了一层卷基层,所以处理的结果会更加精细。作用和R-Net层作用一样。但是该层对人脸区域进行了更多的监督,同时还会输出5个地标(landmark)。
首先将RNet的输出resize成正方形,接下来的操作和对应的RNet部分相似,只是再喂入ONet之前图片是resize乘48x48。

在这里插入图片描述
将48x48x3的图片喂入ONet后输出的是3x3x128的特征图,经过全连接层后同样是有着三条支路。三条支路的损失函数与PNet、RNet一样,但是三个损失函数的权重比为1:0.5:1。

这次从ONet的输出接受cls_scores, reg, landmark这三个数组,同样先根据cls_scores的人脸概率是否大于设定的阈值来抛弃一部分非人脸框。接下来就是确定landmark的值,因为前面直接得到的关键点的x、y坐标相关信息并不是x、y的值,而是一个相对于宽高的偏置值,最终的关键点的x、y值可以通过这个偏置值和bbox的宽或者高(x与宽,y与高)相乘再与bbox的坐标相加得到。

接下来就是回归信息reg来调整bbox的坐标,与RNet输出前的操作一样。完成之后经历两次的NMS操作,但是这次的NMS操作与之前的略有不用,大家可以看详细的代码解释。最后就可以输出bbox和landmark了,至此算法就结束了。

二.PFLD

PFLD算法,目前主流数据集上达到最高精度、ARM安卓机140fps,模型大小仅2.1M!

算法思想:
在这里插入图片描述

其中,黄色曲线包围的是主网络,用于预测特征点的位置;
绿色曲线包围的部分为辅网络,在训练时预测人脸姿态(有文献表明给网络加这个辅助任务可以提高定位精度,具体参考原论文),这部分在测试时不需要。

对于上述影响精度的挑战,修改loss函数在训练时关注那些稀有样本,而提高计算速度和减小模型size则是使用轻量级模型。

Loss函数设计

Loss函数用于神经网络在每次训练时预测的形状和标注形状的误差。
考虑到样本的不平衡,作者希望能对那些稀有样本赋予更高的权重,这种加权的Loss函数被表达为:
在这里插入图片描述
M为样本个数,N为特征点个数,Yn为不同的权重,|| * ||为特征点的距离度量(L1或L2距离)。(以Y代替公式里的希腊字母)

进一步细化Yn:
在这里插入图片描述
其中:
在这里插入图片描述
即为最终的样本权重。
K=3,这一项代表着人脸姿态的三个维度,即yaw, pitch, roll 角度,可见角度越高,权重越大。
C为不同的人脸类别数,作者将人脸分成多个类别,比如侧脸、正脸、抬头、低头、表情、遮挡等,w为与类别对应的给定权重,如果某类别样本少则给定权重大。

主网络

作者使用轻量级的MobileNet,其参数如下:
在这里插入图片描述

辅网络

参数如下:
在这里插入图片描述

验证结果如下:
在这里插入图片描述

三. 虹软人脸识别算法

虹软属于人脸检测技术,能够帮助您检测并且定位到影像(图片或者视频)中的人脸。

本文将以这三个库为基础,从人脸注册开始,到人脸识别结束。全程演示人脸识别的流程。

人脸信息是保存在AFR_FSDKFace类中的。这的主要结构为

  public static final int FEATURE_SIZE = 22020;byte[] mFeatureData;

如果要进行人脸注册,我们需要定义另外一个类来把人脸信息和姓名关联起来。

class FaceRegist {String mName;List<AFR_FSDKFace> mFaceList;public FaceRegist(String name) {mName = name;mFaceList = new ArrayList<>();}
}

包含特征信息的长度和内容的byte数组。
我们把这些功能定义在类FaceDB中。FaceDB需要包含引擎定义,初始化,把人脸信息保存在版本库和从版本库中读出人脸信息这些功能

初始化引擎
为了程序结构性考虑,我们将人脸识别相关的代码独立出来一个类FaceDB,并定义必要的变量

	public static String appid = "bCx99etK9Ns4Saou1EbFdC18xHdY9817EKw****";public static String ft_key = "CopwZarSihp1VBu5AyGxfuLQdRMPyoGV2C2opc****";public static String fd_key = "CopwZarSihp1VBu5AyGxfuLXnpccQbWAjd86S8****";public static String fr_key = "CopwZarSihp1VBu5AyGxfuLexDsi8yyELdgsj4****";String mDBPath;List<FaceRegist> mRegister;AFR_FSDKEngine mFREngine;AFR_FSDKVersion mFRVersion;

定义有参数的构造函数来初始化引擎

public FaceDB(String path) {mDBPath = path;mRegister = new ArrayList<>();mFRVersion = new AFR_FSDKVersion();mUpgrade = false;mFREngine = new AFR_FSDKEngine();AFR_FSDKError error = mFREngine.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key);if (error.getCode() != AFR_FSDKError.MOK) {Log.e(TAG, "AFR_FSDK_InitialEngine fail! error code :" + error.getCode());} else {mFREngine.AFR_FSDK_GetVersion(mFRVersion);Log.d(TAG, "AFR_FSDK_GetVersion=" + mFRVersion.toString());}}

定义析构函数释放引擎占用的系统资源

public void destroy() {if (mFREngine != null) {mFREngine.AFR_FSDK_UninitialEngine();}}

实现人脸增加和读取功能
通常人脸库会存放在数据库中,本次我们使用List来进行简单的模拟,并将其保存在文本文件中,需要时从文本中读取,保存时写入到文件中。

我们使用addFace方法将待注册的人脸信息添加到人脸库中

public  void addFace(String name, AFR_FSDKFace face) {try {//check if already registered.boolean add = true;for (FaceRegist frface : mRegister) {if (frface.mName.equals(name)) {frface.mFaceList.add(face);add = false;break;}}if (add) { // not registered.FaceRegist frface = new FaceRegist(name);frface.mFaceList.add(face);mRegister.add(frface);}if (!new File(mDBPath + "/face.txt").exists()) {if (!saveInfo()) {Log.e(TAG, "save fail!");}}//save nameFileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);ExtOutputStream bos = new ExtOutputStream(fs);bos.writeString(name);bos.close();fs.close();//save featurefs = new FileOutputStream(mDBPath + "/" + name + ".data", true);bos = new ExtOutputStream(fs);bos.writeBytes(face.getFeatureData());bos.close();fs.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}

使用loadFaces从文件中读取人脸

public boolean loadFaces(){if (loadInfo()) {try {for (FaceRegist face : mRegister) {Log.d(TAG, "load name:" + face.mName + "'s face feature data.");FileInputStream fs = new FileInputStream(mDBPath + "/" + face.mName + ".data");ExtInputStream bos = new ExtInputStream(fs);AFR_FSDKFace afr = null;do {if (afr != null) {if (mUpgrade) {//upgrade data.}face.mFaceList.add(afr);}afr = new AFR_FSDKFace();} while (bos.readBytes(afr.getFeatureData()));bos.close();fs.close();}return true;} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}} else {if (!saveInfo()) {Log.e(TAG, "save fail!");}}return false;}

实现业务逻辑
实现人脸注册功能
人脸识别的前提条件就是人脸信息要先注册到人脸库中,注册人脸库

第一步当然是获取待注册的照片,我们可以可以使用摄像头,也可以使用照片。我们使用AlertDialog弹出选择框

new AlertDialog.Builder(this).setTitle("请选择注册方式").setIcon(android.R.drawable.ic_dialog_info).setItems(new String[]{"打开图片", "拍摄照片"}, this).show();

在对应的事件处理函数中进行处理

switch (which){case 1://摄像头Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");ContentValues values = new ContentValues(1);values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");mPath = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, mPath);startActivityForResult(getImageByCamera, REQUEST_CODE_IMAGE_CAMERA);break;case 0://图片Intent getImageByalbum = new Intent(Intent.ACTION_GET_CONTENT);getImageByalbum.addCategory(Intent.CATEGORY_OPENABLE);getImageByalbum.setType("image/jpeg");startActivityForResult(getImageByalbum, REQUEST_CODE_IMAGE_OP);break;default:;
}

获取一张照片后,后续我们就需要实现人脸检测功能。

    if (requestCode == REQUEST_CODE_IMAGE_OP && resultCode == RESULT_OK) {mPath = data.getData();String file = getPath(mPath);//TODO: add image coversion}

在上面的代码中,我们获取到了我们需要的图像数据bmp,把图片取出来
我们在Application类用函数 decodeImage中实现这段代码

public static Bitmap decodeImage(String path) {Bitmap res;try {ExifInterface exif = new ExifInterface(path);int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);BitmapFactory.Options op = new BitmapFactory.Options();op.inSampleSize = 1;op.inJustDecodeBounds = false;//op.inMutable = true;res = BitmapFactory.decodeFile(path, op);//rotate and scale.Matrix matrix = new Matrix();if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {matrix.postRotate(90);} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {matrix.postRotate(180);} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {matrix.postRotate(270);}Bitmap temp = Bitmap.createBitmap(res, 0, 0, res.getWidth(), res.getHeight(), matrix, true);Log.d("com.arcsoft", "check target Image:" + temp.getWidth() + "X" + temp.getHeight());if (!temp.equals(res)) {res.recycle();}return temp;} catch (Exception e) {e.printStackTrace();}return null;
}

调用AFD_FSDK_StillImageFaceDetection返回检测到的人脸信息
人脸注册 ,首先要先检测出来人脸,对于静态图片,虹软人脸SDK中对应的是FD,提供了一个方法名称,叫AFD_FSDK_StillImageFaceDetection 。
我们来看一下参数列表

类型 名称 说明
byte[] data 输入的图像数据
int width 图像宽度
int height 图像高度
int format 图像格式
List<AFD_FSDKFace> list 检测到的人脸会放到到该列表里。
注意AFD_FSDKFace对象引擎内部重复使用,如需保存,请clone一份AFD_FSDKFace对象或另外保存

AFD_FSDKFace是人脸识别的结果,定义如下

public class AFD_FSDKFace {Rect mRect;int mDegree;}

mRect定义一个了一个矩形框Rect

在此之前我们需要注意虹软人脸SDK使用的图像格式是NV21的格式,所以我们需要将获取到的图像转化为对应的格式。在Android_extend.jar中提供了对应的转换函数

byte[] data = new byte[mBitmap.getWidth() * mBitmap.getHeight() * 3 / 2];ImageConverter convert = new ImageConverter();convert.initial(mBitmap.getWidth(), mBitmap.getHeight(), ImageConverter.CP_PAF_NV21);if (convert.convert(mBitmap, data)) {Log.d(TAG, "convert ok!");}convert.destroy();

现在我们就可以调用AFD_FSDK_StillImageFaceDetection方法了

err  = engine.AFD_FSDK_StillImageFaceDetection(data, mBitmap.getWidth(), mBitmap.getHeight(), AFD_FSDKEngine.CP_PAF_NV21, result);

绘出人脸框
在List<AFD_FSDKFace>中保存了检测到的人脸的位置信息和深度信息。
我们可以将检测到的人脸位置信息在图片上用一个矩形框绘制出来表示检测到的人脸信息。

Canvas canvas = mSurfaceHolder.lockCanvas();if (canvas != null) {Paint mPaint = new Paint();boolean fit_horizontal = canvas.getWidth() / (float)src.width() < canvas.getHeight() / (float)src.height() ? true : false;float scale = 1.0f;if (fit_horizontal) {scale = canvas.getWidth() / (float)src.width();dst.left = 0;dst.top = (canvas.getHeight() - (int)(src.height() * scale)) / 2;dst.right = dst.left + canvas.getWidth();dst.bottom = dst.top + (int)(src.height() * scale);} else {scale = canvas.getHeight() / (float)src.height();dst.left = (canvas.getWidth() - (int)(src.width() * scale)) / 2;dst.top = 0;dst.right = dst.left + (int)(src.width() * scale);dst.bottom = dst.top + canvas.getHeight();}canvas.drawBitmap(mBitmap, src, dst, mPaint);canvas.save();canvas.scale((float) dst.width() / (float) src.width(), (float) dst.height() / (float) src.height());canvas.translate(dst.left / scale, dst.top / scale);for (AFD_FSDKFace face : result) {mPaint.setColor(Color.RED);mPaint.setStrokeWidth(10.0f);mPaint.setStyle(Paint.Style.STROKE);canvas.drawRect(face.getRect(), mPaint);}canvas.restore();mSurfaceHolder.unlockCanvasAndPost(canvas);break;}
}

将人脸注册到人脸库
检测到了人脸,我们可以输入相应的描述信息,加入到人脸库中。

为了提高识别的准确性,我们可以对一个人多次注册人脸信息。

public  void addFace(String name, AFR_FSDKFace face) {try {//check if already registered.boolean add = true;for (FaceRegist frface : mRegister) {if (frface.mName.equals(name)) {frface.mFaceList.add(face);add = false;break;}}if (add) { // not registered.FaceRegist frface = new FaceRegist(name);frface.mFaceList.add(face);mRegister.add(frface);}if (!new File(mDBPath + "/face.txt").exists()) {if (!saveInfo()) {Log.e(TAG, "save fail!");}}//save nameFileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);ExtOutputStream bos = new ExtOutputStream(fs);bos.writeString(name);bos.close();fs.close();//save featurefs = new FileOutputStream(mDBPath + "/" + name + ".data", true);bos = new ExtOutputStream(fs);bos.writeBytes(face.getFeatureData());bos.close();fs.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
}

最后,别忘记了销毁人脸检测引擎哦

err = engine.AFD_FSDK_UninitialFaceEngine(); 
Log.d("com.arcsoft", "AFD_FSDK_UninitialFaceEngine =" + err.getCode()); 

实现人脸识别
上面的代码准备完毕后,就可以开始我们的人脸识别的功能了。我们使用一个第三方的扩展库,ExtGLSurfaceView的扩展 库CameraGLSurfaceView,用ImageView和TextView显示检测到的人脸和相应的描述信息。

首先是定义layout。

<?xml version="1.0" encoding="utf-8"?>

<com.guo.android_extend.widget.CameraSurfaceViewandroid:id="@+id/surfaceView"android:layout_width="1dp"android:layout_height="1dp"/><com.guo.android_extend.widget.CameraGLSurfaceViewandroid:id="@+id/glsurfaceView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerInParent="true"/><ImageViewandroid:id="@+id/imageView"android:layout_width="120dp"android:layout_height="120dp"android:layout_marginLeft="10dp"android:layout_marginTop="10dp"/><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@+id/imageView"android:layout_alignRight="@+id/imageView"android:layout_below="@+id/imageView"android:layout_marginTop="10dp"android:text="@string/app_name"android:textAlignment="center"/><TextViewandroid:id="@+id/textView1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@+id/imageView"android:layout_alignRight="@+id/imageView"android:layout_below="@+id/textView"android:layout_marginTop="10dp"android:text="@string/app_name"android:textAlignment="center"/>

因为引擎需要的图像格式是NV21的,所以需要将摄像头中的图像格式预设置为NV21

public Camera setupCamera() {// TODO Auto-generated method stubmCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);try {Camera.Parameters parameters = mCamera.getParameters();parameters.setPreviewSize(mWidth, mHeight);parameters.setPreviewFormat(ImageFormat.NV21);for( Camera.Size size : parameters.getSupportedPreviewSizes()) {Log.d(TAG, "SIZE:" + size.width + "x" + size.height);}for( Integer format : parameters.getSupportedPreviewFormats()) {Log.d(TAG, "FORMAT:" + format);}List<int[]> fps = parameters.getSupportedPreviewFpsRange();for(int[] count : fps) {Log.d(TAG, "T:");for (int data : count) {Log.d(TAG, "V=" + data);}}mCamera.setParameters(parameters);} catch (Exception e) {e.printStackTrace();}if (mCamera != null) {mWidth = mCamera.getParameters().getPreviewSize().width;mHeight = mCamera.getParameters().getPreviewSize().height;}return mCamera;
}

从摄像头识别人脸,需要使用FT库,FT库在人脸跟踪算法上对人脸检测部分进行了优化,是专门为视频处理而优化的库。

初始化人脸检测引擎(FT)
和FD一样,我们需要初始化人脸识别FT引擎。

Log.d(TAG, "AFT_FSDK_InitialFaceEngine =" + err.getCode());
err = engine.AFT_FSDK_GetVersion(version);
Log.d(TAG, "AFT_FSDK_GetVersion:" + version.toString() + "," + err.getCode());

在摄像头的预览事件处理函数中,先调用FT的人脸识函数函数,然后再调用FR中的人脸信息特征提取数函数。

AFT_FSDKError err = engine.AFT_FSDK_FaceFeatureDetect(data, width, height, AFT_FSDKEngine.CP_PAF_NV21, result);AFR_FSDKError error = engine.AFR_FSDK_ExtractFRFeature(mImageNV21, mWidth, mHeight, AFR_FSDKEngine.CP_PAF_NV21,mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree(), result);

这里面的result中保存了人脸特征信息。我们可以将其保存下来或下来并与系统中的其它信息进行对比。

AFR_FSDKMatching score = new AFR_FSDKMatching();
float max = 0.0f;
String name = null;
for (FaceDB.FaceRegist fr : mResgist) {for (AFR_FSDKFace face : fr.mFaceList) {error = engine.AFR_FSDK_FacePairMatching(result, face, score);Log.d(TAG,  "Score:" + score.getScore() + ", AFR_FSDK_FacePairMatching=" + error.getCode());if (max < score.getScore()) {max = score.getScore();name = fr.mName;}}
}

当score的特征信息大于0.6时,我们就可以认为匹配到了人脸。显示人脸匹配信息。

上面的循环中,可以看到,是遍历了真个库进行寻找。我们的目的是为了演示,实际情况下,我们可以在找到一个匹配值比较高的人脸后,就跳出循环。

MTCNN算法demo: https://download.csdn.net/download/weixin_42713739/11081627
PFLD人脸算法demo: https://download.csdn.net/download/weixin_42713739/11106807
虹软算法demo:https://download.csdn.net/download/weixin_42713739/11087890


http://chatgpt.dhexx.cn/article/rpnWNwSN.shtml

相关文章

人脸识别算法(包含简单代码)

TOC 人脸识别算法是一种利用计算机视觉和模式识别技术来识别和验证人脸的算法。它可以通过分析图像或视频中的人脸特征&#xff0c;如脸部轮廓、眼睛、鼻子、嘴巴等来识别和区分不同的个体。人脸识别算法在安全认证、身份验证、监控安防、人机交互等领域具有广泛的应用。 以下…

总结几个简单好用的Python人脸识别算法

今天给大家总结几个简单、好用的人脸识别算法。人脸识别是计算机视觉中比较常见的技术&#xff0c;生活中&#xff0c;我们接触最多的人脸识别场景是人脸考勤. 人脸识别的算法最核心的工作是从一张图片中识别出人脸的位置。识别的算法可以说是多种多样&#xff0c; 下面我就来…

追踪AR圣诞老人,免费领200份圣诞礼物

元宇宙 绿心公园 圣诞节 元宇宙&#xff08;Metaverse&#xff09;&#xff0c;是指 利用科技手段进行链接与创造的&#xff0c; 与现实世界映射与交互的虚拟世界&#xff0c; 具备新型社会体系的数字生活空间。 这一概念在2021年走入了大众视野 勾出了人们对“平行宇宙…

圣诞的礼物

呵呵 &#xff0c;与你一起分享哦。。。。。。

圣诞来了,学编程的小伙伴有收到圣诞老人的礼物?

圣诞来了&#xff0c;学编程的小伙伴有收到圣诞老人的礼物&#xff1f;——编程学习笔记满满的惊喜。 编程是编定程序的中文简称&#xff0c;就是让计算机代为解决某个问题&#xff0c;对某个计算体系规定一定的运算方式&#xff0c;使计算体系按照该计算方式运行&#xff0c;…

圣诞老人的礼物_圣诞节设计资源:圣诞老人

圣诞节对人们而言可能意味着很多事情&#xff0c;但是对于设计师而言&#xff0c;这可能是一个不同的议程。 设计师通常会挤满最后一刻的设计临时通知&#xff0c;以赶在圣诞节贺卡&#xff0c;电子贺卡设计&#xff0c;新闻通讯或其他类型的广告活动的截止日期之前。 因此&am…

微信小程序|飞翔的圣诞老人

一、前言 2022年圣诞节即将到来啦,很高兴这次我们又能一起度过~ 此篇文章主要使用小程序实现飞翔的圣诞老人,跟随文章步骤带着圣诞老人翻越障碍一起去送礼物吧。 二、实现步骤 2.1、创建小程序 访问微信公众平台,点击账号注册。选择小程序,并在表单填写所需的各项信息进行…

圣诞节礼物

对于python小白的你&#xff0c;学了python没多久是不是就有了想自己动手做一个小程序的冲动呢&#xff1f;在这里我给大家分享一个我以前做的小程序——“圣诞节礼物”——希望能给大家带来灵感和帮助。 话不多说&#xff0c;先看一下截图&#xff1a; 这是一个小动画&#xf…

2022圣诞代码合集(圣诞树+圣诞老人)

文章目录 前言使用方法圣诞树圣诞老人 前言 圣诞节里的喜悦&#xff0c;飘扬万里&#xff1b;圣诞树上的星星&#xff0c;璀璨耀眼&#xff1b;圣诞星空绽放的烟花&#xff0c;迎来吉祥&#xff1b;圣诞钟声奏响的旋律&#xff0c;传递欢乐&#xff1b;圣诞老人送给你的礼物&a…

圣诞老人来了嘛

Jingle bells jingle bells jingle all the way... 随着歌声的临近&#xff0c;一年一度的圣诞节也如期而至的到来了 传说啊&#xff0c;圣诞老人为奥丁神后裔。也传说称圣诞老人由尼古拉而来&#xff0c;所以圣诞老人也称St.Nicholas。据说他原是小亚细亚每拉城的主教&#xf…

算法基础部分6-贪心算法

算法部分 基础6 一、贪心算法简述 贪心算法的每一步行动总是按照某种指标选取最优的操作来进行该指标&#xff0c;只看眼前并不考虑以后可能造成的影响。证明方法通过替换法和数学归纳法实现。 二、贪心算法例子 1. 圣诞老人的礼物 问题描述&#xff1a;圣诞节来临了&#x…

python外包_python怎么找外包

很多程序员兄弟们工资不高&#xff0c;但时间还是比较充足的&#xff0c;剩下的时间想多搞点外快&#xff0c;毕竟要养家糊口。我来讲讲怎么做可以一个月轻松多赚1万多元的外快。 一、接项目 是程序员都想过接私活&#xff0c;但真正去做了的寥寥无几。可能的原因&#xff1a;…

国外最流行的几个外包接活网站 简要介绍

国外主要外包接活站点&#xff1a; No.1 Freelancer.com &#xff08;旧称GAF – GetAFreelancer&#xff09;: 国外访问人数最多&#xff0c;更新速度最快的外包平台。详细介绍 No.2 Elance : 国外比较成熟的三大业务外包平台之一&#xff0c;外包项目类型以软件和网站为主。当…

PHP学习笔记

学习笔记 PHP学习笔记部署遇到的问题一二 php在win10 64位下执行curl_init()方法报错的bugthinkphp登陆失效的问题不同的win系统上无法使用curl的问题thinkphp下调用python脚本先要有反应提示python不是内部命令以上处理后还提示no file /directory 关于json里为数组时出现的问…

PHP实现支付宝支付

支付宝付款,开发上比起微信支付要简单很多,今天就以支付宝手机网站支付为例,简单讲一下实现方法: 前期准备,当然就不多说了,当你想开发使用支付宝支付,必然需要在支付宝开放品台注册认证并且创建好应用并且具备手机网站支付功能!不明白可以查看支付宝官方文档(https://docs.op…

外包网站建设需要注意什么

外包网站建设需要注意什么 对于如今许多的公司来说&#xff0c;拥有一个属于公司的网站是非常必要的。但是并不是所有的公司都会拥有专业的人员来进行网站的建设&#xff0c;所以网站建设外包则成为了诸多公司的首选。但是外包网站建设都需要哪些问题呢&#xff1f; 外包网站…

苏州外包php,【苏州IT外包经验】Ubuntu 17.10系统下配置PHP+Apache+Mysql

发布于 2017年11月16日 星期四 01:15 点击数&#xff1a;20883 本文为Ubuntu 17.10系统下配置PHP7.1Apache2.4Mysql5.7 一、Apache2 安装apache sudo apt-get apache2 编辑apache主配置文件/etc/apache2/apache2.conf&#xff0c;修改KeepAlive设置 KeepAlive Off Apache默认的…

Linux任务之自动发邮件

Linux任务之自动发邮件 文章目录 Linux任务之自动发邮件1、虚拟机Centos7环境&#xff08;1&#xff09;先设置自己的邮箱&#xff0c;我这用qq邮箱&#xff1a;&#xff08;2&#xff09;获取到密码后&#xff0c;我们要在Linux上配置邮件文件&#xff1a;&#xff08;3&#…

Mac 定时自动发送邮件

文章目录 WhyHow1. 在automator中创建application2. 创建calender 自动定时发送email3. 自行测试 Refer Why 工作中&#xff0c;有时需要发送固定格式的email&#xff0c;每次人工做&#xff0c;比较繁琐&#xff0c;本篇博客描述了一种可以自动定时发送email的方式。 下面例…

如何利用 Python 自动发邮件,打工人福音

在工作中&#xff0c;每天或者每周结束的时候我们都会发送相应的日报或者周报给上级领导&#xff0c;来汇报你做了那些工作&#xff0c;可是汇报工作内容的时候我们始终都是打开邮箱、写入内容&#xff0c;发送和抄送给固定的人&#xff0c;那么这么繁琐并且重复的一件事&#…