【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据

前言

之前使用tensorflowkeras的时候,都各自有一套数据读取方法,但是遇到一个问题就是,在训练的时候,GPU的利用率忽高忽低,极大可能是由于训练过程中读取每个batch数据造成的,所以又看了tensorflow官方的加载数据方法。主要是利用了tf.data.Dataset这里面的一系列操作。

国际惯例,参考博客:

tensorflow官方加载数据集方法

官方文档对应的代码images.ipynb

官方文档对应的代码tf_records.ipynb

Tensorflow中创建自己的TFRecord格式数据集

TensorFlow全新的数据读取方式:Dataset API入门教程

以tf.data优化训练数据 Google开发者大会2018

Tensorflow数据预处理之tf.data.TFRecordDataset—TFRecords详解\TFRecords图像预处理

buffer_size的含义——Dataset.map , Dataset.prefetch and Dataset.shuffle

tensorflow 数据读取总结—(直接供给数据(feeding) 从文件中以管线形式读取数据 预加载数据)

复习

先复习一下之前博客中tensorflowkeras加载数据的方法

之前采用的tensorflow加载数据方法

详细查看之前的这篇博客:

IMG_HEIGHT = 28 # 高
IMG_WIDTH = 28 # 宽
CHANNELS = 3 # 通道数
def read_images(dataset_path, batch_size):imagepaths, labels = list(), list()data = open(dataset_path, 'r').read().splitlines()for d in data:imagepaths.append(d.split(' ')[0])labels.append(int(d.split(' ')[1]))   # 转换为张量imagepaths = tf.convert_to_tensor(imagepaths, dtype=tf.string)labels = tf.convert_to_tensor(labels, dtype=tf.int32)# 建立TF队列,打乱数据image, label = tf.train.slice_input_producer([imagepaths, labels],shuffle=True)# 读取数据image = tf.read_file(image)image = tf.image.decode_jpeg(image, channels=CHANNELS)# 将图像resize成规定大小image = tf.image.resize_images(image, [IMG_HEIGHT, IMG_WIDTH])# 手动归一化image = image * 1.0/127.5 - 1.0# 创建batchinputX, inputY = tf.train.batch([image, label], batch_size=batch_size,capacity=batch_size * 8,num_threads=4)return inputX, inputY

主要使用tf.train中的一系列操作

keras中自带的数据加载方法

直接看官方文档即可,我比较喜欢用下面这一系列方法从文件夹中读取数据:

train_datagen = ImageDataGenerator(rescale=1./255,shear_range=0.2,zoom_range=0.2,horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory('data/train',target_size=(150, 150),batch_size=32,class_mode='binary')validation_generator = test_datagen.flow_from_directory('data/validation',target_size=(150, 150),batch_size=32,class_mode='binary')

只要使用flow_from_directory自动从文件夹中读取数据。

利用tf.data直接读取数据

数据集准备

下列所有实验的数据都基于tensorflow提供的flower_photos数据集,才220M左右,下载地址戳这里。

我也上传到网盘了:

链接:https://pan.baidu.com/s/13esPlx-fkKlXaegJNROPyw
提取码:nv64

解压后,得到五个文件夹,每个文件夹一类花朵。

代码

首先引入必要的包:

import os 
import tensorflow as tf
import pathlib
import random
import numpy as np

读取图片数据

  • 首先找到所有图片和对应的路径:

    data_root = pathlib.Path('./dataset/flower_photos/')
    all_image_paths = list(data_root.glob('*/*'))
    all_image_paths = [str(path) for path in all_image_paths]
    random.shuffle(all_image_paths)
    image_count = len(all_image_paths)
    print('total image num:',image_count)#total image num: 3670
    
  • 数据预处理:

    class Process_img:def __init__(self,img_size):self.img_size = img_sizedef load_and_preprocess_image(self,img_path):image = tf.read_file(img_path)image = tf.image.decode_jpeg(image,channels=3)#进行各种图像处理:裁剪、缩放、旋转、亮度调整等image=tf.image.resize_images(image,self.img_size) #此处严格按照API文档调用,tensorflow 版本不同使用的方法不同image /= 255.0return image
    
  • 数据预处理必须使用如下流程,先from_tensor_slice转换成Dataset格式,然后使用map将数据丢到预处理函数中:

    path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)a=Process_img(img_size=[192,192])
    image_ds = path_ds.map(a.load_and_preprocess_image,num_parallel_calls=tf.contrib.data.AUTOTUNE)
    

    这里有一个小技巧就是:本人不清楚map函数如何接受预处理函数所需传递的方法,有一个方法就是将预处理参数,比如image_size也丢到Dataset里面去,但是有点麻烦,这样做from_tensor_slice里面的参数有点长,还不如初始化一个对象,存储预处理所需参数了,清晰易懂。

