2018-09-06 17:26:18 sy20173081277 阅读数 4657

      首先,基于图像的识别,分类等操作,具有无接触、高精度的特点,尤其在活体检测、识别、分类等过程中这种无接触的方式适用性极强。

      然而到目前为止,在图像识别领域的每一个重大的突破无一不都是用到了卷积神经网络。卷积神经网络CNN以及众多以此衍生出来的网络模型,可以直接将图像数据作为输入,不仅无需人工对图像进行预处理和额外的特征抽取等复杂操作,而且以其特有的细粒度特征提取方式,使得对图像的处理达到了几近人力的水平。这可能也是CNN为何这么火的原因吧。

      无法预知以后会不会有新的网络模型可以取而代之。

 

2019-09-25 15:33:49 weixin_43991178 阅读数 83

针孔相机

SLAM中采用的相机模型大多基于针孔相机模型,在说明针孔相机模型的投影关系之前,先明确需要用到的几个坐标系下用一个空间点的不同坐标表示:

-世界坐标系ww:空间点在世界坐标系下的坐标表示为Pw=[XYZ]T\bm{P}^w=\left[\begin{array}{ccc}X & Y & Z \end{array}\right]^T

  • 相机坐标系cc:空间点在相机坐标系下的坐标表示为pc=[xyz]T\bm{p}^c=\left[\begin{array}{ccc}x & y & z \end{array}\right]^T
  • 像素坐标系pp:空间点在像素坐标系下的坐标表示为up=[uv]T\bm{u}^p=\left[\begin{array}{cc}u & v \end{array}\right]^T

