【TensorFlow-serving】初步学习模型部署

前言

初步学习tensorflow serving的手写数字识别模型部署。包括简单的模型训练、保存、部署上线。因为对docker和网络不太熟悉,可能会有部分错误,但是看完博客,能跑通整个流程。此博客将详细介绍流程,但是不详细介绍每个流程的每步的含义,因为这些步骤不会随着任务的不同而发生太大改变。在后续博客中可能会精细介绍每一步的含义。

国际惯例,参考博客:

tensorflow官方文档:低阶API保存和恢复

tensorflow官方文档:tensorflow serving

tensorflow github案例:mnist和resnet

Tensorflow SavedModel模型的保存与加载

如何用TF Serving部署TensorFlow模型

Tensorflow Serving | Tensorflow Serving

Tensorflow使用SavedModel格式模型

我们给你推荐一种TensorFlow模型格式

使用 TensorFlow Serving 和 Docker 快速部署机器学习服务

如何将TensorFlow Serving的性能提高超过70%?

模型构建

跟之前的博客一样,简单搭建一个卷积网络,输入数据是mnist,还有损失函数和评估函数:

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_datasteps = 1000
batch_size = 100
mnist = input_data.read_data_sets('./mnist_dataset',one_hot=True)def conv_network(x):x = tf.reshape(x,[-1,28,28,1])# 第一层卷积conv1 = tf.layers.conv2d(inputs=x,filters=32,kernel_size=[5,5],activation=tf.nn.relu)conv1 = tf.layers.max_pooling2d(conv1,pool_size=[2,2],strides=[2,2])#第二层卷积conv2 = tf.layers.conv2d(inputs=conv1,filters=64,kernel_size=[3,3],activation=tf.nn.relu)conv2 = tf.layers.max_pooling2d(conv2,pool_size=[2,2],strides=[2,2])#第三层卷积conv3 = tf.layers.conv2d(inputs=conv2,filters=32,kernel_size=[3,3],activation=tf.nn.relu)conv3 = tf.layers.max_pooling2d(inputs=conv3,pool_size=[2,2],strides=[2,2])#全连接fc1 = tf.layers.flatten(conv3)fc1 = tf.layers.dense(fc1,500,activation=tf.nn.relu)#输出分类fc2 = tf.layers.dense(fc1,10)return fc2#输入输出容器
input_x = tf.placeholder(dtype=tf.float32,shape=[None,28*28],name='X')
input_y = tf.placeholder(dtype=tf.int32,shape=[None,10])#损失函数
model = conv_network(input_x)
logit_loss = tf.nn.softmax_cross_entropy_with_logits_v2(labels=input_y,logits=model)
optimize = tf.train.AdamOptimizer(0.001).minimize(logit_loss)
#评估
pred_equal = tf.equal(tf.arg_max(model,1),tf.arg_max(input_y,1))
accuracy = tf.reduce_mean(tf.cast(pred_equal,tf.float32))

模型保存

传统方法checkpoint

这部分就不细说了,我们之前训练模型基本都是这个方法:

init = tf.global_variables_initializer()
saver = tf. train.Saver(max_to_keep=1)
tf.add_to_collection('pred',model)with tf.Session() as sess:sess.run(init)for step in range(steps):data_x,data_y = mnist.train.next_batch(batch_size)test_x,test_y = mnist.test.next_batch(1000)        train_acc = sess.run(optimize,feed_dict={input_x:data_x,input_y:data_y})        if(step % 100==0 or step==1):accuracy_val = sess.run(accuracy,feed_dict={input_x:data_x,input_y:data_y})print('steps:{0},val_loss:{1}'.format(step,accuracy_val))#保存模型print('train finished!')saver.save(sess,'./model/cnn')

主要就是利用tf.train.Saver保存训练好的模型

tesorflow serving准备的模型保存方法

第一步:准备好模型需要保存的位置以及版本控制:

model_version = 1 #版本控制
export_path_base = '/tmp/cnn_mnist'
export_path =  os.path.join(tf.compat.as_bytes(export_path_base),tf.compat.as_bytes(str(model_version)))
print('Exporting trained model to',export_path)
builder = tf.saved_model.builder.SavedModelBuilder(export_path)tensor_info_x = tf.saved_model.utils.build_tensor_info(input_x)
tensor_info_y = tf.saved_model.utils.build_tensor_info(model)
prediction_signature = (tf.saved_model.signature_def_utils.build_signature_def(inputs={'images':tensor_info_x},outputs={'scores':tensor_info_y},method_name = tf.saved_model.signature_constants.PREDICT_METHOD_NAME))

