DeepLabv3+代码实现+训练自己的数据集_如何deeplabv3+代码实现-程序员宅基地

技术标签: python  tensorflow  语义分割  深度学习  DeepLabv3+  

1.环境配置

(1)首先需要配置代码运行所需要的环境,本文使用的是从官网下载的代码,其源码是在TensorFlow运行的,故需要安装TensorFlow,我安装的是TensorFlow-gpu=15.0版本,所以之前需要先配置好相应的Cuda环境和Cudnn环境,这里使用的是Cuda10.0,我试过Cuda10.1,会出现错误,虽然可以通过修改实现代码正常运行,但最后还是安装与TensorFlow版本相对应的cuda版本。
TensorFlow版本
(2)安装之后运行代码可能会出现ImportError: No module named 'nets’的错误,这是因为TensorFlow15.0中不包含这个模块了,所以需要额外添加。
1)首先从git上下载slim源码,其包含在model中,下载
2)打开相应目录
在这里插入图片描述
然后运行setup.py文件,将slim中所有的模块加载。运行命令:
python setup.py build
python setup.py install
运行命令是可能会出现
error: could not create ‘build’:
当文件已存在时,无法创建该文件。
原因是git clone下来的代码库中有个BUILD文件,而build和install指令需要新建build文件夹,名字冲突导致问题。暂时不清楚BUILD文件的作用。将该文件移动到其他目录,再运行上述指令,即可成功安装。

2.制作数据集

(1)可以使用labelme进行数据集的制作,首先使用pip安装labelme,一般都可以成功安装,个别可能出现错误,但都很容易解决。标注过程如下:
在这里插入图片描述
点击保存后会出现一个同名的.json文件,里面包含了标注信息。由于源码只能通过json文件一个个的生产分割图像,太多麻烦,所以我修改了一下源码可以成批生产分割图像。

import argparse
import json
import os
import os.path as osp
import warnings
import copy
import shutil
import numpy as np
import PIL.Image
from skimage import io
import yaml

from labelme import utils

NAME_LABEL_MAP = {
    
    '_background_': 0,
    'Top': 1,
    'Rock': 2,
    'coal': 3,
}

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('json_file')
    parser.add_argument('-o', '--out', default=None)
    args = parser.parse_args()
    
    json_file = args.json_file
    out_dir = args.out
    if not os.path.exists(out_dir):
       os.mkdir(out_dir)
    print(out_dir)   
    list = os.listdir(json_file)
    for i in range(0, len(list)):
        path = os.path.join(json_file, list[i])       
        if (list[i].split(".")[-1])!="json":
            continue                                   
        filename = list[i][:-5]       # .json
        print(filename)      
        #label_name_to_value = {'_background_': 0}
        if os.path.isfile(path):

            data = json.load(open(path))
            img = utils.image.img_b64_to_arr(data['imageData'])
            lbl, lbl_names = utils.shape.labelme_shapes_to_label(img.shape, data['shapes'])  # labelme_shapes_to_label
            #lbl = utils.shapes_to_label(img.shape, data['shapes'], label_name_to_value)
            
            # modify labels according to NAME_LABEL_MAP
            lbl_tmp = copy.copy(lbl)
            for key_name in lbl_names:
                old_lbl_val = lbl_names[key_name]
                new_lbl_val = NAME_LABEL_MAP[key_name]
                lbl_tmp[lbl == old_lbl_val] = new_lbl_val
            lbl_names_tmp = {
    }
            for key_name in lbl_names:
                lbl_names_tmp[key_name] = NAME_LABEL_MAP[key_name]

            # Assign the new label to lbl and lbl_names dict
            lbl = np.array(lbl_tmp, dtype=np.int8)
            lbl_names = lbl_names_tmp
            captions = ['%d: %s' % (l, name) for l, name in enumerate(lbl_names)]
            lbl_viz = utils.draw.draw_label(lbl, img, captions)           
            utils.lblsave(osp.join(out_dir, '{}.png'.format(filename)), lbl)
            print('Saved to: %s' % out_dir)