读取标签数据

  • 先获取标签,因为路径中文件名的上级文件夹就是标签,所以可以:

    label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
    print('label names:',label_names)
    #label names: ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
    
  • 再将标签转换为int型索引:

    #将标签转换为索引值
    label_to_index = dict((name,index) for index,name in enumerate(label_names))
    print('label corresponding index:',label_to_index)
    
  • 然后获取到所有图像对应的标签:

    all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]
    

    验证一下看看:

    print('first 10 sample path and labels:')
    for i in range(0,10):print('{0}\t {1}'.format(all_image_paths[i],all_image_labels[i]))
    '''
    first 10 sample path and labels:
    dataset\flower_photos\roses\5799616059_0ffda02e54.jpg	 2
    dataset\flower_photos\roses\22385375599_1faf334f5d_n.jpg	 2
    dataset\flower_photos\sunflowers\6627521877_6e43fb3c49_m.jpg	 3
    dataset\flower_photos\dandelion\3465599902_14729e2b1b_n.jpg	 1
    dataset\flower_photos\roses\4267024012_295e7141a3_n.jpg	 2
    dataset\flower_photos\dandelion\23414449869_ee849a80d4.jpg	 1
    dataset\flower_photos\tulips\4418204816_018375acd0_m.jpg	 4
    dataset\flower_photos\dandelion\9517326597_5d116a0166.jpg	 1
    dataset\flower_photos\dandelion\7197581386_8a51f1bb12_n.jpg	 1
    dataset\flower_photos\dandelion\425800274_27dba84fac_n.jpg	 1
    '''
    
  • 同样将标签也转换成Dataset格式:

    label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels,tf.int64))
    

组合数据

因为后续需要打乱和分批,所以需要将图像与标签对应打包对应好,后面一起变换

img_label_ds = tf.data.Dataset.zip((image_ds,label_ds))

打乱、重复数据、分批,详细解释可以查看这里,我们只看如何使用:

batch_size = 32
ds = ds.cache()
ds=img_label_ds.shuffle(buffer_size=image_count)
ds=ds.repeat()
ds=ds.batch(batch_size)
ds = ds.prefetch(buffer_size=tf.contrib.data.AUTOTUNE)

【注】不加cache也行,但是官方文档说加了能提高数据喂进内存的性能。prefetch在官方文档中说的是在训练时将数据喂到batch里面。

训练

使用tf.keras里面的mobileNetV2模型微调

预处理

因为mobileNetV2要求输入数据范围在(−1,−1)(-1,-1)(1,1),所以我们还要做一次预处理:

# 把数据由(0,1)转换为(-1,1)
def change_range(image,label):return 2*image-1,label
keras_ds=ds.map(change_range)

【注】可以发现,数据变成Dataset格式以后,各种预处理都得用map映射到处理函数。

载入模型并训练

去掉mobileNet的尾巴,是否使用imagenet的权重与训练,取决于weights是否None

mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192,192,3),include_top=False,weights=None)

接个全连接做分类:

model = tf.keras.Sequential([mobile_net,tf.keras.layers.GlobalAveragePooling2D(),tf.keras.layers.Dense(5,activation='softmax')
])

看看网络结构

model.summary()
'''
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_192 (Model) (None, 6, 6, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 5)                 6405      
=================================================================
Total params: 2,264,389
Trainable params: 2,230,277
Non-trainable params: 34,112
_________________________________________________________________
'''

编译模型:

model.compile(optimizer=tf.train.AdamOptimizer(),loss=tf.keras.losses.sparse_categorical_crossentropy,metrics=["accuracy"])

开始训练

steps_per_epoch = int(np.ceil(len(all_image_paths)/batch_size))
model.fit(keras_ds,epochs=1000,steps_per_epoch=steps_per_epoch)
'''
Epoch 1/1000
115/115 [==============================] - 172s 1s/step - loss: 1.3721 - acc: 0.4427
Epoch 2/1000
115/115 [==============================] - 155s 1s/step - loss: 1.1061 - acc: 0.5582
Epoch 3/1000
115/115 [==============================] - 150s 1s/step - loss: 0.9562 - acc: 0.6190
Epoch 4/1000
115/115 [==============================] - 148s 1s/step - loss: 0.8750 - acc: 0.6617
Epoch 5/1000
115/115 [==============================] - 223s 2s/step - loss: 0.8136 - acc: 0.6927
Epoch 6/1000
115/115 [==============================] - 148s 1s/step - loss: 0.7368 - acc: 0.7201
Epoch 7/1000
115/115 [==============================] - 148s 1s/step - loss: 0.6718 - acc: 0.7582
Epoch 8/1000
115/115 [==============================] - 148s 1s/step - loss: 0.6206 - acc: 0.7682
Epoch 9/1000
115/115 [==============================] - 148s 1s/step - loss: 0.5699 - acc: 0.7905
Epoch 10/1000
115/115 [==============================] - 147s 1s/step - loss: 0.5368 - acc: 0.8041
Epoch 11/1000
115/115 [==============================] - 147s 1s/step - loss: 0.4938 - acc: 0.8190
Epoch 12/1000
115/115 [==============================] - 148s 1s/step - loss: 0.4456 - acc: 0.8372
Epoch 13/1000
115/115 [==============================] - 147s 1s/step - loss: 0.4257 - acc: 0.8429
Epoch 14/1000
115/115 [==============================] - 149s 1s/step - loss: 0.3856 - acc: 0.8573
.......
'''

