深度学习之超分辨率算法——SRCNN

  • 网络为基础卷积层

  • tensorflow 1.14

  • scipy 1.2.1

  • numpy 1.16

  • 大概意思就是针对数据,我们先把图片按缩小因子照整数倍进行缩减为小图片,再针对小图片进行插值算法,获得还原后的低分辨率的图片作为标签。

  • main.py 配置文件

from model import SRCNN
from utils import input_setup
import numpy as np
import tensorflow as tf
import pprint
import osflags = tf.app.flags
# 设置轮次
flags.DEFINE_integer("epoch", 1000, "Number of epoch [1000]")
# 设置批次
flags.DEFINE_integer("batch_size", 128, "The size of batch images [128]")
# 设置image大小
flags.DEFINE_integer("image_size", 33, "The size of image to use [33]")
# 设置label
flags.DEFINE_integer("label_size", 21, "The size of label to produce [21]")
# 学习率
flags.DEFINE_float("learning_rate", 1e-4, "The learning rate of gradient descent algorithm [1e-4]")
# 图像颜色的尺寸
flags.DEFINE_integer("c_dim", 1, "Dimension of image color. [1]")
# 对输入图像进行预处理的比例因子大小
flags.DEFINE_integer("scale", 3, "The size of scale factor for preprocessing input image [3]")
# 步长
flags.DEFINE_integer("stride", 14, "The size of stride to apply input image [14]")
# 权重位置
flags.DEFINE_string("checkpoint_dir", "checkpoint", "Name of checkpoint directory [checkpoint]")
# 样本目录
flags.DEFINE_string("sample_dir", "sample", "Name of sample directory [sample]")
# 训练还是测试
flags.DEFINE_boolean("is_train", False, "True for training, False for testing [True]")
FLAGS = flags.FLAGS# 格式化打印
pp = pprint.PrettyPrinter()def main(_):#   打印参数pp.pprint(flags.FLAGS.__flags)# 没有就新建~if not os.path.exists(FLAGS.checkpoint_dir):os.makedirs(FLAGS.checkpoint_dir)if not os.path.exists(FLAGS.sample_dir):os.makedirs(FLAGS.sample_dir)# Session提供了Operation执行和Tensor求值的环境;with tf.Session() as sess:srcnn = SRCNN(sess,image_size=FLAGS.image_size,label_size=FLAGS.label_size,batch_size=FLAGS.batch_size,c_dim=FLAGS.c_dim,checkpoint_dir=FLAGS.checkpoint_dir,sample_dir=FLAGS.sample_dir)srcnn.train(FLAGS)if __name__ == '__main__':tf.app.run()
from utils import (read_data,input_setup,imsave,merge
)
import time
import os
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tftry:xrange
except:xrange = rangeclass SRCNN(object):# 模型初始化def __init__(self,sess,image_size=33,label_size=21,batch_size=128,c_dim=1,checkpoint_dir=None,sample_dir=None):self.sess = sess# 判断灰度图self.is_grayscale = (c_dim == 1)self.image_size = image_sizeself.label_size = label_sizeself.batch_size = batch_sizeself.c_dim = c_dimself.checkpoint_dir = checkpoint_dirself.sample_dir = sample_dirself.build_model()def build_model(self):# tf.placeholder(# dtype,# shape = None,# name = None# )# 定义image,labels 输入形式 N W H Cself.images = tf.placeholder(dtype=tf.float32, shape=[None, self.image_size, self.image_size, self.c_dim], name='images')self.labels = tf.placeholder(tf.float32, [None, self.label_size, self.label_size, self.c_dim], name='labels')# tf.Variable(initializer, name), 参数initializer是初始化参数,name是可自定义的变量名称,# shape为[filter_height, filter_width, in_channel, out_channels]# 构建模型参数self.weights = {'w1': tf.Variable(initial_value=tf.random_normal([9, 9, 1, 64], stddev=1e-3), name='w1'),'w2': tf.Variable(initial_value=tf.random_normal([1, 1, 64, 32], stddev=1e-3), name='w2'),'w3': tf.Variable(initial_value=tf.random_normal([5, 5, 32, 1], stddev=1e-3), name='w3')}# the dim of bias== c_dimself.biases = {'b1': tf.Variable(tf.zeros([64]), name='b1'),'b2': tf.Variable(tf.zeros([32]), name='b2'),'b3': tf.Variable(tf.zeros([1]), name='b3')}# 构建模型 返回MHWCself.pred = self.model()# Loss function (MSE)self.loss = tf.reduce_mean(tf.square(self.labels - self.pred))# 保存和加载模型# 如果只想保留最新的4个模型,并希望每2个小时保存一次,self.saver = tf.train.Saver(max_to_keep=4,keep_checkpoint_every_n_hours=2)def train(self, config):if config.is_train:# 训练状态input_setup(self.sess, config)else:nx, ny = input_setup(self.sess, config)if config.is_train:data_dir = os.path.join('./{}'.format(config.checkpoint_dir), "train.h5")else:data_dir = os.path.join('./{}'.format(config.checkpoint_dir), "test.h5")train_data, train_label = read_data(data_dir)# Stochastic gradient descent with the standard backpropagationself.train_op = tf.train.GradientDescentOptimizer(config.learning_rate).minimize(self.loss)tf.initialize_all_variables().run()counter = 0start_time = time.time()if self.load(self.checkpoint_dir):print(" [*] Load SUCCESS")else:print(" [!] Load failed...")if config.is_train:print("Training...")for ep in xrange(config.epoch):# Run by batch imagesbatch_idxs = len(train_data) // config.batch_sizefor idx in xrange(0, batch_idxs):batch_images = train_data[idx * config.batch_size: (idx + 1) * config.batch_size]batch_labels = train_label[idx * config.batch_size: (idx + 1) * config.batch_size]counter += 1_, err = self.sess.run([self.train_op, self.loss],feed_dict={self.images: batch_images, self.labels: batch_labels})if counter % 10 == 0:print("Epoch: [%2d], step: [%2d], time: [%4.4f], loss: [%.8f]" \% ((ep + 1), counter, time.time() - start_time, err))if counter % 500 == 0:self.save(config.checkpoint_dir, counter)else:print("Testing...")# print(train_data.shape)# print(train_label.shape)# print("---------")result = self.pred.eval({self.images: train_data, self.labels: train_label})# print(result.shape)result = merge(result, [nx, ny])result = result.squeeze()image_path = os.path.join(os.getcwd(), config.sample_dir)image_path = os.path.join(image_path, "test_image.png")imsave(result, image_path)def model(self):# input : 输入的要做卷积的图片,要求为一个张量,shape为 [ batch, in_height, in_width, in_channel ],其中batch为图片的数量,in_height 为图片高度,in_width 为图片宽度,in_channel 为图片的通道数,灰度图该值为1,彩色图为3。(也可以用其它值,但是具体含义不是很理解)# filter: 卷积核,要求也是一个张量,shape为 [ filter_height, filter_width, in_channel, out_channels ],其中 filter_height 为卷积核高度,filter_width 为卷积核宽度,in_channel 是图像通道数 ,和 input 的 in_channel 要保持一致,out_channel 是卷积核数量。# strides: 卷积时在图像每一维的步长,这是一个一维的向量,[ 1, strides, strides, 1],第一位和最后一位固定必须是1# padding: string类型,值为“SAME” 和 “VALID”,表示的是卷积的形式,是否考虑边界。"SAME"是考虑边界,不足的时候用0去填充周围,"VALID"则不考虑# use_cudnn_on_gpu:  bool类型,是否使用cudnn加速,默认为true# padding = “SAME”输入和输出大小关系如下:输出大小等于输入大小除以步长向上取整,s是步长大小;# padding = “VALID”输入和输出大小关系如下:输出大小等于输入大小减去滤波器大小加上1,最后再除以步长(f为滤波器的大小,s是步长大小)。conv1 = tf.nn.relu(tf.nn.conv2d(self.images, self.weights['w1'], strides=[1, 1, 1, 1], padding='VALID',use_cudnn_on_gpu=True) + self.biases['b1'])conv2 = tf.nn.relu(tf.nn.conv2d(conv1, self.weights['w2'], strides=[1, 1, 1, 1], padding='VALID',use_cudnn_on_gpu=True) + self.biases['b2'])conv3 = tf.nn.conv2d(conv2, self.weights['w3'], strides=[1, 1, 1, 1], padding='VALID',use_cudnn_on_gpu=True) + self.biases['b3']return conv3def save(self, checkpoint_dir, step):model_name = "SRCNN.model"model_dir = "%s_%s" % ("srcnn", self.label_size)# 目录checkpoint_dir = os.path.join(checkpoint_dir, model_dir)# 不存在就新建if not os.path.exists(checkpoint_dir):os.makedirs(checkpoint_dir)# 保存# 参数'''sess,save_path,global_step=None,latest_filename=None,meta_graph_suffix="meta",write_meta_graph=True,write_state=True,strip_default_attrs=False,save_debug_info=False)'''self.saver.save(self.sess,os.path.join(checkpoint_dir, model_name),global_step=step)def load(self, checkpoint_dir):print(" [*] Reading checkpoints...")model_dir = "%s_%s" % ("srcnn", self.label_size)# 加载模型checkpoint_dir = os.path.join(checkpoint_dir, model_dir)# 通过checkpoint文件找到模型文件名ckpt = tf.train.get_checkpoint_state(checkpoint_dir)if ckpt and ckpt.model_checkpoint_path:# 返回path最后的文件名。如果path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素。ckpt_name = os.path.basename(ckpt.model_checkpoint_path)self.saver.restore(self.sess, os.path.join(checkpoint_dir, ckpt_name))# 加载成功return Trueelse:# 加载失败return False
  • utils.py 配置工具函数