if __name__ == '__main__':
    main()

其运行方式为python batch_json_to_dataset.py “json_path” -o=“out_path”
即可将文件夹中所以文件转化为分割图像;然后使用VOC数据集格式进行处理。获得最终数据集。
在这里插入图片描述
(2)将数据集分成train与val两部分,方便之后的测试;我的代码是按9:1来分的,具体比例根据实际情况来定。

#coding:utf-8
import os
import random

trainval_percent = 1  #训练验证数据集的百分比
train_percent = 0.9 		#训练集的百分比
filepath = './JPEGImages'
total_img = os.listdir(filepath)
num=len(total_img)  				#列表的长度
list=range(num)
tv=int(num*trainval_percent)  #训练验证集的图片个数
tr=int(tv*train_percent)  	  #训练集的图片个数	# sample(seq, n) 从序列seq中选择n个随机且独立的元素;
trainval= random.sample(list,tv)
train=random.sample(trainval,tr)
#创建文件trainval.txt,test.txt,train.txt,val.txt
ftrain = open('./ImageSets/Segmentation/train.txt', 'w')
fval = open('./ImageSets/Segmentation/val.txt', 'w')
for i  in list:
    name=total_img[i][:-4]+'\n'
    if i in train:
        ftrain.write(name)
    else:
        fval.write(name)
ftrain.close()
fval.close()

(3)数据集处理
首先使用代码中自带remove_gt_colormap.py文件,将数据集转化为单通道的标签数据,

'''Removes the color map from the ground truth segmentation annotations and save the results to output_dir.'''
import glob
import os.path
import numpy as np
from PIL import Image
import tensorflow as tf
FLAGS = tf.compat.v1.flags.FLAGS

#彩色分割图像目录
tf.compat.v1.flags.DEFINE_string('original_gt_folder',
                                 './SegmentationClass',
                                 'Original ground truth annotations.')
#分割图像保存格式
tf.compat.v1.flags.DEFINE_string('segmentation_format', 'png', 'Segmentation format.')
#输出单通道图像保存目录
tf.compat.v1.flags.DEFINE_string('output_dir',
                                 './SegmentationClassRaw',
                                 'folder to save modified ground truth annotations.')

#打开并转化图像数据排列方式
def _remove_colormap(filename):
  return np.array(Image.open(filename))

#保存单通道语义分割图像
def _save_annotation(annotation, filename):
  """Saves the annotation as png file.
  Args:
    annotation: Segmentation annotation.
    filename: Output filename.
  """
  pil_image = Image.fromarray(annotation.astype(dtype=np.uint8))
  with tf.io.gfile.GFile(filename, mode='w') as f:
    pil_image.save(f, 'PNG')


def main(unused_argv):
  #创建输出目录(如果不存在)
  if not tf.io.gfile.isdir(FLAGS.output_dir):
    tf.io.gfile.makedirs(FLAGS.output_dir)
  #获取所有彩色语义分割图像目录
  annotations = glob.glob(os.path.join(FLAGS.original_gt_folder,'*.' + FLAGS.segmentation_format))
  #遍历所有的彩色语义分割图像,将其转化为单通道图像
  for annotation in annotations:
    raw_annotation = _remove_colormap(annotation)
    filename = os.path.basename(annotation)[:-4]
    _save_annotation(raw_annotation,os.path.join(FLAGS.output_dir,filename + '.' + FLAGS.segmentation_format))

if __name__ == '__main__':
  tf.compat.v1.app.run()

然后使用build_voc2012_data.py文件将其转化为tfrcord格式的数据,运行时需要修改–image_format=“png”。
build_data.py

import collections
import six
#import tensorflow as tf
import tensorflow.compat.v1 as tf
FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_enum('image_format', 'jpg', ['jpg', 'jpeg', 'png'],
                         'Image format.')

tf.app.flags.DEFINE_enum('label_format', 'png', ['png'],
                         'Segmentation label format.')

# A map from image format to expected data format.
_IMAGE_FORMAT_MAP = {
    
    'jpg': 'jpeg',
    'jpeg': 'jpeg',
    'png': 'png',
}