此处注意,如果你的export_path_base/model_version目录存在,将会报错,因为tensorflow serving有一个有点就是在更新模型的时候,无需停止服务,服务是根据版本来控制的,所以每次训练都是一个新版本。而且这个模型最好是绝对路径,因为后续部署服务的时候,模型不能是相对路径。

错误提示:

AssertionError: Export directory already exists. Please specify a different export directory: b'/tmp/cnn_mnist/1'

第二步:将输入输出打包起来,方便从客户端接收参数

prediction_signature = (tf.saved_model.signature_def_utils.build_signature_def(inputs={'images':tensor_info_x},outputs={'scores':tensor_info_y},method_name = tf.saved_model.signature_constants.PREDICT_METHOD_NAME))

注意这里有个method_name, 这个需要用tf.saved_model.signature_constants.里面的一系列NAME,因为后续客户端传递给服务端的请求是json格式,而predcitregressclassify任务的json格式有区别,具体格式看这里,当然后面也会讲到

第三步:就是在Session中保存模型了

#训练与保存
with tf.Session() as sess:sess.run(tf.global_variables_initializer())for step in range(steps):data_x,data_y = mnist.train.next_batch(batch_size)test_x,test_y = mnist.test.next_batch(1000)        train_acc = sess.run(optimize,feed_dict={input_x:data_x,input_y:data_y})        if(step % 100==0 or step==1):accuracy_val = sess.run(accuracy,feed_dict={input_x:data_x,input_y:data_y})print('steps:{0},val_loss:{1}'.format(step,accuracy_val))#保存模型    builder.add_meta_graph_and_variables(sess,[tf.saved_model.tag_constants.SERVING],signature_def_map = {'predict_images':prediction_signature},main_op = tf.tables_initializer(),strip_default_attrs = True)builder.save()print('Done exporting')

这一步,官方文档有详细介绍,具体参数的使用没仔细看,目前只需要前面三个必须传入sesstagsignature_def_map,重点是将上面定义的包含输入输出与任务种类的prediction_signature传进来。给个名字predict_images是为了后续调用服务的时候,说明我们要调用哪个服务,所以这个signature_def_map理论上应该可以包含多个任务接口,而官方例子也有相关操作:

  builder.add_meta_graph_and_variables(sess, [tf.saved_model.tag_constants.SERVING],signature_def_map={'predict_images':prediction_signature,tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:classification_signature,},main_op=tf.tables_initializer(),strip_default_attrs=True)

至此,为tensorflow serving提供的模型文件是如何训练和保存的已经介绍完毕,在下一篇博客应该会探索如何将训练好的checkpoint转换为tensorflow serving可使用的模型文件。

通过docker部署模型

安装docker的方法在这里能找到,或者docker官方文档我当时好像就一句话搞定:

sudo apt install docker.io

因为我以前没装过docker,服务器上用过一丢丢。

tensorflow serving镜像

首先拉取tensorflow的镜像:

docker pull tensorflow/serving

有时候由于环境限制,可以从别人pull好的镜像中恢复,镜像的导出和导入可参考此处,主要用到了:

有镜像的电脑导出镜像:

docker save 582a4 > tensorflow_serving.tar

其中582a4是用docker images查看的tensorflow/serving的ID。

无镜像的电脑导入镜像:

docker load < tensorflow_serving.tar 

通常导入以后,REPOSITORYTAGnone,最好给个名区分:

docker tag 91abe tensorflow/serving:latest

这里我备用了一份tensorflow/serving镜像:

链接: https://pan.baidu.com/s/1l_ZGVkRKcP4HgSKxGgekRA 提取码: ewqv

启动在线服务

方法一:

docker run -p 9500:8500 -p:9501:8501 \
--mount type=bind,source=/tmp/cnn_mnist,target=/models/cnn_mnist \
-e MODEL_NAME=cnn_mnist -t tensorflow/serving

这句话的意思就是:

  • 启动docker容器container

  • 实现gRPCREST端口到主机端口的映射,注意,port1:port2,前者是主机端口,后者是tensorflow serving dockergRPCREST端口。主机端口port1可以随便改,只要没被占用,但是tensorflow serving docker的两个端口固定,不能动。

终端通过sudo netstat -nap可以看到tcp6中开启了两个端口,分别就是95009501

运行容器后的最后一部分输出是