利用tf.data转成tfrecord再载入

比较喜欢的方法就是跟caffe一样,先做数据集,训练的时候读取,tensorflow中建议的存储格式就是tfrecord

制作数据集

导入对应包:

import os 
import tensorflow as tf
import pathlib
import random
import numpy as np

获取图像数据路径:

data_root = pathlib.Path('./dataset/flower_photos/')
all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
random.shuffle(all_image_paths)
image_count = len(all_image_paths)
print('total image num:',image_count)

获取图像对应标签:

label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
label_to_index = dict((name,index) for index,name in enumerate(label_names))
all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]

把图像与标签打包:

image_labels = zip(all_image_paths,all_image_labels)

按照tensorflow的方法将图像和标签做成tfrecord格式数据集:

def _bytes_feature(value):"""Returns a bytes_list from a string / byte."""return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))def _float_feature(value):"""Returns a float_list from a float / double."""return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))def _int64_feature(value):"""Returns an int64_list from a bool / enum / int / uint."""return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
def image_example(image_string, label):feature = {'label': _int64_feature(label),'image_raw': _bytes_feature(image_string)}return tf.train.Example(features=tf.train.Features(feature=feature))
with tf.python_io.TFRecordWriter('images.tfrecords') as writer:for filename, label in image_labels:image_string = open(filename, 'rb').read()tf_example = image_example(image_string, label)writer.write(tf_example.SerializeToString())

上面需要注意的就是image_example里面的feature里面存的内容,你可以自己定义一些其它的,比如图像宽高之类的,后续读取的时候可以通过键值获取对应值,这里只存了必须的图像字节和标签。其余的函数干啥的,别问,用之就对了。

读取数据

读取必要包:

import os 
import tensorflow as tf
import pathlib
import random
import numpy as np

读取tfrecord对应的数据:

raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')# Create a dictionary describing the features.
image_feature_description = {'label': tf.FixedLenFeature([], tf.int64),'image_raw': tf.FixedLenFeature([], tf.string),
}def _parse_image_function(example_proto):# Parse the input tf.Example proto using the dictionary above.example = tf.parse_single_example(example_proto, image_feature_description)image = tf.image.resize_images(tf.image.decode_jpeg(example['image_raw'],channels=3),[192,192])image/=255.0label = example['label']return image,labelparsed_image_dataset = raw_image_dataset.map(_parse_image_function)
parsed_image_dataset

流程基本就是使用tf.data.TFRecordDataset载入tfrecord数据,然后取对应存储的信息,如图像与标签。还可以来一波预处理,当然还是利用map将数据丢到预处理函数中。

接着就是打乱、分批、重复

train_data = parsed_image_dataset.shuffle(buffer_size=100)
train_data = train_data.batch(8)
train_data = train_data.repeat()
train_data = train_data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE)
print(train_data)

关于buffer_size的说明,戳这里

训练

跟前面没啥区别:

mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192,192,3),include_top=False,weights=None)model = tf.keras.Sequential([mobile_net,tf.keras.layers.GlobalAveragePooling2D(),tf.keras.layers.Dense(5,activation='softmax')
])model.compile(optimizer=tf.train.AdamOptimizer(),loss=tf.keras.losses.sparse_categorical_crossentropy,metrics=["accuracy"])model.fit(train_data,epochs=1000,steps_per_epoch=1000)
'''
Epoch 1/1000
1000/1000 [==============================] - 356s 356ms/step - loss: 1.2820 - acc: 0.4885
Epoch 2/1000
1000/1000 [==============================] - 358s 358ms/step - loss: 1.0426 - acc: 0.5928
Epoch 3/1000
1000/1000 [==============================] - 355s 355ms/step - loss: 0.9630 - acc: 0.6330
Epoch 4/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.9110 - acc: 0.6524
Epoch 5/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.8589 - acc: 0.6771
Epoch 6/1000
1000/1000 [==============================] - 355s 355ms/step - loss: 0.7635 - acc: 0.7152
Epoch 7/1000
1000/1000 [==============================] - 355s 355ms/step - loss: 0.6983 - acc: 0.7406
Epoch 8/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.6482 - acc: 0.7632
Epoch 9/1000
1000/1000 [==============================] - 354s 354ms/step - loss: 0.5834 - acc: 0.7769
Epoch 10/1000
1000/1000 [==============================] - 357s 357ms/step - loss: 0.5439 - acc: 0.7995
'''