class ImageReader(object):
  """Helper class that provides TensorFlow image coding utilities."""

  def __init__(self, image_format='jpeg', channels=3):
    """Class constructor.

    Args:
      image_format: Image format. Only 'jpeg', 'jpg', or 'png' are supported.
      channels: Image channels.
    """
    with tf.Graph().as_default():
      self._decode_data = tf.placeholder(dtype=tf.string)
      self._image_format = image_format
      self._session = tf.Session()
      if self._image_format in ('jpeg', 'jpg'):
        self._decode = tf.image.decode_jpeg(self._decode_data,
                                            channels=channels)
      elif self._image_format == 'png':
        self._decode = tf.image.decode_png(self._decode_data,
                                           channels=channels)

  def read_image_dims(self, image_data):
    """Reads the image dimensions.

    Args:
      image_data: string of image data.

    Returns:
      image_height and image_width.
    """
    image = self.decode_image(image_data)
    return image.shape[:2]

  def decode_image(self, image_data):
    """Decodes the image data string.

    Args:
      image_data: string of image data.

    Returns:
      Decoded image data.

    Raises:
      ValueError: Value of image channels not supported.
    """
    image = self._session.run(self._decode,
                              feed_dict={
    self._decode_data: image_data})
    if len(image.shape) != 3 or image.shape[2] not in (1, 3):
      raise ValueError('The image channels not supported.')

    return image


def _int64_list_feature(values):
  """Returns a TF-Feature of int64_list.

  Args:
    values: A scalar or list of values.

  Returns:
    A TF-Feature.
  """
  if not isinstance(values, collections.Iterable):
    values = [values]

  return tf.train.Feature(int64_list=tf.train.Int64List(value=values))


def _bytes_list_feature(values):
  """Returns a TF-Feature of bytes.

  Args:
    values: A string.

  Returns:
    A TF-Feature.
  """
  def norm2bytes(value):
    return value.encode() if isinstance(value, str) and six.PY3 else value

  return tf.train.Feature(
      bytes_list=tf.train.BytesList(value=[norm2bytes(values)]))


def image_seg_to_tfexample(image_data, filename, height, width, seg_data):
  """Converts one image/segmentation pair to tf example.

  Args:
    image_data: string of image data.
    filename: image filename.
    height: image height.
    width: image width.
    seg_data: string of semantic segmentation data.

  Returns:
    tf example of one image/segmentation pair.
  """
  return tf.train.Example(features=tf.train.Features(feature={
    
      'image/encoded': _bytes_list_feature(image_data),
      'image/filename': _bytes_list_feature(filename),
      'image/format': _bytes_list_feature(_IMAGE_FORMAT_MAP[FLAGS.image_format]),
      'image/height': _int64_list_feature(height),
      'image/width': _int64_list_feature(width),
      'image/channels': _int64_list_feature(3),
      'image/segmentation/class/encoded': (_bytes_list_feature(seg_data)),
      'image/segmentation/class/format': _bytes_list_feature(FLAGS.label_format),
  }))

build_voc2012_data.py

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import math
import os.path
import sys
import build_data
from six.moves import range
#import tensorflow as tf
import tensorflow.compat.v1 as tf
FLAGS = tf.app.flags.FLAGS
#原始图像目录
tf.app.flags.DEFINE_string('image_folder',
                           './MYFJ_data_v3/JPEGImages',
                           'Folder containing images.')
#单通道语义分割图像目录
tf.app.flags.DEFINE_string(
    'semantic_segmentation_folder',
    './MYFJ_data_v3/SegmentationClassRaw',
    'Folder containing semantic segmentation annotations.')
#训练和验证集的文件
tf.app.flags.DEFINE_string(
    'list_folder',
    './MYFJ_data_v3/ImageSets/Segmentation',
    'Folder containing lists for training and validation')
#输出tfrecord保存目录
tf.app.flags.DEFINE_string(
    'output_dir',
    './MYFJ_data_v3/tfrecord',
    'Path to save converted SSTable of TensorFlow examples.')