空间点在世界坐标系和相机坐标系下的关系由外参矩阵T\bm{T}决定,注意由于T\bm{T}是4维矩阵,这里所有坐标需采用齐次表示:
p=TP \bm{p}=\bm{T}\bm{P}
记空间点在成像平面上的坐标为KaTeX parse error: Double superscript at position 9: \bm{p}^c'̲=\left[\begin{a…,则有:
{x=fzxy=fzy \left\{\begin{aligned} x'&=\frac{f}{z}x\\ y'&=\frac{f}{z}y \end{aligned} \right.
由于像素坐标系和成像坐标系原点不一致(成像坐标系原点位于光心延长线,像素坐标系原点位于图像左上角),记两者之间的坐标平移量为[cxcy]\left[\begin{array}{cc}c_x & c_y\end{array}\right];同时,成像坐标系单位为mm,像素坐标系单位为像素pixelpixel,记两者之间的转换比例为[αβ]\left[\begin{array}{cc}\alpha & \beta\end{array}\right],则像素坐标和相机坐标之间的关系可以表示为:
{u=αfzx+cxv=βfzy+cy \left\{\begin{aligned} u&=\alpha\frac{f}{z}x+c_x\\ v&=\beta\frac{f}{z}y+c_y \end{aligned} \right.
αf=fx\alpha f=f_xβf=fy\beta f=f_y,同时将上式写成齐次矩阵形式为:
1z[uv1]=[fx0cx0fycy001]K[xyz] \frac{1}{z}\left[\begin{array}{c}u\\v\\1\end{array}\right]=\underbrace{\left[\begin{matrix} f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0 & 0 & 1 \end{matrix}\right]}_{\bm{K}}\left[\begin{array}{c}x\\y\\z\end{array}\right]
矩阵K\bm{K}称为相机的内参矩阵。

畸变模型

径向畸变模型

上式的空间点投影实在理想状态下给出的,实际情况下的成像存在由各种原因引起的畸变,需要我们对其进行进一步矫正。所有畸变中,最常见且影响最大的为径向畸变和切向畸变。径向畸变采用多项式模型描述,通常我们只取其前两项,即:
{xdistorted=x(1+k1r2+k2r4)ydistorted=y(1+k1r2+k2r4)r2=x2+y2 \left\{\begin{aligned} x_{distorted}&=x\left(1+k_1r^2+k_2r^4\right)\\ y_{distorted}&=y\left(1+k_1r^2+k_2r^4\right)\\ r^2&=x^2+y^2 \end{aligned} \right.

切向畸变模型

相比于径向畸变模型,切向畸变模型则更为复杂:
{xdistorted=x+[2p1xy+p2(r2+2x2)]ydistorted=y+[p1(r2+2y2)+2p2xy]r2=x2+y2 \left\{\begin{aligned} x_{distorted}&=x+\left[2p_1xy+p_2\left(r^2+2x^2\right)\right]\\ y_{distorted}&=y+\left[p_1\left(r^2+2y^2\right)+2p_2xy\right]\\ r^2&=x^2+y^2 \end{aligned} \right.

联合畸变模型

综上,我们可以得到同时存在径向和切向畸变的相机模型为:
{xdistorted=x(1+k1r2+k2r4)+[2p1xy+p2(r2+2x2)]ydistorted=y(1+k1r2+k2r4)+[p1(r2+2y2)+2p2xy]r2=x2+y2 \left\{\begin{aligned} x_{distorted}&=x\left(1+k_1r^2+k_2r^4\right)+\left[2p_1xy+p_2\left(r^2+2x^2\right)\right]\\ y_{distorted}&=y\left(1+k_1r^2+k_2r^4\right)+\left[p_1\left(r^2+2y^2\right)+2p_2xy\right]\\ r^2&=x^2+y^2 \end{aligned} \right.

畸变矫正方法

在相机各项畸变参数已知的情况下,由于r2r^2的存在,想要直接求解上式也是困难的,通常我们采用迭代方式求解,另畸变方程左边等于零有
{x(1+k1r2+k2r4)=xdistorted[2p1xy+p2(r2+2x2)]y(1+k1r2+k2r4)=ydistorted[p1(r2+2y2)+2p2xy]{x=xdistorted[2p1xy+p2(r2+2x2)](1+k1r2+k2r4)y=ydistorted[p1(r2+2y2)+2p2xy](1+k1r2+k2r4) \begin{aligned} &\left\{\begin{aligned} &x\left(1+k_1r^2+k_2r^4\right)=x_{distorted}-\left[2p_1xy+p_2\left(r^2+2x^2\right)\right]\\ &y\left(1+k_1r^2+k_2r^4\right)=y_{distorted}-\left[p_1\left(r^2+2y^2\right)+2p_2xy\right]\\ \end{aligned}\right.\\ &\Rightarrow\left\{\begin{aligned} x&=\frac{x_{distorted}-\left[2p_1xy+p_2\left(r^2+2x^2\right)\right]}{\left(1+k_1r^2+k_2r^4\right)}\\ y&=\frac{y_{distorted}-\left[p_1\left(r^2+2y^2\right)+2p_2xy\right]}{\left(1+k_1r^2+k_2r^4\right)}\\ \end{aligned}\right.\\ \end{aligned}
注意公式中的xdistortedx_{distorted}ydistortedy_{distorted}为迭代初值,即带畸变的点坐标;xxyy为当前迭代的去畸变点坐标。使用上式不断迭代,直至两步之间的迭代误差小于一定阈值即可,一个简单的demo如下:

/*
 * @Description: 
 * @Author: Yuntian Li
 * @Github: https://github.com/yuntinali91
 * @Date: 2019-09-24 14:28:08
 * @LastEditors: Yuntian Li
 * @LastEditTime: 2019-09-25 15:13:20
 */
#include <iostream>
#include <opencv2/opencv.hpp>
#include <Eigen/Dense>

using namespace std;

void distortion(const Eigen::Vector2d& u_u, Eigen::Vector2d& u_d, 
				const Eigen::Matrix3d& K, const Eigen::Vector4d& D);
void undistortion(const Eigen::Vector2d& u_u, Eigen::Vector2d& u_d, 
				const Eigen::Matrix3d& K, const Eigen::Vector4d& D, const double tolerance);

int main(int argc, char** argv){
    // distortion coefficients
    Eigen::Vector4d D{-2.917e-1, 8.228e-2, 5.333e-5, -1.578e-4};
    // intrinsic matrix
    Eigen::Matrix3d K;
    K << 4.616e2, 0, 3.63e2,
         0, 4.603e2, 2.481e2,
         0, 0, 1;
    
    Eigen::Vector2d u{40., 50.};
    Eigen::Vector2d u_u, u_d;

    distortion(u, u_d, K, D);
    cout << "Distorted point coordinates are:" << endl << u_d << endl;

    double tolerance(1e-4);
    undistortion(u_d, u_u, K, D, tolerance);
    cout << "Undistorted point coordinates are: " << endl << u_u << endl;

    return 0;
}

void distortion(const Eigen::Vector2d& u_u, Eigen::Vector2d& u_d, 
				const Eigen::Matrix3d& K, const Eigen::Vector4d& D){
    // lift from pixel plane to normalized plane
    Eigen::Vector3d p_u;
    Eigen::Vector3d u_u_homo{0., 0., 1.};
    
    u_u_homo.head(2) = u_u;

    p_u = K.inverse() * u_u_homo;
    // apply distortion
    double x_u, y_u;
    x_u = p_u[0]; y_u = p_u[1];
    
    double k1, k2; // radial distortion coefficients
    double p1, p2; // tangent distortion coefficients
    k1 = D[0]; k2 = D[1];
    p1 = D[2]; p2 = D[3]; 

    double x_u2, y_u2, xy_u, r2;
    x_u2 = x_u * x_u;
    y_u2 = y_u * y_u;
    xy_u = x_u * y_u;
    r2 = x_u2 + y_u2;
    
    Eigen::Vector3d p_d;
    p_d[0] = x_u * (1 + k1 * r2 + k2 * r2 * r2) + (2 * p1 * xy_u + p2 * (r2 + 2 * x_u2));
    p_d[1] = y_u * (1 + k1 * r2 + k2 * r2 * r2) + (2 * p2 * xy_u + p1 * (r2 + 2 * y_u2));
    p_d[2] = 1.;
    // project from normalized plane to normalized plane
    u_d = (K * p_d).head(2);
}

void undistortion(const Eigen::Vector2d& u_d, Eigen::Vector2d& u_u, 
				const Eigen::Matrix3d& K, const Eigen::Vector4d& D, const double tolerance){
    // lift from pixel plane to normalized plane
    Eigen::Vector3d p_d;
    Eigen::Vector3d u_d_homo{0., 0., 1.};

    u_d_homo.head(2)=u_d;
    p_d = K.inverse() * u_d_homo;

    // recursive undistortion
    double k1, k2; // radial distortion coefficients
    double p1, p2; // tangent distortion coefficients
    k1 = D[0]; k2 = D[1];
    p1 = D[2]; p2 = D[3]; 

    double x_d, y_d;
    x_d = p_d[0]; y_d = p_d[1];

    Eigen::Vector3d p_u;
    p_u[2] = 1.0;

    int count(0), conv_count(0);

    while (1){
        
        count++;
        double x_d2, y_d2, xy_d, r2;
        x_d2 = x_d * x_d;
        y_d2 = y_d * y_d;
        xy_d = x_d * y_d;
        r2 = x_d2 + y_d2;

        p_u[0] = (p_d[0] - (2 * p1 * xy_d + p2 * (r2 + 2 * x_d2))) / (1 + k1 * r2 + k2 * r2 * r2);
        p_u[1] = (p_d[1] - (2 * p2 * xy_d + p2 * (r2 + 2 * y_d2))) / (1 + k1 * r2 + k2 * r2 * r2);

        // project from normalized plane to pixel plane
        u_u = (K * p_u).head(2);
        cout << "Iterative times: " << count << "  Estimation: " << u_u.transpose() << endl;
    
        // termination criteria
        double u_err, v_err;
        u_err = abs(K(0, 0) * (p_u[0] - x_d));
        v_err = abs(K(1, 1) * (p_u[1] - y_d));
        if ( u_err < tolerance && v_err < tolerance) conv_count++;           
    
        if (conv_count > 3){
            cout << "Total Iterative times: " << count << endl
                << "Final error: " << u_err << " pixels in x and " << v_err << " pixels in y." << endl
                << "Final estimation: " << u_u.transpose() << endl;
            break;
        }

        x_d = p_u[0]; y_d = p_u[1];

    }
}

鱼眼相机模型

Kannala畸变模型

鱼眼相机的成像原理并不同于普通的真空相机模型,而是一个半球面映射过程,包括透射映射、等距映射、等立体角映射、正交投影、球极投影等。这里仅介绍OpenCV中使用的等距投影模型,Kannala畸变投影模型。
在这里插入图片描述
上图给出了鱼眼相机的等距成像模型,所谓等距模型,即空间点在像平面的像高rr仅与入射角θ\theta有关,且两者之间为等比关系,即r=fθr = f\theta
在这里插入图片描述
上图进一步给出了鱼眼相机在等距模型下的完整成像关系。假设空间点在经过外参变换后在相机坐标系下的坐标为,P=[Xc,Yc,Zc]T\bm{P}=[X^c,Y^c,Z^c]^T,将其缩放至归一化平面有:
Xnc=Xc/ZcYnc=Yc/Zc(1) \begin{aligned} X_n^c &= X^c / Z^c\\ Y_n^c &= Y^c / Z^c \end{aligned} \tag{1}
则入射角θ\theta可以表示为:
rc=Xnc2+Xnc2θ=atan(rc/1)=atan(rc)(2) \begin{aligned} r^c& = \sqrt{{X_n^c}^2+{X_n^c}^2}\\ \theta&=atan(r^c/1)=atan(r^c) \end{aligned} \tag{2}
进而根据等距投影模型可以得到像点的像高为:ri=fθr^i=f\theta,利用相似三角形关系,同时考虑成像面为归一化成像面(f=1f=1)可得:
{Xncrc=xriYncrc=yri{x=fθrcXncy=fθrcYnc{x=θrcXncy=θrcYnc(3) \left\{ \begin{aligned} \frac{X_n^c}{r^c}&=\frac{x}{r^i}\\ \frac{Y_n^c}{r^c}&=\frac{y}{r^i} \end{aligned} \right. \Rightarrow \left\{ \begin{aligned} x &= \frac{f\theta}{r^c}X_n^c\\ y &= \frac{f\theta}{r^c}Y_n^c \end{aligned} \right. \Rightarrow \left\{ \begin{aligned} x &= \frac{\theta}{r^c}X_n^c\\ y &= \frac{\theta}{r^c}Y_n^c \end{aligned} \right. \tag{3}
式(1)-(3)即为理想情况下鱼眼相机的等距投影模型,实际成像中,鱼眼相机的畸变会引起入射角的变化,Kannala给出的鱼眼畸变模型如下:
θd=θ+k1θ3+k2θ5+k3θ7+k4θ9(4) \theta_d=\theta+k_1\theta^3+k_2\theta^5+k_3\theta^7+k_4\theta^9 \tag{4}
将式(4)计算得到的θd\theta_d带入式(3)中替换θ\theta即可得到存在畸变的鱼眼相机等距投影模型。
在获取像平面坐标p=[x,y,z]T\bm{p}=[x,y,z]^T后,基于相机内参矩阵K\bm{K}获得像点在像素平面上的坐标,由于这一过程与针孔相机模型完全一致,这里略过不谈。

鱼眼相机畸变校正

在上述鱼眼相机等距投影模型中,可以记空间点P\bm{P}与鱼眼透镜光心O\bm{O}连线与透镜的交点为P1=[X1c,Y1c,Z1c]T\bm{P}_1=[X_1^c,Y_1^c, Z_1^c]^T,将P1\bm{P}_1表示为极坐标形式有:
X1c=Rsin(θ)cos(φ)Y1c=Rsin(θ)sin(φ)Z1c=Rcos(θ) \begin{aligned} X_1^c&=R\sin(\theta)\cos(\varphi)\\ Y_1^c&=R\sin(\theta)\sin(\varphi)\\ Z_1^c&=R\cos(\theta) \end{aligned}
由于通常我们在归一化球面(R=1R=1)上进行投影,鱼眼相机畸变校正等效于已知像点坐标p=[x,y,z]T\bm{p}=[x,y,z]^T和畸变参数k1,k2,k3,k4k_1,k_2,k_3,k_4,获得无畸变情况下的极坐标参数θ\thetaφ\varphi

  • φ\varphi求解
    成像面上夹角φ1\varphi_1可以用像点坐标表示为:tan(φ1)=y/x\tan(\varphi_1)=y/x。由于入射光线和出射光线必定在同一个入射面内,有φ=φ1\varphi=\varphi_1,因此未畸变的极坐标参数φ\varphi为:
    φ=tan(y/x) \varphi=\tan(y/x)
  • θ\theta求解
    畸变后的入射角θd\theta_d可以用像点坐标表示为θ=(x2+y2)\theta=\sqrt{(x^2+y^2)}θ\theta角可通过牛顿迭代求解式(4)所表示的多项式方程获得,令:
    f(θ)=θ+k1θ3+k2θ5+k3θ7+k4θ9θd f(\theta)=\theta+k_1\theta^3+k_2\theta^5+k_3\theta^7+k_4\theta^9 - \theta_d
    则根据牛顿迭代有:
    θk+1=θkf(θ)f(θ) \theta_{k+1}=\theta_k-\frac{f(\theta)}{f'(\theta)}
2019-04-29 15:14:42 weixin_43002202 阅读数 476

对图像进行增强处理的函数input,相关操作包括:随机裁剪、随机翻转、随机调整对比度、随机调整亮度以及标准化和归一化操作,目的在于能够产生更多训练样本。在大部分图像识别问题中,通过图像数据预处理可以尽量避免模型受到无关因素的影响或者达到数据增强的目的,从而提高模型的准确率,TensorFlow提供了几类简单的图像处理函数。

本节会用到一张分辨率为895x560的.png格式的图像。所有的代码为了展示图像处理的效果都用到了matplotlib.pyplot工具,这是Python的画图工具,接下来会对几个常用到的图像处理API(预定义的函数)详细介绍。

1、图像编解码处理


一副彩色的RGB图像可以看做是一个三维矩阵,矩阵中不同位置的数据表示图像不同位置颜色的亮度。然而图像在储存的时候不会直接存储为矩阵数据,而是记录经过编码后的结果,所以要将一副图像还原为一个三维矩阵,需要解码的过程,接下来的裁剪、翻转、调整色度等操作都是在图像解码操作为基础进行的。

图像一般会编码存储为.jpeg、.jpg、.png和.gif等格式,TensorFlow提供了一些函数实现对这些格式图像的编码\解码操作的支持

  • JPEG: 应用最广泛的图片格式之一,它采用一种特殊的有损压缩算法,将不易被人眼察觉的图像颜色删除,从而达到较大的压缩比(可达到2:1甚至40:1),所以“身材娇小,容貌姣好”,特别受网络青睐 ,JPEG的文件格式一般有两种文件扩展名:.jpg和.jpeg,这两种扩展名的实质是相同的,我们可以把.jpg的文件改名为.jpeg,而对文件本身不会有任何影响。
  • GIF:分为静态GIF和动画GIF两种,支持透明背景图像,适用于多种操作系统,“体型”很小,网上很多小动画都是GIF格式。其实GIF是将多幅图像保存为一个图像文件,从而形成动画,所以归根到底GIF仍然是图片文件格式。
  • PNG: 与JPG格式类似,网页中有很多图片都是这种格式,压缩比高于GIF,支持图像透明,可以利用Alpha通道调节图像的透明度,因此.png图像是一个四通道的图像。

下面的样例展示了使用函数对.png格式图片进行解码/编码的过程:

API1:tf.gfile.FastGFile(path,decodestyle) 图片读取

1、函数功能:实现从存储的文件当中对图片的读取。

2、函数参数:

  • path:图片所在路径
  • decodestyle:图片的解码方式。(‘r’:UTF-8编码; ‘rb’:非UTF-8编码)

    UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。是一种编码方式,UFT-8是Unicode编码方式的一种。

  • API2:tf.image.decode_png(contents, channels=None, name=None) .png图片的解码函数

    1、函数功能:TensorFlow提供了decode_png()函数将.png格式的图像解码从而得到图像对应的三维矩阵,数据类型为uint8。

    2、函数参数:

  • contents: string”类型的张量,使用png编码格式的图片
  • channels: 默认是None,表示解码图像的颜色通道数。
  • 3、其他解码函数

  • .jpg.jpeg: tf.image.decode_jpeg()
  • .gif: tf.image.decode_gif()
  •  
  • name: A name for the operation (optional).

API3:tf.image.encode_png(image)

1、函数功能:表示一张图片的三维矩阵重新按照png格式编码并存入文件中

2、函数参数:

  • image: sshape为3的Tensor

3、其他编码函数

  • .jpg.jpeg: tf.image.encode_jpeg()

 

import matplotlib.pyplot as plt
import tensorflow as tf

# 读取原始的图像,实现对图片的读取,读取到内存当中
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:

    img_after_decode = tf.image.decode_png(image)

    # 输出解码之后的三维矩阵,并调用pyplot工具可视化得到的图像
    
    print(img_after_decode.eval())
    
    plt.imshow(img_after_decode.eval())#负责处理
    plt.show()#负责执行

    # 这一句是为了方便后续的样例程序对图像进行处理
    # img_after_decode = tf.image.convert_image_dtype(img_after_decode,dtype = tf.float32)

    # TensorFlow提供了encode_png()函数将解码后的图像进行再编码
    # 表示一张图片的三维矩阵重新按照png格式编码并存入文件中。在资源管理器中打开这张图像,可以得到和原始图像一样的图像
    encode_image = tf.image.encode_png(img_after_decode)
    with tf.gfile.GFile("cat.png", "wb") as f:
        f.write(encode_image.eval())

2、翻转图像


在数据增强的过程中,使用了image.random_flip_left_right()函数对图像进行随机左右翻转,这是image.py中的函数。

使用翻转函数的目的在于能够使得训练出来的网络具有识别经翻转后图像的能力。


API4 : tf.image.random_flip_left_right( image,seed=None) 随机左右翻转函数

1、函数功能:按水平 (从左向右)方向 随机翻转图像,将图像左右翻转的概率为0.5,即输出的图像有50%的可能性是上左右翻转的,否则就输出原图.

2、函数参数:

  • image:形状为[height, width, channels]的三维张量.
  • seed:一个Python整数,用于创建一个随机种子.

其他翻转函数:

图像固定左右翻转: lipped2 = tf.image.flip_left_right(image)
随机上下翻转图片: image.random_flip_up_down(image,seed)
图像固定上下翻转: image.flip_up_down(image)
图像固定对角线翻转: image.transpose_image(image)

 

import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    #decode
    img_after_decode = tf.image.decode_png(image)
    # 以一定概率左右翻转图片。
    # 函数原型为random_flip_left_right(image,seed)
    flipped = tf.image.random_flip_left_right(img_after_decode)

    # 用pyplot工具显示
    plt.imshow(flipped.eval())
    plt.show()

3、图像色彩调整


图像色彩的调整包括对图像的亮度、对比度、饱和度和色相方面的调整,训练神经网络模型时随机调整这些属性,目的是使经过训练的模型尽可能小地受到这些与图像识别无关因素的影响,使CNN模型更加稳定。


* 亮度调整


API 5:tf.image.random_brightness(image, max_delta,seed=None) 随机调整亮度函数

1、函数功能:在某范围[-max_delta, max_delta)中随机选取的delta,来随机调整图片亮度,公式为:????????=????????+?????newimage=oldimage+delta

2、函数参数:

  • image: 一个图像.
  • max_delta:float型数据,必须是非负的

其他亮度调整函数(固定调整亮度):

tf.image.adjust_brightness(image,delta)
说明:指定参数delta,参数为正值则图像的亮度会增加,为负值则图像的亮度会降低

 

#随机调整亮度 tf.image.random_brightness

import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)

    #函数原型random_brightness(image,max_delta,seed)
    #max_delta的值不能为负,函数会在[-max_delta,max_delta]值之间随机调整图像的亮度
    adjusted_brightness = tf.image.random_brightness(img_after_decode,max_delta=0.8)
    
    # 用pyplot工具显示
    plt.imshow(adjusted_brightness.eval())
    plt.show()

* 对比度调整


API 6:tf.image.random_contrast(image,lower,upper,seed=None) 随机对比度调整函数

1、函数功能:通过在某范围[lower,upper)中随机选取调整对比度因子contrast_factor,以随机调整图像的对比度. 改变原图片的对比度,公式为(?−????)∗??????????????+????(?−????)∗??????????????+????(x−mean)∗contrastfactor+mean(x−mean)∗contrastfactor+mean, 其中的x为原输入,mean为每个通道的均值。

2、函数参数:

  • image:具有3个或更多维度的图像张量
  • lower:浮点型,随机对比因子的下限.
  • upper:浮点型,随机对比因子的上限.

其他亮度调整函数(固定调整对比度):

tf.image.adjust_contrast(images,contrast_factor)
说明:参数contrast_factor选取时可为正或为负,正值会增加对比度,负值会降低对比度。
#随机调整对比度tf.image.random_contrast
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)

    #函数原型random_contrast(image,lower,upper,seed)
    #函数会在[lower upper]之间随机调整图像的对比度
    #但要注意参数lower和upper都不能为负
    adjusted_contrast = tf.image.random_contrast(img_after_decode, 0.2,18, )
    
    plt.imshow(adjusted_contrast.eval())
    plt.show()

 


* 色相调整


API 7:tf.image.adjust_hue(image,delta,name=None) 固定色相调整函数

1、函数功能:改变原图像的色彩(hue),本函数将原RGB图像转换到HSV空间后,在hue通道上加上delta后,再转换回RGB空间。

HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间,这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)

RGB和HSV转换

 

2、函数参数:

  • image:RGB图像或图像
  • delta:浮点型,要添加多少色相通道.

3、注意:

对于色相和饱和度的调整,因为有RGB图像转换到HSV的过程,因此只能对RGB三通道的图像进行处理,因此这一步应该使用.jpeg格式的图像来进行操作


其他色相调整函数:

tf.image.random_hue(image, max_delta)
说明:功能是在[-max_delta, max_delta]的范围随机调整图片的色相。max_delta的取值在[0, 0.5]之间。

 

#固定调整色相adjusted_hue

import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat2.jpeg", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_jpeg(image)

    # 函数原型adjust_hue(image,delta,name)
    adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.1)
    adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.3)
    adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.6)
    adjusted_hue = tf.image.adjust_hue(img_after_decode, 0.9)

    plt.imshow(adjusted_hue.eval())
    plt.show()

* 饱和度调整


API 8:tf.image.adjust_saturation(image,saturation_factor, name=None) 固定饱和度调整函数

1、函数功能:将RGB图像转换为浮点表示形式,将其转换为HSV,向饱和通道添加偏移量,再转换回RGB

2、函数参数:

  • image:RGB图像或图像,最后尺寸的大小必须为3
  • saturation_factor:浮点型,因子乘以饱和度

其他亮度调整函数(随机调整对比度):

tf.image.random_saturation(image, lower, upper,seed)在[lower, upper]的范围随机调整图的饱和度。

 

#固定调整饱和度tf.image.adjust_saturation
import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat2.jpeg", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_jpeg(image)

    #函数原型adjust_contrast(images,contrast_factor)
    #将图片的饱和度调整-6。
    adjusted_saturation = tf.image.adjust_saturation(img_after_decode, -6)
    #将图片的饱和度调整+6。
    adjusted_saturation = tf.image.adjust_saturation(img_after_decode, 6)
    
    plt.imshow(adjusted_saturation.eval())
    plt.show()

5、调整图像大小


在实际情况中,从各渠道获取的图片不会是固定大小的。为例能够将图像的像素值输入到输入单元个数固定的网格中,需要将图像的大小统一,这就是图像大小调整需要完成的任务。调整图像的方式有多种,包括保留全部信息的缩放、以中心位置为中心的裁剪填充、指定区域的裁剪填充


*保留全部图像信息的缩放


API 9:tf.image.resize_images(image,size, method)

1、函数功能:使用指定的调整方法method调整images尺寸为为size的大小.

2、函数参数:

  • image:需要改变尺寸的图像
  • size: 改变之后图像的大小
  • method: 用于表示改变图像过程用的差值方法。有四种取值分别对应不同的调整方法
      0:双线性插值法。
      1:最近邻居法。
      2:双三次插值法。
      3:面积插值法。

 四种方法有细微的差别,但是如果没有特别的要求,可以认为结果是相同的,一般采用双线插值法。

 

import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)

    #函数原型resize_images(images,size,method,align_corners),原图大小895x560
    
    resized0 = tf.image.resize_images(img_after_decode, [300, 300], method=0)
    resized1 = tf.image.resize_images(img_after_decode, [300, 300], method=1)
    resized2 = tf.image.resize_images(img_after_decode, [300, 300], method=2)
    resized3 = tf.image.resize_images(img_after_decode, [300, 300], method=3)

    print(resized.dtype)
    #打印的信息<dtype: 'uint8'>

    # 从print的结果看出经由resize_images()函数处理图片后返回的数据是float32格式的,
    # 所以需要转换成uint8才能正确打印图片,这里使用np.asarray()存储了转换的结果
    resized0 = np.asarray(resized0.eval(), dtype="uint8")
    resized1 = np.asarray(resized1.eval(), dtype="uint8")
    resized2 = np.asarray(resized2.eval(), dtype="uint8")
    resized3 = np.asarray(resized3.eval(), dtype="uint8")


    plt.imshow(resized0)
    plt.show()
    plt.imshow(resized1)
    plt.show()
    plt.imshow(resized2)
    plt.show()
    plt.imshow(resized3)
    plt.show()

*图像裁剪或填充(中心位置)


API 10:tf.image.resize_image_with_crop_or_pad(image,target_height,target_width)

1、函数功能:实现了裁剪或者填充的功能,是第四节中用到的裁剪函数,在使用时会会传入的图像数据制定一个目标大小

如果原始图像的大小小于目标大小,则函数会在原始图像的四周进行全0填充,以达到目标大小;

如果原始图像的大小大于目标大小,则函数会以原始图像的中心为中心对图像进行裁剪,裁剪之后就得到了目标大小   

2、函数参数:

  • image:需要改变尺寸的图像
  • target_height:目标高度.
  • target_width:目标宽度.

以下代码是使用该函数的示范,实现了将图像裁剪到300x300和填充到1000x1000(原图像895x560)

 

#随机裁剪填充函数tf.image.resize_image_with_crop_or_pad()

import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)

    #函数原型resize_image_with_crop_or_pad(image,target_height,target_width)
    #裁剪图像
    croped = tf.image.resize_image_with_crop_or_pad(img_after_decode, 300, 300)
    #填充图像
    padded = tf.image.resize_image_with_crop_or_pad(img_after_decode, 1000, 1000)

    #用pyplot显示结果
    plt.imshow(croped.eval())
    plt.show()
    plt.imshow(padded.eval())
    plt.show()

*按比例裁剪图像


API 11:tf.image.central_crop(image,central_fraction)

1、函数功能:以原始图像的中心为中心,按照传递进来的比例参数将图像进行裁剪,保留中心区域。

2、函数参数:

  • image:需要改变尺寸的图像
  • central_fraction:float类型,取值在(0,1]内,要裁剪的大小部分的比率

以下代码是使用该函数的示范,实现了将图像裁剪到原始大小的40%。

 

#按比例调整图像大小函数 tf.image.central_crop()
import matplotlib.pyplot as plt
import tensorflow as tf

image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)

    # 函数原型central_crop(image,central_fraction)
    #参数central_fraction是裁剪比例
    
    central_cropped = tf.image.central_crop(img_after_decode, 0.4)
    
    plt.imshow(central_cropped.eval())
    plt.show()

*指定位置裁剪、填充图像


API 12.1:tf.image.crop_to_bounding_box(image,offset_height,offset_width,target_height,target_width)

API 12.2:tf.image.pad_to_bounding_box(image,offset_height,offset_width,target_height,target_width)

以上函数都是以图像中心为中心对图像进行裁剪或者填充的,除了这些函数,TensorFlow还提供了上面两个函数才裁剪或者填充图像的指定区域

这两个函数都是通过设置高度和宽度方向的偏移量来控制索要裁剪或填充的区域,将图像裁剪到指定的边界框.

tf.image.crop_to_bounding_box()

  • offset_height:表示目标图像的左上角距离原始顶部边的行数

  • offset_width:表示目标图像的左上角距离原始图像左侧边的列数

  • target_height,target_width:表示裁剪后的目标图像大小

tf.image.pad_to_bounding_box()

  • offset_height:表示在图像的顶部添加全0填充的行数

  • offset_width:表示在图像的左侧添加全0填充的列数

  • target_height,target_width:表示填充后的目标图像大小

以下代码是使用该函数的示范

import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)

    # 函数原型
    # crop_to_bounding_box(image,offset_height,offset_width,target_height,target_width)
    # pad_to_bounding_box(image,offset_height,offset_width,target_height,target_width)
    croped = tf.image.crop_to_bounding_box(img_after_decode, 100, 100, 300, 300)
    padded = tf.image.pad_to_bounding_box(img_after_decode, 100, 100, 1000, 1000)

    plt.imshow(croped.eval())
    plt.show()
    plt.imshow(padded.eval())
    plt.show()

 

6、图像的标注框


在图像中添加标注框有着很大的作用,在一些用于图像识别的数据集中,图像中某些需要关注的特征通常会被标注框圈出来,或者在一些图像识别问题中需要模型将识别出的图像中的物体用标注框圈出来。


*矩形标注框


API 13:tf.image.draw_bounding_boxes(images,boxes,name=None)

1、函数功能:在一批图像上绘制边界框。

在图像像素之上绘制由位置指定的边界框boxes,可以指定一个或多个boxes。

每个边界框的坐标boxes编码为[y_min, x_min, y_max, x_max]。  

对于图像大小为895x560,假设指定boxes的值为[0.05, 0.05, 0.9, 0.7],那么矩形标注框左上角在图像中的坐标为(0.05x895,0.05x560),矩形边框右下角的坐标为(0.9x895,0.7x560)

2、函数参数:

  • image:四维的 Tensor,形状[batch, height, width, depth],一批图片。
  • boxes:类型为float32,shape为三维的Tensor。

API 14:tf.expand_dims(input, dim, name=None)

1、函数功能:实现给定一个input,给input增加一个维度。
tf.image.draw_bounding_boxes()函数要求输入的图像数据是是实数型,并且输入是一个batch的数据,因此是一个四维矩阵,所以要增加一个维度

2、函数参数:

  • input:输入的Tensor
  • dim:增加的维度,从0开始插入

 

import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)

    # tf.expand_dims()将矩阵转化为为四维矩阵,函数原型expand_dims(input,axis,name,dim)
    # tf.image.convert_image_dtype()将数据类型转化为实数型
    batched = tf.expand_dims(tf.image.convert_image_dtype(img_after_decode, tf.float32), 0)

    # 定义边框的坐标系数
    boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.20, 0.3, 0.5, 0.5]]])

    # 绘制边框,函数原型draw_bounding_boxes(images,boxes,name)
    image_boxed = tf.image.draw_bounding_boxes(batched, boxes)

    # draw_bounding_boxes()函数处理的是一个batch的图片,如果此处给imshow()函数
    # 传入image_boxed参数会造成报错(Invalid dimensions for image data)
    plt.imshow(image_boxed[0].eval())
    plt.show()

*随机变形的边界框


API 15:tf.image.sample_distorted_bounding_box(

image_size,
bounding_boxes,
seed=None,
seed2=None,
min_object_covered=None,
aspect_ratio_range=None,
area_range=None,
max_attempts=None,
use_image_if_no_bounding_boxes=None,
name=None

)

1、函数功能:此函数为图像生成单个随机变形的边界框。函数输出的是可用于配合tf.slice()函数裁剪原始图像;还可以配合tf.image.draw_bounding_boxes()产生随机标注框

一般在设置该函数参数时只需要设置前两个,我们了解一下前两个参数的用法

2、函数参数:

  • image_size:一个Tensor,必须是下列类型之一:uint8,int8,int16,int32,int64,是1维的,并且包含[height, width, channels]

  • bounding_boxes:一个float32类型的三维Tensor,形状为[batch, N, 4],描述与图像相关的Ñ个边界框。

    例如bounding_boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.20, 0.3, 0.5, 0.5]]]),它的形状就是[1,2,4]

3、返回值:

返回值为3个张量:begin,size和 bboxes。

  • begin,size: 用于 tf.slice() 剪裁图像,作为输入的参数,随机裁剪

  • bboxes: 可以用于 tf.image.draw_bounding_boxes 函数来画出边界框,bboxes形状是固定的[1,1,4],因此最终只有一个标注边框的输出

下面的程序展示了通过tf.image.sample_distorted_bounding_box()函数配合函数tf.image.draw_bounding_boxes()以及函数tf.slice()来完成图像随机裁剪以及随机添加标注框

tf.image.sample_distorted_bounding_box()函数的返回值具有随机性,因此标注的框和裁剪出来的图片也都是随机的,所以每次得到的结果也不相同。

 

import matplotlib.pyplot as plt
import tensorflow as tf
image = tf.gfile.FastGFile("cat.png", 'rb').read()

with tf.Session() as sess:
    img_after_decode = tf.image.decode_png(image)
    boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.20, 0.3, 0.5, 0.5]]])

    #函数原型
    #sample_distorted_bounding_box(image_size,bounding_boxes,seed,seed2,min_object_covered,
    # aspect_ratio_range,area_range,max_attempts,use_image_if_no_bounding_boxes,name)
    
    begin, size, bounding_box = tf.image.sample_distorted_bounding_box(tf.shape(img_after_decode), bounding_boxes=boxes)

    batched = tf.expand_dims(tf.image.convert_image_dtype(img_after_decode, tf.float32), 0)
    image_boxed = tf.image.draw_bounding_boxes(batched, bounding_box)

    #slice()函数原型slice(input_,begin,size,name)
    sliced_image = tf.slice(img_after_decode,begin,size)

    plt.imshow(image_boxed[0].eval())
    plt.show()
    plt.imshow(sliced_image.eval())
    plt.show()

 

