当前位置: 首页 > news >正文

工业园企业建设网站公司网站快照不更新了

工业园企业建设网站公司,网站快照不更新了,wordpress 中文工单,流水线 东莞网站建设一、介绍 本博文主要介绍实现通过SSD物体检测方式实现工件裂纹检测。裂纹图像如下所示#xff1a; 二、关于SSD算法 具体算法不再阐述#xff0c;详细请参考#xff1a; https://blog.csdn.net/u013989576/article/details/73439202 https://blog.csdn.net/xiaohu2022/arti…一、介绍 本博文主要介绍实现通过SSD物体检测方式实现工件裂纹检测。裂纹图像如下所示 二、关于SSD算法 具体算法不再阐述详细请参考 https://blog.csdn.net/u013989576/article/details/73439202 https://blog.csdn.net/xiaohu2022/article/details/79833786 https://www.sohu.com/a/168738025_717210 三、训练数据的制作 训练数据制作的时候选择LabelImg关于LabelImg的安装使用请参考https://blog.csdn.net/xunan003/article/details/78720189 关于选取裂纹数据的一点建议建议选的检测框数据一定要小这样方便收敛。 这里使用的是VOC2007的数据格式文件夹下面一共三个子文件夹。 其中Annotations文件夹存放的是LbaelImg制作数据生成的xml文件。 JPEGImages存放的是原图像.jpg格式。 ImageSets下面有一个Main文件夹Main文件夹下面主要是四个txt文件。 分别对应训练集、测试集、验证集等。该文件夹中的四个txt文件是从Annotations文件夹中随机选取的图像名称并按照一定的比例划分。 从xml文件生成Main文件夹的四个txt文件实现源码如下 import os import random trainval_percent 0.9 train_percent 0.9 xmlfilepath F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_two_class/Annotations txtsavepath F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_two_class/ImageSets/Main total_xml os.listdir(xmlfilepath) numlen(total_xml) listrange(num) tvint(num*trainval_percent) trint(tv*train_percent) trainval random.sample(list,tv) trainrandom.sample(trainval,tr) ftrainval open(txtsavepath/trainval.txt, w) ftest open(txtsavepath/test.txt, w) ftrain open(txtsavepath/train.txt, w) fval open(txtsavepath/val.txt, w) for i  in list:     nametotal_xml[i][:-4]\n     if i in trainval:         ftrainval.write(name)         if i in train:             ftrain.write(name)         else:             fval.write(name)     else:         ftest.write(name) ftrainval.close() ftrain.close() fval.close() ftest .close() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 四、训练数据 训练数据的文件为train_ssd300.py顾名思义就是图像的输入是300x300不过不用担心代码内部已经实现转换的程序可以输入任意尺寸的图像源码如下 from keras.optimizers import Adam, SGD from keras.callbacks import ModelCheckpoint, LearningRateScheduler, TerminateOnNaN, CSVLogger from keras import backend as K from keras.models import load_model from math import ceil import numpy as np from matplotlib import pyplot as plt from models.keras_ssd300 import ssd_300 from keras_loss_function.keras_ssd_loss import SSDLoss from keras_layers.keras_layer_AnchorBoxes import AnchorBoxes from keras_layers.keras_layer_DecodeDetections import DecodeDetections from keras_layers.keras_layer_DecodeDetectionsFast import DecodeDetectionsFast from keras_layers.keras_layer_L2Normalization import L2Normalization from ssd_encoder_decoder.ssd_input_encoder import SSDInputEncoder from ssd_encoder_decoder.ssd_output_decoder import decode_detections, decode_detections_fast from data_generator.object_detection_2d_data_generator import DataGenerator from data_generator.object_detection_2d_geometric_ops import Resize from data_generator.object_detection_2d_photometric_ops import ConvertTo3Channels from data_generator.data_augmentation_chain_original_ssd import SSDDataAugmentation from data_generator.object_detection_2d_misc_utils import apply_inverse_transforms import tensorflow as tf from keras import backend as K from focal_loss import focal_loss img_height 300 # Height of the model input images img_width 300 # Width of the model input images img_channels 3 # Number of color channels of the model input images mean_color [123, 117, 104] # The per-channel mean of the images in the dataset. Do not change this value if youre using any of the pre-trained weights. swap_channels [2, 1, 0] # The color channel order in the original SSD is BGR, so well have the model reverse the color channel order of the input images. n_classes 1 # 类的数量不算背景 scales_pascal [0.1, 0.2, 0.37, 0.54, 0.71, 0.88, 1.05] # The anchor box scaling factors used in the original SSD300 for the Pascal VOC datasets #一共在六个不同scale层次上进行采样最后一个1.05应该是无效的scales中的数字代表生成检测框的长度是feature map的长度的0.10.20.370.54.。。倍 # 长宽比例对应在aspect_ratios中不同scale采样的anchor数量和比例也不相同 scales_coco [0.07, 0.15, 0.33, 0.51, 0.69, 0.87, 1.05] # The anchor box scaling factors used in the original SSD300 for the MS COCO datasets scales scales_pascal aspect_ratios [[1.0, 2.0, 0.5],                  [1.0, 2.0, 0.5, 3.0, 1.0/3.0],                  [1.0, 2.0, 0.5, 3.0, 1.0/3.0],                  [1.0, 2.0, 0.5, 3.0, 1.0/3.0],                  [1.0, 2.0, 0.5],                  [1.0, 2.0, 0.5]] # The anchor box aspect ratios used in the original SSD300; the order matters two_boxes_for_ar1 True steps [8, 16, 32, 64, 100, 300] # The space between two adjacent anchor box center points for each predictor layer. offsets [0.5, 0.5, 0.5, 0.5, 0.5, 0.5] # The offsets of the first anchor box center points from the top and left borders of the image as a fraction of the step size for each predictor layer. clip_boxes False # Whether or not to clip the anchor boxes to lie entirely within the image boundaries variances [0.1, 0.1, 0.2, 0.2] # The variances by which the encoded target coordinates are divided as in the original implementation normalize_coords True # 加载或者重新建立一个模型二者选其一 # 1: Build the Keras model. K.clear_session() # Clear previous models from memory. model ssd_300(image_size(img_height, img_width, img_channels),                 n_classesn_classes,                 modetraining,                 l2_regularization0.0005,                 scalesscales,                 aspect_ratios_per_layeraspect_ratios,                 two_boxes_for_ar1two_boxes_for_ar1,                 stepssteps,                 offsetsoffsets,                 clip_boxesclip_boxes,                 variancesvariances,                 normalize_coordsnormalize_coords,                 subtract_meanmean_color,                 swap_channelsswap_channels) # 2: Load some weights into the model. # TODO: Set the path to the weights you want to load. weights_path VGG_ILSVRC_16_layers_fc_reduced.h5 model.load_weights(weights_path, by_nameTrue) model.summary() # 3: Instantiate an optimizer and the SSD loss function and compile the model. #    If you want to follow the original Caffe implementation, use the preset SGD #    optimizer, otherwise Id recommend the commented-out Adam optimizer. # adam Adam(lr0.001, beta_10.9, beta_20.999, epsilon1e-08, decay0.0) sgd SGD(lr0.0001, momentum0.9, decay0.001, nesterovFalse) ssd_loss SSDLoss(neg_pos_ratio3, alpha1.0) model.compile(optimizersgd, lossssd_loss.compute_loss, metrics[accuracy]) # model.compile(optimizersgd,  losscategorical_crossentropy, metrics[accuracy]) #模型加载结束 # 注意这里出现了梯度爆炸 #加载数据 # 1: Instantiate two DataGenerator objects: One for training, one for validation. # Optional: If you have enough memory, consider loading the images into memory for the reasons explained above. train_dataset DataGenerator(load_images_into_memoryFalse, hdf5_dataset_pathNone) val_dataset DataGenerator(load_images_into_memoryFalse, hdf5_dataset_pathNone) # 2: Parse the image and label lists for the training and validation datasets. This can take a while. # TODO: Set the paths to the datasets here. # The directories that contain the images. VOC_2007_images_dir       F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_expand/JPEGImages/ # VOC_2012_images_dir       ../../datasets/VOCdevkit/VOC2012/JPEGImages/ # The directories that contain the annotations. VOC_2007_annotations_dir       F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_expand/Annotations/ # VOC_2012_annotations_dir       ../../datasets/VOCdevkit/VOC2012/Annotations/ # The paths to the image sets. VOC_2007_train_image_set_filename     F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_expand/ImageSets/Main/train.txt # VOC_2012_train_image_set_filename     ../../datasets/VOCdevkit/VOC2012/ImageSets/Main/train.txt VOC_2007_val_image_set_filename       F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_expand/ImageSets/Main/val.txt # VOC_2012_val_image_set_filename       ../../datasets/VOCdevkit/VOC2012/ImageSets/Main/val.txt VOC_2007_trainval_image_set_filename F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_expand/ImageSets/Main/trainval.txt # VOC_2012_trainval_image_set_filename ../../datasets/VOCdevkit/VOC2012/ImageSets/Main/trainval.txt VOC_2007_test_image_set_filename     F:/competition code/ssd_keras-master/ssd_keras-master/data/liewen_expand/ImageSets/Main/test.txt # The XML parser needs to now what object class names to look for and in which order to map them to integers. # classes [background, #            aeroplane, bicycle, bird, boat, #            bottle, bus, car, cat, #            chair, cow, diningtable, dog, #            horse, motorbike, person, pottedplant, #            sheep, sofa, train, tvmonitor] classes [background,neg]#类的名称此时要加上background train_dataset.parse_xml(images_dirs[VOC_2007_images_dir],                         image_set_filenames[VOC_2007_trainval_image_set_filename],                         annotations_dirs[VOC_2007_annotations_dir],                         classesclasses,                         include_classesall,                         exclude_truncatedFalse,                         exclude_difficultFalse,                         retFalse) val_dataset.parse_xml(images_dirs[VOC_2007_images_dir],                       image_set_filenames[VOC_2007_test_image_set_filename],                       annotations_dirs[VOC_2007_annotations_dir],                       classesclasses,                       include_classesall,                       exclude_truncatedFalse,                       exclude_difficultTrue,                       retFalse) # Optional: Convert the dataset into an HDF5 dataset. This will require more disk space, but will # speed up the training. Doing this is not relevant in case you activated the load_images_into_memory # option in the constructor, because in that cas the images are in memory already anyway. If you dont # want to create HDF5 datasets, comment out the subsequent two function calls. train_dataset.create_hdf5_dataset(file_pathdataset_pascal_voc_0712_trainval.h5,                                   resizeFalse,                                   variable_image_sizeTrue,                                   verboseTrue) val_dataset.create_hdf5_dataset(file_pathdataset_pascal_voc_07_test.h5,                                 resizeFalse,                                 variable_image_sizeTrue,                                 verboseTrue) # 3: Set the batch size. batch_size 8 # Change the batch size if you like, or if you run into GPU memory issues. # 4: Set the image transformations for pre-processing and data augmentation options. # For the training generator: ssd_data_augmentation SSDDataAugmentation(img_heightimg_height,                                             img_widthimg_width,                                             backgroundmean_color) # For the validation generator: convert_to_3_channels ConvertTo3Channels() resize Resize(heightimg_height, widthimg_width) # 5: Instantiate an encoder that can encode ground truth labels into the format needed by the SSD loss function. # The encoder constructor needs the spatial dimensions of the models predictor layers to create the anchor boxes. predictor_sizes [model.get_layer(conv4_3_norm_mbox_conf).output_shape[1:3],                    model.get_layer(fc7_mbox_conf).output_shape[1:3],                    model.get_layer(conv6_2_mbox_conf).output_shape[1:3],                    model.get_layer(conv7_2_mbox_conf).output_shape[1:3],                    model.get_layer(conv8_2_mbox_conf).output_shape[1:3],                    model.get_layer(conv9_2_mbox_conf).output_shape[1:3]] ssd_input_encoder SSDInputEncoder(img_heightimg_height,                                     img_widthimg_width,                                     n_classesn_classes,                                     predictor_sizespredictor_sizes,                                     scalesscales,                                     aspect_ratios_per_layeraspect_ratios,                                     two_boxes_for_ar1two_boxes_for_ar1,                                     stepssteps,                                     offsetsoffsets,                                     clip_boxesclip_boxes,                                     variancesvariances,                                     matching_typemulti,                                     pos_iou_threshold0.5,                                     neg_iou_limit0.5,                                     normalize_coordsnormalize_coords) # 6: Create the generator handles that will be passed to Keras fit_generator() function. train_generator train_dataset.generate(batch_sizebatch_size,                                          shuffleTrue,                                          transformations[ssd_data_augmentation],                                          label_encoderssd_input_encoder,                                          returns{processed_images,                                                   encoded_labels},                                          keep_images_without_gtFalse) val_generator val_dataset.generate(batch_sizebatch_size,                                      shuffleFalse,                                      transformations[convert_to_3_channels,                                                       resize],                                      label_encoderssd_input_encoder,                                      returns{processed_images,                                               encoded_labels},                                      keep_images_without_gtFalse) # Get the number of samples in the training and validations datasets. train_dataset_size train_dataset.get_dataset_size() val_dataset_size   val_dataset.get_dataset_size() print(Number of images in the training dataset:\t{:6}.format(train_dataset_size)) print(Number of images in the validation dataset:\t{:6}.format(val_dataset_size)) print(cuiwei) def lr_schedule(epoch):#通过回调函数设置学习率     if epoch 80:         return 0.0001     elif epoch 100:         return 0.0001     else:         return 0.00001 # Define model callbacks. # TODO: Set the filepath under which you want to save the model. model_checkpoint ModelCheckpoint(filepathssd300_model_liehen_expand.h5,#模型保存名称                                    monitorval_loss,                                    verbose1,                                    save_best_onlyTrue,                                    save_weights_onlyFalse,                                    modeauto,                                    period1) #model_checkpoint.best csv_logger CSVLogger(filenamessd300_pascal_0712_training_log.csv,                        separator,,                        appendTrue) learning_rate_scheduler LearningRateScheduler(schedulelr_schedule,                                                 verbose1) terminate_on_nan TerminateOnNaN() callbacks [model_checkpoint,              csv_logger,              learning_rate_scheduler,              terminate_on_nan] # If youre resuming a previous training, set initial_epoch and final_epoch accordingly. initial_epoch   0 final_epoch     20 steps_per_epoch 80 history model.fit_generator(generatortrain_generator,                               steps_per_epochsteps_per_epoch,                               epochsfinal_epoch,                               callbackscallbacks,                               validation_dataval_generator,                               validation_stepsceil(val_dataset_size/batch_size),                               initial_epochinitial_epoch) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 五、测试数据 训练完成后对模型进行测试test_ssd300.py文件源代码如下 from keras import backend as K from keras.models import load_model from keras.preprocessing import image from keras.optimizers import Adam from imageio import imread import numpy as np from matplotlib import pyplot as plt from models.keras_ssd300 import ssd_300 from keras_loss_function.keras_ssd_loss import SSDLoss from keras_layers.keras_layer_AnchorBoxes import AnchorBoxes from keras_layers.keras_layer_DecodeDetections import DecodeDetections from keras_layers.keras_layer_DecodeDetectionsFast import DecodeDetectionsFast from keras_layers.keras_layer_L2Normalization import L2Normalization from ssd_encoder_decoder.ssd_output_decoder import decode_detections, decode_detections_fast from data_generator.object_detection_2d_data_generator import DataGenerator from data_generator.object_detection_2d_photometric_ops import ConvertTo3Channels from data_generator.object_detection_2d_geometric_ops import Resize from data_generator.object_detection_2d_misc_utils import apply_inverse_transforms import cv2 # Set the image size. img_height 300 img_width 300 # # TODO: Set the path to the .h5 file of the model to be loaded. # # model_path ssd300_model.h5 # model_path VGG_VOC0712Plus_SSD_300x300_iter_240000.h5 # # We need to create an SSDLoss object in order to pass that to the model loader. # ssd_loss SSDLoss(neg_pos_ratio3, n_neg_min0, alpha1.0) # # K.clear_session() # Clear previous models from memory. # # model load_model(model_path, custom_objects{AnchorBoxes: AnchorBoxes, #                                                L2Normalization: L2Normalization, #                                                DecodeDetections: DecodeDetections, #                                                compute_loss: ssd_loss.compute_loss}) K.clear_session() # Clear previous models from memory. model ssd_300(image_size(img_height, img_width, 3),                 n_classes1,                 modeinference,                 l2_regularization0.0005,                 scales[0.1, 0.2, 0.37, 0.54, 0.71, 0.88, 1.05], # The scales for MS COCO are [0.07, 0.15, 0.33, 0.51, 0.69, 0.87, 1.05]                 aspect_ratios_per_layer[[1.0, 2.0, 0.5],                                          [1.0, 2.0, 0.5, 3.0, 1.0/3.0],                                          [1.0, 2.0, 0.5, 3.0, 1.0/3.0],                                          [1.0, 2.0, 0.5, 3.0, 1.0/3.0],                                          [1.0, 2.0, 0.5],                                          [1.0, 2.0, 0.5]],                 two_boxes_for_ar1True,                 steps[8, 16, 32, 64, 100, 300],                 offsets[0.5, 0.5, 0.5, 0.5, 0.5, 0.5],                 clip_boxesFalse,                 variances[0.1, 0.1, 0.2, 0.2],                 normalize_coordsTrue,                 subtract_mean[123, 117, 104],                 swap_channels[2, 1, 0],                 confidence_thresh0.5,                 iou_threshold0.45,                 top_k200,                 nms_max_output_size400) # 2: Load the trained weights into the model. # TODO: Set the path of the trained weights. # weights_path VGG_VOC0712Plus_SSD_300x300_iter_240000.h5 # weights_path ssd300_model_liehen_small.h5 # weights_path ssd300_model_liehen_expand.h5 weights_path ssd300_model_liehen.h5 model.load_weights(weights_path, by_nameTrue) # 3: Compile the model so that Keras wont complain the next time you load it. adam Adam(lr0.001, beta_10.9, beta_20.999, epsilon1e-08, decay0.0) ssd_loss SSDLoss(neg_pos_ratio3, alpha1.0) model.compile(optimizeradam, lossssd_loss.compute_loss) model.summary() orig_images [] # Store the images here. input_images [] # Store resized versions of the images here. # Well only load one image in this example. # img_path VOC2007/JPEGImages/16.jpg img_pathF:/Data/crack image/ChallengeDataset/ChallengeDataset/train/neg/428.jpg image_opencvcv2.imread(img_path) # img_pathVOCtest_06-Nov-2007/VOCdevkit/VOC2007/JPEGImages/000001.jpg orig_images.append(imread(img_path)) img image.load_img(img_path, target_size(img_height, img_width)) img image.img_to_array(img) input_images.append(img) input_images np.array(input_images) #对新的图像进行预测 y_pred model.predict(input_images) # confidence_threshold 0 y_pred_thresh [y_pred[k][y_pred[k,:,1] confidence_threshold] for k in range(y_pred.shape[0])] np.set_printoptions(precision2, suppressTrue, linewidth90) print(Predicted boxes:\n) print(   class   conf xmin   ymin   xmax   ymax) print(y_pred_thresh[0]) # Display the image and draw the predicted boxes onto it. # Set the colors for the bounding boxes colors plt.cm.hsv(np.linspace(0, 1, 21)).tolist() # classes [background, #            aeroplane, bicycle, bird, boat, #            bottle, bus, car, cat, #            chair, cow, diningtable, dog, #            horse, motorbike, person, pottedplant, #            sheep, sofa, train, tvmonitor] classes[background,neg] plt.figure(figsize(20,12)) plt.imshow(orig_images[0]) current_axis plt.gca() for box in y_pred_thresh[0]:     # Transform the predicted bounding boxes for the 300x300 image to the original image dimensions.     xmin box[2] * orig_images[0].shape[1] / img_width     ymin box[3] * orig_images[0].shape[0] / img_height     xmax box[4] * orig_images[0].shape[1] / img_width     ymax box[5] * orig_images[0].shape[0] / img_height     color colors[int(box[0])]     label {}: {:.2f}.format(classes[int(box[0])], box[1])     current_axis.add_patch(plt.Rectangle((xmin, ymin), xmax-xmin, ymax-ymin, colorcolor, fillFalse, linewidth2))     current_axis.text(xmin, ymin, label, sizex-large, colorwhite, bbox{facecolor:color, alpha:1.0})     cv2.putText(image_opencv, label, (int(xmin), int(ymin)-10), cv2.FONT_HERSHEY_COMPLEX, 0.8, (255, 255, 0), 1)     cv2.rectangle(image_opencv, (int(xmin), int(ymin)), (int(xmax), int(ymax)), (0, 255, 0), 2) cv2.namedWindow(Canvas,0) cv2.imshow(Canvas, image_opencv) cv2.waitKey(0) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 测试结果 六、源代码和数据 SSD裂纹检测源代码https://download.csdn.net/download/qq_29462849/10748838 裂纹图像数据https://download.csdn.net/download/qq_29462849/10748828 七、需要注意的问题 该SSD源代码在设置学习率的时候需要设置小些本代码中设置0.0001设置过大会导致梯度爆炸这个亲身体验过~~~ 在制作数据的时候需要把前景和背景区别开来对裂纹比较明显的特征检测框可以设置小些而且尽量不要包含无关的背景对裂纹不明显的特征需要设置检测框大些不明显的特征只能通过长裂纹来和背景区分。 八、另外一个思路 如果不需要定位裂纹在图像中的位置只需要识别整幅图像是否有裂纹并以此来做识别分类。有一个思路很好那就是对整幅图像进行切分比如在y轴方向做切割切割成四份图像如下图所示。因为裂纹大都是处于同一水平位置上的研y轴进行切割可以最大程度保留完整的裂纹。把切割好的裂纹和非裂纹图像挑选出来分别给予标签这样送进分类网络比如DenseNet中进行训练。 网络训练完成后就可以对场景图像进行识别了只不过在识别的过程中同样需要对场景图像进行切分然后对每一个切片图像进行分类识别。判断依据只要场景图像的切片图像有一个被分类为有裂纹的那该场景图像就是有裂纹的。 关于切片的方向和数量可以根据自己工况的需要来完成不一定非得是四份。 如有疑问欢迎加企鹅516999497交流~ ———————————————— 版权声明本文为CSDN博主「Oliver Cui」的原创文章遵循 CC 4.0 BY-SA 版权协议转载请附上原文出处链接及本声明。 原文链接https://blog.csdn.net/qq_29462849/article/details/83472430
http://www.huolong8.cn/news/107781/