#图像分成几份
_NUM_SHARDS = 2


def _convert_dataset(dataset_split):
  """Converts the specified dataset split to TFRecord format.

  Args:
    dataset_split: The dataset split (e.g., train, test).

  Raises:
    RuntimeError: If loaded image and label have different shape.
  """
  # 返回路径dataset_splits所对应的文件名,并且去除扩展名.txt
  dataset = os.path.basename(dataset_split)[:-4]
  sys.stdout.write('Processing ' + dataset)

  # 打开索引文件,并生成索引文件名列表
  filenames = [x.strip('\n') for x in open(dataset_split, 'r')]
  num_images = len(filenames)# 数据集中图像的数量即索引列表的长度
  num_per_shard = int(math.ceil(num_images / _NUM_SHARDS))# 将图像均匀分成_NUM_SHARDS份,输出_NUM_SHARDS个tfrecord文件

  
  image_reader = build_data.ImageReader('jpeg', channels=3)
  label_reader = build_data.ImageReader('png', channels=1)
  
  for shard_id in range(_NUM_SHARDS):
    output_filename = os.path.join(FLAGS.output_dir,
        '%s-%05d-of-%05d.tfrecord' % (dataset, shard_id, _NUM_SHARDS))# _NUM_SHARDS个输出的tfrecord文件的输出目录

    with tf.python_io.TFRecordWriter(output_filename) as tfrecord_writer:# 向output_filename文件夹写入tfrecord文件
      start_idx = shard_id * num_per_shard# 每个tfrecord文件存储的起始图像索引
      end_idx = min((shard_id + 1) * num_per_shard, num_images)# 每个tfrecord文件存储的结束图像索引;对于最后一个tfrecord文件,可能仅保存剩余的图像。
      for i in range(start_idx, end_idx):
        sys.stdout.write('\r>> Converting image %d/%d shard %d' % (i + 1, len(filenames), shard_id))
        sys.stdout.flush()
        # Read the image.
        image_filename = os.path.join(FLAGS.image_folder, filenames[i] + '.' + FLAGS.image_format)# 图像路径
        image_data = tf.gfile.GFile(image_filename, 'rb').read()#GFile函数读取图像
        height, width = image_reader.read_image_dims(image_data)#获取图像的高宽
        
        # Read the semantic segmentation annotation.
        seg_filename = os.path.join(FLAGS.semantic_segmentation_folder,filenames[i] + '.' + FLAGS.label_format)# 标签图像路径
        seg_data = tf.gfile.GFile(seg_filename, 'rb').read()# 读取标签
        seg_height, seg_width = label_reader.read_image_dims(seg_data)
        if height != seg_height or width != seg_width:
          raise RuntimeError('Shape mismatched between image and label.')
        # Convert to tf example.# 保存为tfrecord的example格式
        example = build_data.image_seg_to_tfexample(image_data, filenames[i], height, width, seg_data)
        tfrecord_writer.write(example.SerializeToString())# 用字符串形式存储example信息
    sys.stdout.write('\n')
    sys.stdout.flush()

def main(unused_argv):
  # 查找匹配该路径名的文件(即训练集和验证集图像索引文件)
  dataset_splits = tf.gfile.Glob(os.path.join(FLAGS.list_folder, '*.txt'))
  #遍历训练集和验证集,依次转化为tfrecord格式文件
  for dataset_split in dataset_splits:
    _convert_dataset(dataset_split)

if __name__ == '__main__':
  tf.app.run()

3.训练模型

(1)下载代码deeplab,同时下载预训练模型model
(2)修改代码
1)首先修改data_generator.py 文件,将自己的数据集加入到里面

_MYFJ = DatasetDescriptor(
   splits_to_sizes={
    
        #'train': 649,  # num of samples in images/training
        #'val': 73,  # num of samples in images/validation
        'train': 186,  # num of samples in images/training
        'val': 21,  # num of samples in images/validation
    },
    num_classes=4,
    ignore_label=255,
)
_DATASETS_INFORMATION = {
    
    'cityscapes': _CITYSCAPES_INFORMATION,
    'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
    'ade20k': _ADE20K_INFORMATION,
    'myfj':_MYFJ,
}