"""
Scipy version > 0.18 is needed, due to 'mode' option from scipy.misc.imread function
"""import os
import glob
import h5py
import random
import matplotlib.pyplot as pltfrom PIL import Image  # for loading images as YCbCr format
import scipy.misc
import scipy.ndimage
import numpy as npimport tensorflow as tftry:xrange
except:xrange = rangeFLAGS = tf.app.flags.FLAGSdef read_data(path):"""Read h5 format data fileArgs:path: file path of desired filedata: '.h5' file format that contains train data valueslabel: '.h5' file format that contains train label values"""with h5py.File(path, 'r') as hf:data = np.array(hf.get('data'))label = np.array(hf.get('label'))return data, labeldef preprocess(path, scale=3):"""Preprocess single image file(1) Read original image as YCbCr format (and grayscale as default)(2) Normalize(3) Apply image file with bicubic interpolationArgs:path: file path of desired fileinput_: image applied bicubic interpolation (low-resolution)label_: image with original resolution (high-resolution)"""# 读取灰度图image = imread(path, is_grayscale=True)label_ = modcrop(image, scale)# Must be normalized# 归一化image = image / 255.label_ = label_ / 255.# zoom:类型为float或sequence,沿轴的缩放系数。 如果float,每个轴的缩放是相同的。 如果sequence,zoom应包含每个轴的一个值。# output:放置输出的数组,或返回数组的dtype# order:样条插值的顺序,默认为3.顺序必须在0-5范围内。# prefilter: bool, optional 。参数预滤波器确定输入是否在插值之前使用spline_filter进行预过滤(对于 > 1# 的样条插值所必需的)。 如果为False,则假定输入已被过滤。 默认为True。input_ = scipy.ndimage.interpolation.zoom(input=label_,zoom=(1. / scale), prefilter=False)input_ = scipy.ndimage.interpolation.zoom(input=input_,zoom=(scale / 1.), prefilter=False)return input_, label_def prepare_data(sess, dataset):"""Args:dataset: choose train dataset or test datasetFor train dataset, output data would be ['.../t1.bmp', '.../t2.bmp', ..., '.../t99.bmp']dataset:"Train" or "Test":to choose the data is train or test"""if FLAGS.is_train:filenames = os.listdir(dataset)#  获取数据目录data_dir = os.path.join(os.getcwd(), dataset)data = glob.glob(os.path.join(data_dir, "*.bmp"))else:# 获取测试集路径data_dir = os.path.join(os.sep, (os.path.join(os.getcwd(), dataset)), "Set5")data = glob.glob(os.path.join(data_dir, "*.bmp"))# 返回文件目录return datadef make_data(sess, data, label):"""Make input data as h5 file formatDepending on 'is_train' (flag value), savepath would be changed."""if FLAGS.is_train:savepath = os.path.join(os.getcwd(), 'checkpoint/train.h5')else:savepath = os.path.join(os.getcwd(), 'checkpoint/test.h5')with h5py.File(savepath, 'w') as hf:hf.create_dataset('data', data=data)hf.create_dataset('label', data=label)def imread(path, is_grayscale=True):"""Read image using its path.Default value is gray-scale, and image is read by YCbCr format as the paper said."""if is_grayscale:return scipy.misc.imread(path, flatten=True, mode='YCbCr').astype(np.float)else:return scipy.misc.imread(path, mode='YCbCr').astype(np.float)def modcrop(image, scale=3):"""To scale down and up the original image, first thing to do is to have no remainder while scaling operation.We need to find modulo of height (and width) and scale factor.Then, subtract the modulo from height (and width) of original image size.There would be no remainder even after scaling operation.要缩小和放大原始图像,首先要做的是在缩放操作时没有剩余。我们需要找到高度(和宽度)和比例因子的模。然后,从原始图像的高度(和宽度)中减去模。即使经过缩放操作,也不会有余数。"""if len(image.shape) == 3:# 取整h, w, _ = image.shapeh = h - np.mod(h, scale)w = w - np.mod(w, scale)image = image[0:h, 0:w, :]else:h, w = image.shapeh = h - np.mod(h, scale)w = w - np.mod(w, scale)image = image[0:h, 0:w]return imagedef input_setup(sess, config):"""Read image files and make their sub-images and saved them as a h5 file format."""# Load data pathif config.is_train:data = prepare_data(sess, dataset="Train")else:data = prepare_data(sess, dataset="Test")sub_input_sequence = []sub_label_sequence = []# 计算paddingpadding = abs(config.image_size - config.label_size) / 2  # 6if config.is_train:for i in xrange(len(data)):# TODO 获取原图和低分辨率还原标签input_, label_ = preprocess(data[i], config.scale)if len(input_.shape) == 3:h, w, _ = input_.shapeelse:h, w = input_.shapefor x in range(0, h - config.image_size + 1, config.stride):for y in range(0, w - config.image_size + 1, config.stride):sub_input = input_[x:x + config.image_size, y:y + config.image_size]  # [33 x 33]sub_label = label_[x + int(padding):x + int(padding) + config.label_size,y + int(padding):y + int(padding) + config.label_size]  # [21 x 21]# Make channel valuesub_input = sub_input.reshape([config.image_size, config.image_size, 1])sub_label = sub_label.reshape([config.label_size, config.label_size, 1])sub_input_sequence.append(sub_input)sub_label_sequence.append(sub_label)else:input_, label_ = preprocess(data[1], config.scale)if len(input_.shape) == 3:h, w, _ = input_.shapeelse:h, w = input_.shape# Numbers of sub-images in height and width of image are needed to compute merge operation.nx = ny = 0for x in range(0, h - config.image_size + 1, config.stride):# 保存索引nx += 1ny = 0for y in range(0, w - config.image_size + 1, config.stride):ny += 1sub_input = input_[x:x + config.image_size, y:y + config.image_size]  # [33 x 33]sub_label = label_[x + int(padding):x + int(padding) + config.label_size,y + int(padding):y + int(padding) + config.label_size]  # [21 x 21]sub_input = sub_input.reshape([config.image_size, config.image_size, 1])sub_label = sub_label.reshape([config.label_size, config.label_size, 1])sub_input_sequence.append(sub_input)sub_label_sequence.append(sub_label)"""len(sub_input_sequence) : the number of sub_input (33 x 33 x ch) in one image(sub_input_sequence[0]).shape : (33, 33, 1)"""# Make list to numpy array. With this transformarrdata = np.asarray(sub_input_sequence)  # [?, 33, 33, 1]arrlabel = np.asarray(sub_label_sequence)  # [?, 21, 21, 1]make_data(sess, arrdata, arrlabel)if not config.is_train:return nx, nydef imsave(image, path):return scipy.misc.imsave(path, image)def merge(images, size):# 合并图片h, w = images.shape[1], images.shape[2]img = np.zeros((h * size[0], w * size[1], 1))for idx, image in enumerate(images):i = idx % size[1]j = idx // size[1]img[j * h:j * h + h, i * w:i * w + w, :] = imagereturn img
  • 原图
    在这里插入图片描述

  • 效果图

  • 在这里插入图片描述

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

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

