本文分析注释的源码链接为:https://github.com/tegg89/SRCNN-Tensorflow
环境搭建参考其README.md

SRCNN训练阶段流程分析:https://download.csdn.net/download/baixue0729/12451414
SRCNN测试阶段流程分析:
https://download.csdn.net/download/baixue0729/12458624

main.py

from model import SRCNN
from utils import input_setupimport numpy as np
import tensorflow as tfimport pprint
import os'''
定义训练和测试参数
(包括:如果采用SGD时的batchSize、学习率、步长stride、训练还是测试模式),
此后由设定的参数进行训练或测试。
'''flags = tf.app.flags  #命令行执行时传参数,使命令行运行的时候可以定义里面的参数
#第一个是参数名称,第二个参数是默认值,第三个是参数描述
flags.DEFINE_integer("epoch", 15000, "Number of epoch [15000]")
#一个batch更新一次参数
flags.DEFINE_integer("batch_size", 128, "The size of batch images [128]")
flags.DEFINE_integer("image_size", 33, "The size of image to use [33]")
#因为卷积时不进行padding,三层卷积后特征尺寸变为21,因此将图像中心的21*21的小图像作为标签值。
flags.DEFINE_integer("label_size", 21, "The size of label to produce [21]")
##学习率文中设置为:前两层1e-4 第三层1e-5。SGD+指数学习率10-2作为初始?
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]")
#卷积步长:训练采用14(可变,越小训练集越多);测试采用21,因为image_size33-(image_size33-label_size21)=21
flags.DEFINE_integer("stride", 14, "The size of stride to apply input image [14]")
#checkpoint所在文件夹名称,数据保存检查点
flags.DEFINE_string("checkpoint_dir", "checkpoint", "Name of checkpoint directory [checkpoint]")
#sample所在文件夹名称
flags.DEFINE_string("sample_dir", "sample", "Name of sample directory [sample]")
flags.DEFINE_boolean("is_train", True, "True for training, False for testing [True]")
FLAGS = flags.FLAGS
#测试:stride=21,is_train=False。训练:stride=14,is_train=True。#创建了一个打印的类,这样就可以调用pp的函数了
pp = pprint.PrettyPrinter()def main(_):pp.pprint(flags.FLAGS.__flags)if not os.path.exists(FLAGS.checkpoint_dir):os.makedirs(FLAGS.checkpoint_dir)  #在当前地址创建"checkpoint"文件夹if not os.path.exists(FLAGS.sample_dir):os.makedirs(FLAGS.sample_dir)  #在当前地址创建"sample"文件夹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对象,自动调用初始化函数srcnn.train(FLAGS)if __name__ == '__main__':   #import到其他脚本中不会被执行tf.app.run()

model.py