2)不需要修改train_utils.py,在使用预训练权重时候,不加载该logit层,所以有人认为应该如下修改:

  #exclude_list = ['global_step']
  exclude_list = ['global_step','logits']
  if not initialize_last_layer:
    exclude_list.extend(last_layers)

但是事实上不需要,因为

if not initialize_last_layer:
    exclude_list.extend(last_layers)

这句会将你设置的不用加载的层都排除在外,使其加入到exclude_list中;所以不用多此一举。

3)修改train .py

如果想在其他数据集上微调DeepLab时,有以下几种情况:
(a)想要使用预训练模型的所有权重:设置initialize_last_layer = True(在这种情况下,last_layers_contain_logits_only无关紧要)。
(b)只想使用网络主干网权重(即排除ASPP,解码器等):设置initialize_last_layer = False和last_layers_contain_logits_only = False(c)使用除logit层之外的所有训练好的权重(因为num_classes可能不同):设置initialize_last_layer = False和last_layers_contain_logits_only = True。
最终,我的设置是:
initialize_last_layer=False  
last_layers_contain_logits_only=True

(3)运行train代码

python train.py \
  --logtostderr \
  --train_split="train" \  可以选择train/val/trainval 不同的数据集 
  --model_variant="xception_65" \
  --atrous_rates=6 \
  --atrous_rates=12 \
  --atrous_rates=18 \
  --output_stride=16 \
  --decoder_output_stride=4 \
  --train_crop_size=513 \
  --train_crop_size=513\
  --train_batch_size=8\
  --training_number_of_steps=10000 \
  --fine_tune_batch_norm=False \(由于batchsize小于12,将其改为false)
  --tf_initial_checkpoint="加载与训练模型/model.ckpt" \
  --train_logdir="保存训练的中间结果" \
  --dataset_dir="生成的tfrecord的路径"

(4)运行eval.py,注意eval_crop_size大小与输入的图像相同

python eval.py \
  --logtostderr \
  --eval_split="val" \
  --model_variant="xception_65" \
  --atrous_rates=6 \
  --atrous_rates=12 \
  --atrous_rates=18 \
  --output_stride=16 \
  --decoder_output_stride=4 \
  --eval_crop_size=513 \
  --eval_crop_size=513 \
  --checkpoint_dir="${TRAIN_LOGDIR}" \
  --eval_logdir="${EVAL_LOGDIR}" \
  --dataset_dir="${DATASET}" 

(5)运行vis.py,注意vis_crop_size大小与输入的图像相同

python vis.py \
  --logtostderr \
  --vis_split="val" \
  --model_variant="xception_65" \
  --atrous_rates=6 \
  --atrous_rates=12 \
  --atrous_rates=18 \
  --output_stride=16 \
  --decoder_output_stride=4 \
  --vis_crop_size="513,513" \
  --checkpoint_dir="${TRAIN_LOGDIR}" \
  --vis_logdir="${VIS_LOGDIR}" \
  --dataset_dir="${PASCAL_DATASET}" \
  --max_number_of_iterations=1

(6)运行export_model.py,生成frozen_inference_graph.pb文件,还是注意crop_size大小与输入图像相同

CKPT_PATH="${TRAIN_LOGDIR}/model.ckpt-${NUM_ITERATIONS}"
EXPORT_PATH="${EXPORT_DIR}/frozen_inference_graph.pb"
python export_model.py \
  --logtostderr \
  --checkpoint_path="${CKPT_PATH}" \
  --export_path="${EXPORT_PATH}" \
  --model_variant="xception_65" \
  --atrous_rates=6 \
  --atrous_rates=12 \
  --atrous_rates=18 \
  --output_stride=16 \
  --decoder_output_stride=4 \
  --num_classes=21 \
  --crop_size=513 \
  --crop_size=513 \
  --inference_scales=1.0

(7)运行deeplab_demo.py
测试图

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37799466/article/details/104016347

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签