相关文章

基于海思soc的智能产品开发(mcu读保护的设置)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 对于市场上的产品,除了电路之外,软件保护也是非常重要的一个环节。要是自己辛辛苦苦写的软件,被竞争对手轻易地…

Zabbix6.0升级为6.4

为了体验一些新的功能,比如 Webhook 和问题抑制等,升级个小版本。 一、环境信息 1. 版本要求 一定要事先查看官方文档,确认组件要求的版本,否则版本过高或者过低都会出现问题。 2. 升级前后信息 环境升级前升级后操作系统CentOS…

GitLab的卸载与重装

目录 一、GitLab的卸载 二、 GitLab的安装与配置 1. 创建安装目录 2. 安装 3. 使用 3.1 初始化 3.2 创建空白项目 ​编辑 3.3 配置SSH 3.3.1 配置公钥 ​编辑 3.3.2 配置私钥 3.4 配置本地git库 一、GitLab的卸载 1. 停止gitlab sudo gitlab-ctl stop 2. 卸载…

Linux快速入门-Linux的常用命令

Linux的常用命令 1. Linux的终端与工作区1.1 终端概述1.2 切换终端 2. Shell语言解释器2.1 Shell概述 3. 用户登录与身份切换3.1 su 命令3.2 sudo 命令 4. 文件、目录操作命令4.1 pwd 命令4.2 cd 命令4.3 ls 命令4.3.1 ls 指令叠加使用 4.4 mkdir 命令4.5 rmdir 命令4.6 cp 命令…