from utils import (read_data, input_setup, imsave,merge
)import time
import os
import matplotlib.pyplot as pltimport 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 = sessself.is_grayscale = (c_dim == 1)self.image_size = image_sizeself.label_size = label_sizeself.batch_size = batch_sizeself.c_dim = c_dim  #图像的颜色维度self.checkpoint_dir = checkpoint_dirself.sample_dir = sample_dirself.build_model()'''三次卷积,卷积核大小分别是9,1,5。输出通道分别是64,32,1。#第一层CNN:对输入图片的特征提取。(9 x 9 x 64卷积核)#第二层CNN:对第一层提取的特征的非线性映射(1 x 1 x 32卷积核)#第三层CNN:对映射后的特征进行重建,生成高分辨率图像(5 x 5 x 1卷积核)'''def build_model(self):#插入一个将始终供给张量的占位符,返回可以用作输入值的句柄的“张量”。(张量的元素类型, shape=要输入的张量的形状(可选), name=操作的名称(可选))self.images = tf.placeholder(tf.float32, [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')self.weights = {# 卷积核:f1*f1*c*n1. c为输入图像通道数,文中取YCrCb中Y通道,c=1;f1=9;n1为当前卷积核输出深度取64'''tf.Variable():创建一个值为initial_value的新变量。新变量将添加到集合中列出的图形集合中。initial_value:默认=None。一个Tensor或可转换为Tensor的Python对象,它是变量的初始值。trainable: 默认=True。如果默认值True,也会将该变量添加到图形集合GraphKeys.TRAINABLE_VARIABLES中。该集合用作`Optimizer`类使用的默认变量列表。collections: 默认=None。图形集合键列表。新变量将添加到这些集合中。validate_shape: 默认=True。如果为False,则允许使用未知形状的值初始化变量。如果默认值为True,则必须知道initial_value的形状。caching_device: 默认=None。可选的设备字符串,描述应将变量缓存在何处以进行读取。name: 默认=None。变量的可选名称。variable_def: 默认=None。`VariableDef`协议缓冲区。dtype: 默认=None。如果设置,则initial_value将转换为给定类型。如果为“无”,则将保留数据类型。expected_shape: 默认=None。一个TensorShape。如果设置,则initial_value应该具有此形状。import_scope: 默认=None。可选的`string`。要添加到“变量”的名称范围。仅在从协议缓冲区初始化时使用。tf.random_normal():输出符合正态分布的随机值。返回:具有随机法线值的指定形状的张量。shape:一维整数Tensor或Python数组。 输出张量的形状。mean:默认=0.0。类型为dtype的0-D张量或Python值。 正态分布的平均值。stddev:默认=1.0。类型为dtype的0-D张量或Python值。 正态分布的标准偏差。dtype:默认=dtypes.float32。输出的类型。seed:默认=None。一个Python整数。 用于为分发创建随机种子。name:默认=None。操作的名称(可选)''''w1': tf.Variable(tf.random_normal([9, 9, 1, 64], stddev=1e-3), name='w1'),'w2': tf.Variable(tf.random_normal([1, 1, 64, 32], stddev=1e-3), name='w2'),'w3': tf.Variable(tf.random_normal([5, 5, 32, 1], stddev=1e-3), name='w3')}self.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')}self.pred = self.model()  #只要调用了model函数就会有输出,返回三层卷积后的结果,预测值# Loss function (MSE)  loss用的是MSEself.loss = tf.reduce_mean(tf.square(self.labels - self.pred))# 主函数调用(训练或测试),创建一个Saver变量self.saver = tf.train.Saver()def train(self, config):#数据的准备if config.is_train:input_setup(self.sess, config)  #读取图像文件并制作其子图像,并将其保存为h5文件格式。else:nx, ny = input_setup(self.sess, config)# 训练为checkpoint下train.h5;测试为checkpoint下test.h5if 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)#建立优化器,初始化所有参数,计数器,计时器#采用SGD(具有标准反向传播的随机梯度下降)优化器。还有Adam优化器,据说SGD的效果更好(待验证)self.train_op = tf.train.GradientDescentOptimizer(config.learning_rate).minimize(self.loss)tf.initialize_all_variables().run()counter = 0  #计数器start_time = time.time()  #计时器#加载训练过的参数if self.load(self.checkpoint_dir):print(" [*] Load SUCCESS")else:print(" [!] Load failed...")#训练模型,每隔500次保存一次模型if config.is_train:print("Training...")for ep in xrange(config.epoch):  #对于每次epoch# Run by batch imagesbatch_idxs = len(train_data) // config.batch_size  #计算一个epoch有多少batchfor idx in xrange(0, batch_idxs):  #以batch为单元更新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:  #每更新10次输出一次数据print("Epoch: [%2d], step: [%2d], time: [%4.4f], loss: [%.8f]" \% ((ep+1), counter, time.time()-start_time, err))if counter % 500 == 0:  #每更新500次保存一次数据self.save(config.checkpoint_dir, counter)  #确认路径,存储sesselse:print("Testing...")#调用此方法将执行所有前面的操作,这些操作将生成生成该张量的操作所需的输入。 feed_dict:将“ Tensor”对象映射到提要值的字典。result = self.pred.eval({self.images: train_data, self.labels: train_label})  #网络输出result = merge(result, [nx, ny])  #将一个batch内的图片拼接在一起,测试时只切一张图所以一个batch就是全部输入result = result.squeeze()  #squeeze去除维度为1的地方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):'''将图片经过3次卷积,步长都是1。卷积加偏置,前两层有relu激活函数,最后一层无激活函数。:return: 最后的一次的卷积结果strides在官方定义中是一个一维具有四个元素的张量,其规定前后必须为1,所以我们可以改的是中间两个数,中间两个数分别代表了水平滑动和垂直滑动步长值。'''conv1 = tf.nn.relu(tf.nn.conv2d(self.images, self.weights['w1'], strides=[1,1,1,1], padding='VALID') + self.biases['b1'])conv2 = tf.nn.relu(tf.nn.conv2d(conv1, self.weights['w2'], strides=[1,1,1,1], padding='VALID') + self.biases['b2'])conv3 = tf.nn.conv2d(conv2, self.weights['w3'], strides=[1,1,1,1], padding='VALID') + self.biases['b3']return conv3#确认路径,存储sessdef 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)  #存储路径为checkpoint/srcnn_label_sizeif not os.path.exists(checkpoint_dir):os.makedirs(checkpoint_dir)'''向文件夹中写入包含当前模型中所有可训练变量的checkpoint文件,之后可以使用saver.restore()方法,重载模型的参数,继续训练或者用于测试数据'''self.saver.save(self.sess,os.path.join(checkpoint_dir, model_name),global_step=step)#加载sess,成功加载返回True,否则返回False。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)ckpt = tf.train.get_checkpoint_state(checkpoint_dir)  #从文件夹中获取checkpoint文件if ckpt and ckpt.model_checkpoint_path:ckpt_name = os.path.basename(ckpt.model_checkpoint_path)  #获取checkpoint文件的文件名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
由于scipy.misc.imread函数的'mode'选项,需要Scipy版本> 0.18
"""import os
import glob  #glob库,作用是类似于系统的文件路径匹配查询
import h5py  #h5py库,主要用于读取或创建datasets或groups
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.FLAGS  #命令行参数传递def read_data(path):"""Read h5 format data file读取h5文件中的data和label数据,需要将其转换成np.array格式Args: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:  #读取h5格式数据文件(用于训练或测试)data = np.array(hf.get('data'))label = np.array(hf.get('label'))return data, labeldef preprocess(path, scale=3):"""处理图片,input_,label_分别是输入和输出的图片,对应低分辨率和高分辨率Preprocess single image file (1) Read original image as YCbCr format (and grayscale as default)(2) Normalize(3) Apply image file with bicubic interpolation预处理单个图像文件(1)以YCbCr格式读取原始图像(默认为灰度)(2)归一化  (将0-255的uint8型数据转换到0-1之间)(3)应用三次插值的图像文件Args: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)  #n维数组对象,灰度图。暂不确定颜色通道数是1还是3,假设是1label_ = modcrop(image, scale)  #将图片规整到可以被scale整除的宽和高,例如:scale=3时(197,176,1?)->(195,174,1)# Must be normalizedimage = image / 255.    #无用label_ = label_ / 255.'''scipy.ndimage.interpolation.zoom(input,  #输入数组zoom,   #沿轴的缩放系数。output=None,  #放置输出的数组,或返回数组的dtypeorder=3,      #样条插值的顺序(0~5),=3:三次插值,=0:最近插值,=1:双线性插值.mode='constant', #根据给定的模式('常数','最近','反映'或'换行')填充输入边界之外的点cval=0.0,        #如果mode ='constant',则用于输入边界之外的点的值。prefilter=True)  #是否在插值之前使用spline_filter进行预过滤,如果为False,则假定输入已被过滤。'''input_ = scipy.ndimage.interpolation.zoom(label_, (1./scale), prefilter=False)  #使用三次插值缩小scale倍 (195,174,1)->(65,58,1)input_ = scipy.ndimage.interpolation.zoom(input_, (scale/1.), prefilter=False)  #使用三次插值扩大scale倍 (65,58,1)->(195,174,1)return input_, label_   #低分辨率,高分辨率 (195,174,1)def prepare_data(sess, dataset):"""得到图片路径listArgs:dataset: choose train dataset or test datasetFor train dataset, output data would be ['.../t1.bmp', '.../t2.bmp', ..., '.../t99.bmp']"""if FLAGS.is_train:filenames = os.listdir(dataset)data_dir = os.path.join(os.getcwd(), dataset)  #os.getcwd():得到当前文件路径data = glob.glob(os.path.join(data_dir, "*.png"))  #.bmpelse:data_dir = os.path.join(os.sep, (os.path.join(os.getcwd(), dataset)), "Set")   #os.sep():无需考虑],\和/data = glob.glob(os.path.join(data_dir, "*.png"))return datadef make_data(sess, data, label):"""制作h5文件,将data(checkpoint下的train.h5 或test.h5)利用h5的create_dataset 写入Make input data as h5 file formatDepending on 'is_train' (flag value), savepath would be changed.将输入数据设置为h5文件格式根据“ is_train”(标志值),保存路径将被更改。"""if FLAGS.is_train:savepath = os.path.join(os.getcwd(), 'checkpoint/train.h5') #定义h5文件保存地址else:savepath = os.path.join(os.getcwd(), 'checkpoint/test.h5')with h5py.File(savepath, 'w') as hf:hf.create_dataset('data', data=data)   #建立一个名叫"data"的HDF5数据集hf.create_dataset('label', data=label) #建立一个名叫"label"的HDF5数据集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.使用其路径读取图像。默认值为灰度,如论文所述,图像以YCbCr格式读取。"""if is_grayscale:return scipy.misc.imread(path, flatten=True, mode='L').astype(np.float)  #flatten参数:将彩色图层变为灰度else:return scipy.misc.imread(path, mode='L').astype(np.float)    #mode='YCbCr'def modcrop(image, scale=3):"""将图片规整到可以被scale整除的宽高。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.shape       #eg: image.shape=(197, 176, 3), scale=3h = h - np.mod(h, scale)    #h=195w = w - np.mod(w, scale)    #w=174image = 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 image#测试时返回每行每列能裁剪出的子图个数
def input_setup(sess, config):"""就是把输入和输出图片,切成一小块存起来Read image files and make their sub-images and saved them as a h5 file format.读取图像文件并制作其子图像,并将其保存为h5文件格式。"""# 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 = []padding = abs(config.image_size - config.label_size) / 2  #(33-21)/2=6if config.is_train:for i in xrange(len(data)):  #对于每张图input_, label_ = preprocess(data[i], config.scale)  #低分辨率,高分辨率if len(input_.shape) == 3:h, w, _ = input_.shape  #195,174,1else:h, w = input_.shapefor x in range(0, h-config.image_size+1, config.stride):   #(0~163),stride=测试用21,为了拼接。for y in range(0, w-config.image_size+1, config.stride): #(0~142)# (x ~ x+33, y ~ y+33),裁剪成[33*33]的小图sub_input = input_[x:x+config.image_size, y:y+config.image_size]# (x+6 ~ x+6+21,y+6 ~ y+6+21),裁剪成[21*21]的小图,舍弃边缘部分sub_label = label_[x+int(padding):x+int(padding)+config.label_size, y+int(padding):y+int(padding)+config.label_size]# Make channel value,重定义图片块大小:image_size*image_size*1, 为什么要定义成三通道sub_input = sub_input.reshape([config.image_size, config.image_size, 1])  #image_size:33,即33*33*1sub_label = sub_label.reshape([config.label_size, config.label_size, 1])  #label_size:21,即21*21*1sub_input_sequence.append(sub_input)  #将小图添加到输入列表sub_label_sequence.append(sub_label)  #将小图添加到标签列表else:  #test的话少了一个循环,它只对其中的一张图片做处理。input_, label_ = preprocess(data[0], 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 = 0   #宽度和高度方向上分别裁剪出的子图数量for x in range(0, h-config.image_size+1, config.stride):nx += 1; ny = 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 transform# 需要将数据转成numpy类型,被存为h5格式。arrdata = np.asarray(sub_input_sequence)  # 输入集,格式为[?, 33, 33, 1],?代表小图总数arrlabel = np.asarray(sub_label_sequence) # 标签集,格式为[?, 21, 21, 1]make_data(sess, arrdata, arrlabel)  #制作h5文件,这个是产生训练数据的函数#nx,ny分别是有多少列,多少行。if not config.is_train:return nx, nydef imsave(image, path):  #保存图片return scipy.misc.imsave(path, image)def merge(images, size):"""将一个batch内的图片拼接在一起images:一个batch图片,size:第一个参数是高度上有几张图片,第二个宽度上有几张图片%:求模运算,取余。//:取整,返回不大于结果的一个最大的整数/:浮点数除法"""h, w = images.shape[1], images.shape[2]   #(21,21),images.shape[0]=小图个数img = np.zeros((h*size[0], w*size[1], 1)) #size[0]=nx; size[1]=nyfor idx, image in enumerate(images):  #枚举i = idx % size[1]   #idx = 0 ~ 小图个数-1j = idx // size[1]img[j*h:j*h+h, i*w:i*w+w, :] = imagereturn img