训练结果和上面直接从文件夹读取的结果差不多,说明流程没问题。

有个坑

读取tfrecord数据集的这句话:

raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')

丫的竟然不核对这个tfrecords文件是否存在,或者是否为空数据,不信你随便改个名,这句话还能运行,真的是醉了。程序model.fit会直接进入死机状态,你也不知道它是在读数据,还是崩了。

所以我们在进行下列一顿操作以后:

raw_image_dataset = tf.data.TFRecordDataset('images11.tfrecords')# Create a dictionary describing the features.
image_feature_description = {'label': tf.FixedLenFeature([], tf.int64),'image_raw': tf.FixedLenFeature([], tf.string),
}def _parse_image_function(example_proto):# Parse the input tf.Example proto using the dictionary above.example = tf.parse_single_example(example_proto, image_feature_description)image = tf.image.resize_images(tf.image.decode_jpeg(example['image_raw'],channels=3),[192,192])image/=255.0label = example['label']return image,labelparsed_image_dataset = raw_image_dataset.map(_parse_image_function)
parsed_image_datasettrain_data = parsed_image_dataset.shuffle(buffer_size=100)
train_data = train_data.batch(8)
train_data = train_data.repeat()
train_data = train_data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE)
print(train_data)

必须得验证一下这个train_data里面是不是有数据,图片与标签是否对应。

验证方法,是迭代输出

iterator = train_data.make_one_shot_iterator()
one_element = iterator.get_next()
with tf.Session() as sess:try:while True:a=sess.run(one_element)print(a[0].shape)#(8, 192, 192, 3)print(a[1].shape)#(8,)breakexcept tf.errors.OutOfRangeError:print('end!')

我们把数据保存在a里面,同时从ashape可以看出来,存了图片和标签,而且存储的是一个batch_size大小的数据。接下来显示一下:

label_name=['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
print((a[0][0]).shape)
show_idx = 0
plt.imshow(a[0][show_idx])
plt.title(label_name[a[1][show_idx]])

在这里插入图片描述

有图片输出就说明没问题了。

后记

可以发现这一系列的数据读取操作是可以封装在一起的,这里先将实验验证用的ipynb放出来:

  • 直接使用tf.data遍历文件夹训练:
    链接:https://pan.baidu.com/s/1YSWLVfmfU2brnLI0uRyljg
    提取码:n2ht
  • 制作tfrecord数据集:
    链接:https://pan.baidu.com/s/1HGH66klAl5zECEznRPhV7g
    提取码:w6ss
  • 读取tfrecord数据集并训练:
    链接:https://pan.baidu.com/s/1Jyyu2u96xLkomJhKT-AgIA
    提取码:mnkj

为了方便后续使用,直接写一个现成的Python脚本,以后直接传入路径,输出可以直接训练的数据参数。

  • 直接使用tf.data遍历文件夹训练:
    链接:https://pan.baidu.com/s/1yb0EoBXzhyQEA-BO3i1Fcg
    提取码:2706

  • 制作tfrecord数据集:

    链接:https://pan.baidu.com/s/1Jw2LDKGeTrMKaItDHqe3dA
    提取码:znfy

  • 读取tfrecord数据集训练:链接:https://pan.baidu.com/s/1rRKx9tP8jrAZhzXjIZNNTQ
    提取码:sa2t

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

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

相关文章

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

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

颜色协调模型Color Harmoniztion

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

Openpose推断阶段原理

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

换脸系列——眼鼻口替换

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

换脸系列——整脸替换

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

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

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

tensorflow官方posenet模型解析

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

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

电脑环境: vs2015python3.7.6,使用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中介绍了主成分分析这一块的知识,而且当时学机器学习的时候,老师是将PCA和SVD联系起来将的,同时UFLDL也讲到了使用PCA做数据白化whitening处理,这个词经常在论文里面看到。 国际惯例,参考博客&#xff1…

OpenCV使用Tensorflow2-Keras模型

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

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

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

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

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

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

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

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

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

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

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

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

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

ColorSpace颜色空间简介

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

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

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

仿射变换和透视变换

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

obj格式解析

前言 最近处理一些网格渲染的时候,需要解析Obj文件,从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例,参考博客: 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中OBJ文…