三、ubuntu18.04安装docker

1.使用默认ubuntu存储库安装docker 更新软件存储库 更新本地软件数据库确保可以访问最新版本。打开终端输入:sudo apt-get update 卸载旧版本的docker 建议继续之前卸载任何旧的docker软件。打开终端输入:sudo apt-get remove docker docker-engine …

【Linux系统编程】:信号(2)——信号的产生

1.前言 我们会讲解五种信号产生的方式: 通过终端按键产生信号,比如键盘上的CtrlC。kill命令。本质上是调用kill()调用函数接口产生信号硬件异常产生信号软件条件产生信号 前两种在前一篇文章中做了介绍,本文介绍下面三种. 2. 调用函数产生信号 2.1 k…

专业电脑数据恢复软件 iFind Data Recovery v9.2.3 绿色便携版

前言 iFinD Data Recovery一款特别实用的数据找回工具,它很厉害,能帮你在SSD硬盘和Windows10系统上找回丢失的数据。而且,它还能深度扫描并恢复各种主流数码相机里的RAW格式照片,速度超快,用起来也很稳定顺畅&#xf…

QT:Widgets中的数据库应用

SQL数据库驱动 pro文件中添加如下一行代码 QT sql widgetsmain.cpp #include <QApplication> #include <QSqlDatabase> #include <QStringList> int main(int argc, char *argv[]) {QApplication a(argc, argv);qDebug() << "Available driver…