SRCNN代码及注释相关推荐

  1. JavaScript兼容HTML代码的注释

    2019独角兽企业重金招聘Python工程师标准>>> JavaScript兼容HTML代码的注释,所以<!--和-->也被视为单行注释. <!-- var x = ...

  2. 我的代码和注释都写的像坨屎,那又怎么样?

    一周前,我的朋友圈被一篇 #百度某新员工发飙:前人代码写得像一坨屎,颠覆了对大厂的认知# 的文章刷屏了,评论区也非常热闹. 但让我惊讶的是,几乎所有的声音里都充满着嘲笑与讥讽,有的剑指百度的价值观,那 ...

  3. java 注释 超链接_java_Java代码注释规范详解,代码附有注释对程序开发者来 - phpStudy...

    Java代码注释规范详解 代码附有注释对程序开发者来说非常重要,随着技术的发展,在项目开发过程中,必须要求程序员写好代码注释,这样有利于代码后续的编写和使用. 基本的要求: 1.注释形式统一 在整个应 ...

  4. python反向缩进_在Pycharm中对代码进行注释和缩进的方法详解

    在Pycharm中对代码进行注释和缩进的方法详解 一.注释 1. #单行注释 2. """ 多行注释 """ 3. pycharm多行注释快 ...

  5. stm32f4 输出pwm波_stm32的pwm输出代码及注释

    stm32是非常常用的单片机.脉冲宽度调制(PWM),是英文"Pulse WidthModulation"的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非 ...

  6. python代码块注释快捷键_pycharm默认注释与快捷键功能

    pycharm快捷键使用技巧 Ctrl+d 复制当前行.或者选择的块 Ctrl+n 跳转到类 Ctrl+shift+n 快速查找文件名 Ctrl+shift+f 全局查找,快速查找关键字的文件 Ctr ...

  7. 代码注释掉还能执行_日志消息是可执行代码和注释

    代码注释掉还能执行 尽管在一个人的代码中应添加多少注释之间存在意见分歧,但我认为可以肯定地说,大多数开发人员都同意以下代码段中的注释是多余的: // increment the total total ...

  8. 日志消息是可执行代码和注释

    尽管在一个人的代码中应添加多少注释之间存在意见分歧,但我认为可以肯定地说,大多数开发人员都同意以下代码段中的注释是多余的: // increment the total total++; 在该示例中, ...

  9. logisim优先编码器怎么用_编码器简介、应用的stm32代码及注释

    在机器人的旋转关节中.或者底盘的驱动轮中,编码器是一个很常用的部件,它用于检测输出轴的位置.转速.下面我们先从它的分类开始,后面还有一个简单的用法示例代码. 磁编码器 1.编码器分类: 按工作原理:光 ...

  10. Keil | 解决Keil与Source Insight4.0配合使用时,代码与注释位置(乱码)不一样的问题

    文章目录 一.前言 二.解决问题 2.1.Keil 2.2.Source Insight4.0 一.前言 Keil | 解决Keil与VScode配合使用时,代码与注释位置不一样的问题 上一次解决VS ...