2019-09-03 11:21:48.489776: I tensorflow_serving/servables/tensorflow/saved_model_warmup.cc:103] No warmup data file found at /models/cnn_mnist/1/assets.extra/tf_serving_warmup_requests
2019-09-03 11:21:48.489938: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: cnn_mnist version: 1}
2019-09-03 11:21:48.504477: I tensorflow_serving/model_servers/server.cc:324] Running gRPC ModelServer at 0.0.0.0:8500 ...
[warn] getaddrinfo: address family for nodename not supported
2019-09-03 11:21:48.519991: I tensorflow_serving/model_servers/server.cc:344] Exporting HTTP/REST API at:localhost:8501 ...
[evhttp_server.cc : 239] RAW: Entering the event loop ...

可以发现,服务端自动查找新模型,同事给出了gRPCREST的端口,但是这连个端口貌似用不了,难道是因为我们做映射了?后面所有的访问,无论是用dockerip还是用hostip,一律通过ip:9500/9501接收请求。

方法二

docker run -t --rm -p 9500:8500 -p:9501:8501 \-v "/tmp/cnn_mnist:/models/cnn_mnist" \-e MODEL_NAME=cnn_mnist \tensorflow/serving

其实和上面一样,只不过对docker的用法不同而已。

如果对docker比较熟悉,可以两种方法都记住,不熟悉的话,熟记一种方法就行了。

测试服务是否开通

下面的dockeriphostip分别为ifconfig -a查出来的dockerhostip

  • 测试1:
    输入:curl http://localhost:8501/v1/models/cnn_mnist
    输出:curl: (7) Failed to connect to localhost port 8501: 拒绝连接

  • 测试2:
    输入:curl http://localhost:8500/v1/models/cnn_mnist
    输出:curl: (7) Failed to connect to localhost port 8500: 拒绝连接

  • 测试3:
    输入:curl http://dockerip:9500/v1/models/cnn_mnist
    输出:

    Warning: Binary output can mess up your terminal. Use "--output -" to tell 
    Warning: curl to output it to your terminal anyway, or consider "--output 
    Warning: <FILE>" to save to a file.
    

    说明没有拒绝连接

  • 测试4:
    输入:curl http://hostip:9500/v1/models/cnn_mnist
    输出:

    Warning: Binary output can mess up your terminal. Use "--output -" to tell 
    Warning: curl to output it to your terminal anyway, or consider "--output 
    Warning: <FILE>" to save to a file.
    

    说明没有拒绝连接

  • 测试5:
    输入:curl http://dockerip:9501/v1/models/cnn_mnist
    输出:

    {"model_version_status": [{"version": "1","state": "AVAILABLE","status": {"error_code": "OK","error_message": ""}}]
    }
    

    没拒绝连接

  • 测试6:
    输入:curl http://hostip:9501/v1/models/cnn_mnist
    输出:

    {"model_version_status": [{"version": "1","state": "AVAILABLE","status": {"error_code": "OK","error_message": ""}}]
    }
    

暂时测试这几种情况,其余组合自己可以测试看看,如果拒绝连接,那就说明ip和对应接口组合不通,无法调用服务。

调用模型

上面说过了,tensorflow serving有两类端口:gRPCREST API,关于这两个区别,可以查看这里,下面分别讲解tensorflow serving中分别怎么请求和解析返回数据。

注意手写数字识别模型接受的是$ (None,784) $的向量

使用gRPC

引入必要包:

import argparse
import tensorflow as tf
from tensorflow_serving.apis  import predict_pb2,prediction_service_pb2_grpc
import grpc
import numpy as np
import cv2

定义入口接收参数:

parser = argparse.ArgumentParser(description='mnist recognization client')
parser.add_argument('--host',default='0.0.0.0',help='serve host')
parser.add_argument('--port',default='9000',help='serve port')
parser.add_argument('--image',default='',help='image path')
FLAGS = parser.parse_args()

所以,用户需要输入的都有:ip、端口、输入图像

读取图像:

img = cv2.imread(FLAGS.image,cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img,(28,28))
_,img = cv2.threshold(img,250,255,cv2.THRESH_BINARY)
img = np.array(img,dtype='float32')
img = img.reshape((28*28))
print(img.shape)  #(784,)

连接服务

server = FLAGS.host + ':' + FLAGS.port
channel = grpc.insecure_channel(server)
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

请求服务