AI的进阶之路:从机器学习到深度学习的演变(三)

&#xff08;承接上集&#xff1a;AI的进阶之路&#xff1a;从机器学习到深度学习的演变&#xff08;二&#xff09;&#xff09; 四、深度学习&#xff08;DL&#xff09;&#xff1a;机器学习的革命性突破 深度学习&#xff08;DL&#xff09;作为机器学习的一个重要分支&am…

数据集-目标检测系列 车牌检测识别 数据集 CCPD2019

车牌检测&识别 数据集 CCPD2019 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” 贵在坚持&#xff01; 数据样…

(2024.12)Ubuntu20.04安装ZED-SDK

一.官网地址 ZED SDK 4.2 - Download | Stereolabs 选择适配版本进行下载 二.安装程序 下载完成后&#xff0c;进入文件目录&#xff0c;打开终端&#xff0c;输入&#xff1a; chmod x ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zstd.run ./ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zst…

python coding(二) Pandas 、PIL、cv2

Pandas 一个分析结构化数据的工具集。Pandas 以 NumPy 为基础&#xff08;实现数据存储和运算&#xff09;&#xff0c;提供了专门用于数据分析的类型、方法和函数&#xff0c;对数据分析和数据挖掘提供了很好的支持&#xff1b;同时 pandas 还可以跟数据可视化工具 matplotli…