2018-12-01 11:38:50 qq_38662930 阅读数 80

keras常用语法

Sequential模型
Keras一般用Sequential模型做为搭建神经网络的开始,本节简要论述Sequential模型接口的主要使用方法。
add(self,layer)
用途:向模型中添加一个层。
参数layer是Layer对象,也即是层。
pop(self)
用途:弹出模型最后的一层,无返回值,该方法一般很少用到。
compile(self,optimizer,loss,metrics=None,sample_weight_mode=None)
该方法编译用来配置模型的学习过程,其参数有以下这些:

  • optimizer:字符串(预定义优化器名)或优化器对象,参考优化器。
  • loss:字符串(预定义损失函数名)或目标函数,参考损失函数。
  • metrics:列表,包含评估模型在训练和测试时的网络性能的指标,典型用法是 metrics=[‘accuracy’]。
  • sample_weight_mode:如果你需要按时间步为样本赋权(2D权矩阵),将该值设
    为“temporal”。默认为“None”,代表按样本赋权(1D权)。
  • kwargs:使用TensorFlow作为后端请忽略该参数,若使用Theano作为后端,kwargs的 值将会传递给K.function。

model=Sequential()
model.add(Dense(32,input_shape(500,)))
model.add(Dense(10,activation='softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',metrics=['accuracy'])

该例子添加两个Dense层,并用rmsprop优化器进行优化,损失函数定义为
categorical_crossentroy,各种损失函数的含义在后面章节中会提高。
模型在使用前必须编译,否则在调用fit或evaluate时会抛出异常。

fit(self,x,y,batch_size=32,epochs=10,verbose=1,callbacks=None,validation_split=0.0,val)
.

本函数将模型训练epoch轮,其参数有:

  • x:输入数据。如果模型只有一个输入,那么x的类型是numpyarray,如果模型有多
    个输入,那么x的类型应当为list,list的元素是对应于各个输入的numpyarray。
  • y:标签向量,numpyarray类型。
  • batch_size:整数,指定进行梯度下降时每个batch包含的样本数。训练时一个batch的
    样本会被计算一次梯度下降,使目标函数优化一步。
  • epochs:训练的轮数,每个epoch会把训练集轮一遍。
  • verbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch输出一行记录。
  • callbacks:list,其中的元素是keras.callbacks.Callback的对象。这个list中的回调函数将会在训练过程中的适当时机被调用,参考回调函数
  • validation_split:0~1之间的浮点数,用来指定训练集的一定比例数据作为验证集。注意,validation_split的划分在shuffle之前,因此如果你的数据本身是有序的,需要先手工打乱再指定validation_split,否则可能会出现验证集样本不均匀。
  • validation_data:形式为(X,y)的tuple,是指定的验证集。此参数将覆盖validation_spilt参数。
  • shuffle:布尔值或字符串,一般为布尔值,表示是否在训练过程中随机打乱输入样本的顺序。若为字符串“batch”,则是用来处理HDF5数据的特殊情况,它将在batch内部将数据打乱。
  • class_weight:字典,将不同的类别映射为不同的权值,该参数用来在训练过程中调整损失函数(只能用于训练)。
  • sample_weight:权值的numpyarray,用于在训练时调整损失函数(仅用于训练)。可以传递一个1D的与样本等长的向量用于对样本进行1对1的加权,或者在面对时序数据时,传递一个的形式为<samples,sequence_length>的矩阵来为每个时间步上的样本赋不同的权。这种情况下请确定在编译模型时添加了
    sample_weight_mode=‘temporal’。
  • initial_epoch:从该参数指定的epoch开始训练,在继续之前的训练时有用。

fit函数返回一个History的对象,其History.history属性记录了损失函数和其他指标的数值随epoch变化的情况,如果有验证集的话,也包含了验证集的这些指标变化情况。

evaluate(self,x,y,batch_size=32,verbose=1,sample_weight=None)

  • x:输入数据,与fit一样,是numpyarray或numpyarray的list。

  • y:标签,numpyarray。

  • batch_size:整数,含义同fit的同名参数。

  • verbose:含义同fit的同名参数,但只能取0或1。

  • sample_weight:numpyarray,含义同fit的同名参数。

本函数返回一个测试误差的标量值(如果模型没有其他评价指标),或一个标量的list(如果模型还有其他的评价指标)。model.metrics_names将给出list中各个值的含义。
如果没有特殊说明,以下函数的参数均保持与fit的同名参数相同的含义。

predict(self,x,batch_size=32,verbose=0)

本函数按batch获得输入数据对应的预测结果,其中x是输入向量,batch_size是每批次选取 的数据集数量。
函数的返回值是预测值的numpyarray。

predict_classes(self,x,batch_size=32,verbose=1)

本函数按batch产生输入数据的类别预测结果,它和predict的区别是:
Predict返回各个类别的可能性结果,是一个n维向量,n等于类别的数量;而predict_classes
返回的是最可能的类别,对每个输入返回的是类别名称。 函数的返回值是类别预测结果的numpyarray或numpy。

train_on_batch(self,x,y,class_weight=None,sample_weight=None)

本函数在一个batch的数据上进行一次参数更新。 函数返回训练误差的标量值或标量值的list,与evaluate的情形相同

test_on_batch(self,x,y,sample_weight=None)

本函数在一个batch的样本上对模型进行评估。 函数的返回与evaluate的情形相同。

predict_on_batch(self,x)

本函数在一个batch的样本上对模型进行测试。 函数返回模型在一个batch上的预测结果。

fit_generator(self,generator,steps_per_epoch,epochs=1,verbose=1,callbacks=None,validatio)

利用Python的生成器,逐个生成数据的batch并进行训练。生成器与模型将并行执行以提高效率。例如,该函数允许我们在CPU上进行实时的数据提升,同时在GPU上进行模型训练。
函数返回一个History对象

函数的参数是:

  • generator:生成器函数,生成器的输出应该为: a)一个形如(inputs,targets)的tuple。 b)一个形如(inputs,targets,sample_weight)的tuple。所有的返回值都应该包含相同数目的样本。生成器将无限在数据集上循环。每个epoch以经过模型的样本数达到 samples_per_epoch时,记一个epoch结束。 steps_per_epoch:整数,当生成器返回steps_per_epoch次数据时计一个epoch结束,执 行下一个epoch。
  • epochs:整数,数据迭代的轮数。
  • verbose :日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每 个epoch输出一行记录。
  • validation_data:具有以下三种形式之一。 a)生成验证集的生成器。
    b)一个形如(inputs,targets)的tuple。
    c)一个形如(inputs,targets,sample_weights)的tuple。
    validation_steps:当validation_data为生成器时,本参数指定验证集的生成器返回次数。
  • class_weight:规定类别权重的字典,将类别映射为权重,常用于处理样本不均衡问题。
  • sample_weight:权值的numpyarray,用于在训练时调整损失函数(仅用于训练)。可以传递一个1D的与样本等长的向量用于对样本进行1对1的加权,或者在面对时序数据时,传递一个的形式为(samples,sequence_length)的矩阵来为每个时间步上的样本赋不同的权。这种情况下请确定在编译模型时添加了
  • sample_weight_mode=‘temporal’。
  • workers:最大进程数。
  • max_q_size:生成器队列的最大容量
  • pickle_safe:若为真,则使用基于进程的线程。由于该实现依赖多进程,不能传递nonpicklable(无法被pickle序列化)的参数到生成器中,因为无法轻易将它们传入子进
    程中。
  • initial_epoch:从该参数指定的epoch开始训练,在继续之前的训练时有用。