request = predict_pb2.PredictRequest()
request.model_spec.name = 'cnn_mnist'
request.model_spec.signature_name = 'predict_images'
request.inputs['images'].CopyFrom(tf.contrib.util.make_tensor_proto(img,shape=[1,28*28]))
result = stub.Predict(request,10.0)

【注】

  • 这里有prediction_service_pb2_grpcpredict_pb2,那么是否有classifyregress对应库呢?后面学习的时候再看。
  • 还有就是因为模型接收的是tensor,所以得用tf.contrib.util.make_tensor_proto转换

解析请求

scores=result.outputs['scores'].float_val
pred_label = np.argmax(scores)
print('pred_label',pred_label)

【注】C++的解析方法戳这里,python的解析方法戳这里

运行测试

在终端中执行:

python serving_test_grpc.py --host '127.0.0.1' --port '9500' --image './test_image/6.png'

这里面的host换成docker或者主机的ipport换成你上面开启的端口。

使用REST

gRPC区别很大,需要用json作为请求的输入格式,具体格式查阅这里,我们使用predict API中的格式:

{// (Optional) Serving signature to use.// If unspecifed default serving signature is used."signature_name": <string>,// Input Tensors in row ("instances") or columnar ("inputs") format.// A request can have either of them but NOT both."instances": <value>|<(nested)list>|<list-of-objects>"inputs": <value>|<(nested)list>|<object>
}

引入相关包:

import requests
import numpy as np
import cv2

读取数据:注意最后转换为(1,784)(1,784)(1,784)list

image_path='./test_image/2.png'
img = cv2.imread(image_path,cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img,(28,28))
_,img = cv2.threshold(img,250,255,cv2.THRESH_BINARY)
img = np.array(img,dtype='float32')
img = img.reshape((28*28))
img = img[np.newaxis,:]
img = img.tolist()

json格式化请求:此处一定要严格按照下面的语句书写,不然请求很容易失败

predict_request='{"signature_name": "predict_images", "instances":[{"images":%s}] }' %img

请求失败时,提示

requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: http://127.0.0.1:9501/v1/models/cnn_mnist:predict

如果提示这个bad request,不要问为什么,问就是你写错json请求了。

发送请求与接收回复以及解析

response = requests.post(SERVER_URL, data=predict_request)
response.raise_for_status()
prediction = response.json()['predictions'][0]
print('label:',np.argmax(prediction))

response.elapsed.total_seconds()可以返回时间,用于测试效率.

使用tensorflow_model_server启动服务

安装

按照官方文档走:

  • 第一步

    echo "deb [arch=amd64] http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal" | sudo tee /etc/apt/sources.list.d/tensorflow-serving.list && \
    curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | sudo apt-key add -
    
  • 第二步

    apt-get update && apt-get install tensorflow-model-server
    
  • 第三步

    apt-get upgrade tensorflow-model-server
    

这个是在线方法,还有一个离线方法,我就不写了,戳这里就行,听说离线编译方法的成功率有点低。以后有机会再试试。

启动服务

一条命令搞定

tensorflow_model_server --port=9500 --rest_api_port=9501 \--model_name=cnn_mnist --model_base_path=/tmp/cnn_mnist

就是直接开启gRPC端口为9500以及开启REST端口为9501,剩下的请求服务与上面的docker教程一模一样。

有些小问题总结

  • 端口占用

    有时候提示端口被占用

    如果使用docker的方法启动服务,可以使用docker ps看启动的服务占用的端口,如果有,就用docker kill CONTAINER_ID

    如果使用tensorflow_model_server启动服务,使用netstat -nap查找端口被谁占用,然后kill -9 PID

  • 重启docker容器
    当你killdocker里面的容器时,并非移除了该容器,可以通过docker ps -a查看所有容器,包括关闭容器,当你再次启动服务的时候,没必要去执行docker run .....的那个脚本,直接docker start CONTAINER_ID即可。

  • 官方有个比较好的例子,是调用resnet作为分类服务的
    模型下载方法:

    #https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz
    mkdir /tmp/resnet
    curl -s https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz | tar --strip-components=2 -C /tmp/resnet -xvz
    

网盘下载:
链接: https://pan.baidu.com/s/1Kyh8sGggdKld4u1wuQSAbA 提取码: 4k3z
服务启动方法:

docker run -p 8500:8500 -p 8501:8501 \
--mount type=bind,source=/tmp/resnet,target=/models/resnet \
-e MODEL_NAME=resnet -t tensorflow/serving
  • 检查模型的输入输出

    saved_model_cli show --dir /tmp/cnn_mnist/1/ --all
    

    输出:

    signature_def['predict_images']:The given SavedModel SignatureDef contains the following input(s):inputs['images'] tensor_info:dtype: DT_FLOATshape: (-1, 784)name: X:0The given SavedModel SignatureDef contains the following output(s):outputs['scores'] tensor_info:dtype: DT_FLOATshape: (-1, 10)name: dense_1/BiasAdd:0Method name is: tensorflow/serving/predict
    
  • 线上调用
    如果用其他ip或者电脑调用模型,请求的ip必须是host,而非localhost

后记

这里只是一个初步入门,后续会更进一步了解其他功能。

本文所有代码打包下载:

链接: https://pan.baidu.com/s/1MOUnU-sUAxfOjAHSPDKvkA 提取码: sa88

里面包含我调试的脚本,懒得剔除了,有兴趣慢慢看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246586.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tensorflow 指令加速

一直没注意过使用Tensorflow的时候有一条warning&#xff1a; Warning: your cpu supports instructions that this tensorflow binary was not compiled to use: avx2 fma这玩意是可以用来加速推断的&#xff0c;分CPU和GPU版&#xff0c;业务相关部署在CPU上&#xff0c;实测…

骨骼动画——论文与代码精读《Phase-Functioned Neural Networks for Character Control》

前言 最近一直玩CV&#xff0c;对之前学的动捕知识都忘得差不多了&#xff0c;最近要好好总结一下一直以来学习的内容&#xff0c;不能学了忘。对2017年的SIGGRAPH论文《Phase-Functioned Neural Networks for Character Control》进行一波深入剖析吧&#xff0c;结合源码。 额…

颜色协调模型Color Harmoniztion

前言 最近做换脸&#xff0c;在肤色调整的那一块&#xff0c;看到一个有意思的文章&#xff0c;复现一波玩玩。不过最后一步掉链子了&#xff0c;有兴趣的可以一起讨论把链子补上。 主要是github上大佬的那个复现代码和原文有点差异&#xff0c;而且代码复杂度过高&#xff0…

Openpose推断阶段原理

前言 之前出过一个关于openpose配置的博客&#xff0c;不过那个代码虽然写的很好&#xff0c;而且是官方的&#xff0c;但是分析起来很困难&#xff0c;然后再opencv相关博客中找到了比较清晰的实现&#xff0c;这里分析一波openpose的推断过程。 国际惯例&#xff0c;参考博…

换脸系列——眼鼻口替换

前言 想着整理一下换脸相关的技术方法&#xff0c;免得以后忘记了&#xff0c;最近脑袋越来越不好使了。应该会包含三个系列&#xff1a; 仅换眼口鼻&#xff1b;换整个面部&#xff1b;3D换脸 先看看2D换脸吧&#xff0c;网上已经有现成的教程了&#xff0c;这里拿过来整理一…

换脸系列——整脸替换

前言 前面介绍了仅替换五官的方法&#xff0c;这里介绍整张脸的方法。 国际惯例&#xff0c;参考博客&#xff1a; [图形算法]Delaunay三角剖分算法 维诺图&#xff08;Voronoi Diagram&#xff09;分析与实现 Delaunay Triangulation and Voronoi Diagram using OpenCV (…

3D人脸重建——PRNet网络输出的理解

前言 之前有款换脸软件不是叫ZAO么&#xff0c;分析了一下&#xff0c;它的实现原理绝对是3D人脸重建&#xff0c;而非deepfake方法&#xff0c;找了一篇3D重建的论文和源码看看。这里对源码中的部分函数做了自己的理解和改写。 国际惯例&#xff0c;参考博客&#xff1a; 什…

tensorflow官方posenet模型解析

前言 tensorflow官方有个姿态估计项目&#xff0c;这个输入和openpose还有点不一样&#xff0c;这里写个单人情况下的模型输出解析方案。 国际惯例&#xff0c;参考博客&#xff1a; 博客: 使用 TensorFlow.js 在浏览器端上实现实时人体姿势检测 tensorflow中posnet的IOS代…

tensorflow2安装时候的一个dll找不到的错误

电脑环境&#xff1a; vs2015python3.7.6&#xff0c;使用anaconda安装的CUDA 10.1cuDnn 7.6.5tensorflow2.1.0 错误内容 File "C:\Users\zb116\anaconda3\lib\imp.py", line 242, in load_modulereturn load_dynamic(name, filename, file)File "C:\Users\z…

PCA、SVD、ZCA白化理论与实现