第十五届蓝桥杯Scratch01月stema选拔赛—排序

排序 具体要求&#xff1a; 1). 点击绿旗&#xff0c;在舞台上出现4张点数不同的扑克牌&#xff0c;牌上的点数是随机的&#xff08;4-9点&#xff09;&#xff0c;如图所示&#xff1b; 完整题目可点击下方链接查看&#xff1a; 排序_scratch_嗨信奥-玩嗨信息奥林匹克竞赛-…

图形 3.4 延迟渲染管线介绍

延迟渲染管线介绍 B站视频&#xff1a;图形 3.4 延迟渲染管线介绍 文章目录 延迟渲染管线介绍渲染路径前向渲染渲染流程光照规则 延迟渲染渲染流程几何缓冲区 G-buffer 不同渲染路径的优劣以及特性优劣 Unity中渲染路径设置移动端优化分块延迟渲染 其他渲染路径不同路径下光源…

Qt之串口设计-线程实现(十二)

Qt开发 系列文章 - Serial-port&#xff08;十二&#xff09; 目录 前言 一、SerialPort 二、实现方式 1.创建类 2.相关功能函数 3.用户使用 4.效果演示 5.拓展应用-实时刷新 总结 前言 Qt作为一个跨平台的应用程序开发框架&#xff0c;在串口编程方面提供了方便易用…

1.gitlab 服务器搭建流程

前提条件&#xff1a; 一、服务器硬件水平 搭建gitlab服务器最低配置要求2核4G,低于这个配置的服务器运行效果很差。 gitlab官网&#xff1a;https://about.gitlab.com/ 下载地址&#xff1a;gitlab/gitlab-ce - Packages packages.gitlab.com 本机ubuntu 二、安装依赖 su…

powerhsell 初认识

免责声明 本文是学习与泷羽Sec B站课程的课程笔记内容&#xff0c;仅作学习使用&#xff0c;如有破坏网络安全的行为&#xff0c;本人概不负责 B站链接&#xff1a;https://space.bilibili.com/350329294 资源自取&#xff1a;https://pan.quark.cn/s/b2718e905db8 powerhsel…

自我维护和保养

学习运动两不误&#xff01; 本人学习过程中&#xff0c;长期久坐导致各种身体问题&#xff08;特别是腰间盘突出&#xff0c;右侧肩胛骨翘等问题&#xff01;&#xff09;&#xff0c;希望给有类似烦恼的人们带去福音&#xff01;&#xff01;&#xff01; 我的椎间盘损伤历…

详解磁盘IO、网络IO、零拷贝IO、BIO、NIO、AIO、IO多路复用(select、poll、epoll)

1、什么是I/O 在计算机操作系统中&#xff0c;所谓的I/O就是输入&#xff08;Input&#xff09;和输出&#xff08;Output&#xff09;&#xff0c;也可以理解为读&#xff08;Read&#xff09;和写&#xff08;Write)&#xff0c;针对不同的对象&#xff0c;I/O模式可以划分为…

Cursor的重磅功能Agent登场

今天看了一些介绍&#xff0c;cursor有一个新功能agent ,试用了一下非常好用。再也不用头痛地选择相关的上下文&#xff0c;真是懒人利器。 Agant特性&#xff1a; 可以自主选择上下文能够使用终端可以独立完成整个任务 赶紧介绍给大家&#xff0c;使用时&#xff0c;需要在c…