defgenerate_arrays_from_file(path):
	while 1:
		f=open(path)
		for	line in	f:
		#create Numpy arrays of inputdata 
		#and labels from each line in thefile
,
			x,y=process_line(line)
			yield(x,y)
		f.close()
		model.fit_generator(generate_arrays_from_file('/my_file.txt'),
samples_per_epoch=10000,epochs=10)
2019-09-07 13:01:33 yuanjiu4221 阅读数 47

用已经训练好的模型对每张图片去处理特征:
本文使用的是 inception_V3的checkpoint去对flicker30k的每张图片进行处理它的特征:
需要用到的文件:
在这里插入图片描述
这个文件的作用:

checkpoint里存的是各个变量的数值,我们需要重新去构建计算图后可以把checkpoint里的变量值载入进来,就是这个图中对应的变量去checkpoint读取变量值,在tensorflow中还支持对checkpoint做进一步的处理,tensorflow中有一个工具觉freezegraph,就是将checkpoint和计算图给结合起来的一种工具,就是它生成的图是graph和checkpoint变量都在这里面,使用这张图我们就不需要先重建图再去载入变量,直接导入这张图,因为这张图里都是固定的变量,然后就可以直接用这张图里的计算方法去进行处理了~
即这个文件就是由一个freezegraph处理得到的工具~

1.首先载入必要的库:

在这里插入图片描述
(2)定义输入输出文件:
在这里插入图片描述
(3)因为有大约3万张图片,将所有的图片特征都抽取出放到一个文件夹下是不明智的,所以把这些数据存储在多个文件中,这样每个文件的载入都不会占用太多的时间,所以需要一个参数来确保如何去切分这些数据,即每个子文件中存放多少个数据:

在这里插入图片描述
即将30000张图片分成30000/100个文件,每个文件有100个小文件
判断输出文件夹是否存在,如果不存在的话需要去建立它:

在这里插入图片描述
(4)解析图片描述文件:

将图片描述文件和图片的对应关系用一个字典表达出来:
在这里插入图片描述
在这里插入图片描述
测试一下:

在这里插入图片描述
以上,完成~

没有更多推荐了,返回首页