相关文章:

  • 衡阳哪有做网站推广的工厂生产管理系统
  • 做汽配的 哪一个网站比较好宁波育才建设教育集团网站
  • 滦南县建设局网站网站开发能进入无形资产吗
  • 专业做网站建设公司排名昆明网站建设SEO公司
  • 小红书网站开发形式选择广州房地产网站建设
  • 医疗门户网站管理系统钢构网架公司
  • 建站宝盒下载市场营销方案
  • php 调试网站北京网站建设专家
  • 弹出全屏视频网站怎么做wordpress网站主机名
  • 做简图的网站手机怎么打开自己做的网站
  • 0基础建站网站搭建教程泰国网站建设
  • 凡科可以做淘客网站吗合肥中科大网站开发
  • 网站建设无锡海之睿怎么做局域网网站
  • 太原站还建综合楼建设工程合同性质上属于实践合同
  • 做网站租什么服务器商城网站的设计风格
  • 织梦网站logo更改北京代理记账公司
  • 做网站网站代理犯法吗佛山优化网站方法
  • 网站优化公司大家好宣传推广图片
  • 合肥企业网站建设公司建筑a证
  • 如何建立自己的超市网站软文平台有哪些
  • 重庆做网站及公众号公司福田企业网站优化最好的方法
  • 乐清公司网站建设电子商务网站架构
  • 免费自己制作网站如何在百度做自己公司的网站
  • 怎么看网站的访问量做公司网站流程
  • 淄博网站建设推广乐达建立网站的链接结构有哪几种形式?简述其各自的优缺点
  • 商城网站建设方案流程海口澄迈县建设局网站
  • 网站建设的发展趋势单位网站建设汇报
  • 微信公众号怎么创建文章seo基本步骤
  • 网站服务器建设网站可以做二维码吗
  • 虚拟机如何做网站云南网站建设企业