简介 在UFLDL中介绍了主成分分析这一块的知识&#xff0c;而且当时学机器学习的时候&#xff0c;老师是将PCA和SVD联系起来将的&#xff0c;同时UFLDL也讲到了使用PCA做数据白化whitening处理&#xff0c;这个词经常在论文里面看到。 国际惯例&#xff0c;参考博客&#xff1…

OpenCV使用Tensorflow2-Keras模型

前言 最近工作上需要在C上快速集成Tensorflow/Keras训练好的模型&#xff0c;做算法验证。首先想到的就是opencv里面的dnn模块了&#xff0c;但是它需要的格式文件比较郁闷&#xff0c;是pb格式的模型&#xff0c;但是keras通常保存的是h5文件&#xff0c;查阅了很多资料&…

3D人脸表情驱动——基于eos库

前言 之前出过三篇换脸的博文&#xff0c;遇到一个问题是表情那一块不好处理&#xff0c;可行方法是直接基于2D人脸关键点做网格变形&#xff0c;强行将表情矫正到目标人脸&#xff0c;还有就是使用PRNet的思想&#xff0c;使用目标人脸的顶点模型配合源人脸的纹理&#xff0c…

3D姿态估计——ThreeDPose项目简单易用的模型解析

前言 之前写过tensorflow官方的posenet模型解析&#xff0c;用起来比较简单&#xff0c;但是缺点是只有2D关键点&#xff0c;本着易用性的原则&#xff0c;当然要再来个简单易用的3D姿态估计。偶然看见了ThreeDPose的项目&#xff0c;感觉很强大的&#xff0c;所以把模型扒下来…

简易的素描图片转换流程与实现

前言 之前经常在网上看到用PS实现真实图片到素描图片的转换&#xff0c;但是流程都大同小异&#xff0c;身为一只程序猿&#xff0c;必须来个一键转化额。 国际惯例&#xff0c;参考博客&#xff1a; Photoshop基础教程&#xff1a;混合模式原理篇 颜色减淡的原理讲解以及应…

一个简单好用的磨皮祛斑算法理论和python实现

前言 最近看了一个磨皮算法祛斑感觉效果不错&#xff0c;效果图看文末就行&#xff0c;个人觉得效果非常不错滴。 国际惯例&#xff0c;参考博客&#xff1a; 磨皮算法的源码&#xff1a;YUCIHighPassSkinSmoothing How To Smooth And Soften Skin With Photoshop 图像算法…

OpenVINO——配置与道路分割案例

前言 最近看到了一个深度学习库OpenVINO&#xff0c;专门用于Intel硬件上部署深度学习模型&#xff0c;其内置了非常非常多使用的预训练模型&#xff0c;比如道路分割、人脸提取、3D姿态估计等等。但是配置和调用有点小恶心&#xff0c;这里以道路分割为例&#xff0c;展示如何…

图像颜色迁移《color transfer between images》

前言 前段时间&#xff0c;在深度学习领域不是有个比较火的方向叫风格迁移的嘛&#xff0c;对于我这种不喜欢深度学习那种不稳定结果的人来说&#xff0c;还是想看看传统图像处理领域有什么类似的技术&#xff0c;发现了一个颜色迁移的算法&#xff0c;很久前的论文了。 国际…

ColorSpace颜色空间简介

前言 如果看过之前的介绍的图像颜色迁移《color transfer between images》和颜色协调模型Color Harmoniztion就会发现&#xff0c;大部分图像处理算法虽然输入输出是RGB像素值&#xff0c;但是中间进行算法处理时很少直接更改RGB值&#xff0c;而是转换到其它空间&#xff0c…

Ogre共享骨骼与两种骨骼驱动方法

前言 最近业务中用到Ogre做基于3D关键点虚拟角色骨骼驱动&#xff0c;但是遇到两个问题&#xff1a; 身体、头、眼睛、衣服等mesh的骨骼是分开的&#xff0c;但是骨骼结构都是一样的&#xff0c;需要设置共享骨骼驱动的时候可以直接修改骨骼旋转量&#xff0c;或者将旋转量存…

仿射变换和透视变换

前言 在前面做换脸的博客中提到了使用仿射变换和透视变换将两张不同的人脸基于关键点进行对齐&#xff0c;保证一张人脸贴到另一张人脸时&#xff0c;大小完全一致&#xff1b;所以有必要理解一下这两个概念的区别&#xff0c;由于以实用性为目的&#xff0c;所以所有的图像算…