最新文章

  1. SQLSERVER使用CLR Stored Procedure导出数据到Excel
  2. uml+oopc嵌入式c语言开发精讲_当前火爆的嵌入式领域,为什么选择C语言作为开发语言?了解一下...
  3. Bootstrap模态框(modal)显示、隐藏与禁用ESC代码实现
  4. C#引用office库
  5. Android APK的加固方法
  6. 承载网络开启后显示无法连接到Internet,已解决
  7. 关于图片预加载的思考
  8. 基于Jenkins+Gitlab的自动化部署实战
  9. 我的CSS笔记(一)
  10. 盐城哪里学计算机表格,盐城办公自动化周末班
  11. 全面解读新中产:有房有车有贷、半数决策看老婆
  12. 视觉十四讲第六章G2O实践出错后的解决方法
  13. 最强开发资源平台大合集
  14. 2022专转本计算机大类
  15. Blockchains Distributed L week3 爱宝授课记录(1)
  16. RIDE中testcase的edit界面显示异常/空白
  17. git添加diff tool和merge tool
  18. 莫尔斯代码的c语言编码,c语言编写莫尔斯码,帮帮忙啊,速回
  19. 蓝桥《最大和》python题解
  20. GEE开发之ERA5(气温、降水、压力、风速等)数据获取和分析

热门文章

  1. HDB3码的编码解释(简单粗暴)
  2. QtableView点击滑动设计
  3. 深入理解“智慧城市”
  4. 2021年全国水体分布(按省、市、县)矢量数据的制作与分享
  5. 【Godot】对 Godot 节点设计的思考
  6. 今日分享|每日领红包最高618,全是无门槛使用!
  7. 2020年Spring学习笔记目录
  8. RS485芯片UN485E的特点及其应用
  9. linux是手机端还是电脑端,pc端是什么意思(pc端和移动端有什么区别)
  10. vue.js官方中文文档