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

天津平台网站建设方案装修公司加盟哪个平台

天津平台网站建设方案,装修公司加盟哪个平台,云服务器做网站好吗,中国城乡与住房建设部网站首页目录 一、简介 二、代码结构 三、环境要求 四、数据增强类 4.1 调整增强概率和参数 4.2 增强方法参数 五、数据增强主函数 六、主函数 效果展示 完整代码 一、简介 在目标检测任务中,数据增强是一种关键技术,通过对原始图像应用多种变换来增加…

目录

一、简介

二、代码结构

三、环境要求

四、数据增强类

4.1 调整增强概率和参数

4.2 增强方法参数

五、数据增强主函数

六、主函数

效果展示

完整代码


一、简介

  在目标检测任务中,数据增强是一种关键技术,通过对原始图像应用多种变换来增加训练数据的多样性,从而提升模型的泛化能力。本教程将详细讲解一个基于 Python 和 OpenCV 的图像数据增强工具,专为目标检测任务设计。该工具支持旋转、平移、裁剪、翻转、亮度调整、噪声添加和 Cutout 等增强方法,并能自动调整边界框(Bounding Box)坐标。

  以下是对代码的功能、结构和使用方法的全面解析,适合有一定 Python 和 OpenCV 基础的读者。

二、代码结构

  代码主要由以下模块组成:

  显示图片函数:show_pic 用于可视化图像和边界框。

  数据增强类:DataAugmentForObjectDetection 包含多种增强方法。

  XML 解析工具类:ToolHelper 用于解析 XML 文件并保存增强结果。

  主函数:通过命令行参数配置路径并执行增强。

三、环境要求

运行代码需要以下库:

  • Python 3.x
  • OpenCV (cv2)
  • NumPy
  • SciKit-Image (skimage)
  • lxml
  • xml.etree.ElementTree (ET)

安装方法:

pip install opencv-python numpy scikit-image lxml

四、数据增强类

class DataAugmentForObjectDetection():def __init__(self, rotation_rate=0.5, max_rotation_angle=5,crop_rate=0.5, shift_rate=0.5, change_light_rate=0.5,add_noise_rate=0.5, flip_rate=0.5,cutout_rate=0.5, cut_out_length=50, cut_out_holes=1, cut_out_threshold=0.5,is_addNoise=False, is_changeLight=False, is_cutout=False, is_rotate_img_bbox=False,is_crop_img_bboxes=True, is_shift_pic_bboxes=False, is_filp_pic_bboxes=False)

在 DataAugmentForObjectDetection 类中,可以通过 is_xxx 参数控制是否启用某种增强方法。

示例:启用旋转和翻转:

dataAug = DataAugmentForObjectDetection(is_rotate_img_bbox=True,is_filp_pic_bboxes=True
)

4.1 调整增强概率和参数

每种增强方法都有对应的概率参数(xxx_rate,范围 0 到 1)和增强强度参数。例如:

旋转:启用旋转,设置旋转概率为 0.8,最大旋转角度为 10 度:

dataAug = DataAugmentForObjectDetection(is_rotate_img_bbox=True,rotation_rate=0.8,max_rotation_angle=10
)

翻转:启用翻转,设置翻转概率为 0.6:

dataAug = DataAugmentForObjectDetection(is_filp_pic_bboxes=True,flip_rate=0.6
)

4.2 增强方法参数

以下是部分增强方法的关键参数:

  • 旋转
    • rotation_rate:旋转概率。
    • max_rotation_angle:最大旋转角度(度)。
  • 裁剪
    • crop_rate:裁剪概率。
  • 平移
    • shift_rate:平移概率。
  • 亮度调整
    • change_light_rate:调整亮度概率。
  • 噪声
    • add_noise_rate:添加噪声概率。
  • Cutout
    • cutout_rate:Cutout 概率。
    • cut_out_length:挖空区域边长。
    • cut_out_holes:挖空区域数量。

五、数据增强主函数

def dataAugment(self, img, bboxes):change_num = 0while change_num < 1:if self.is_rotate_img_bbox and random.random() > self.rotation_rate:change_num += 1angle = random.uniform(-self.max_rotation_angle, self.max_rotation_angle)scale = random.uniform(0.7, 0.8)img, bboxes = self._rotate_img_bbox(img, bboxes, angle, scale)# 其他增强方法类似return img, bboxes

 随机应用一种或多种增强方法。通过随机数和概率参数决定增强类型,确保至少应用一种增强。

六、主函数

if __name__ == '__main__':need_aug_num = 1is_endwidth_dot = TruedataAug = DataAugmentForObjectDetection()toolhelper = ToolHelper()parser = argparse.ArgumentParser()parser.add_argument('--source_img_path', type=str, default="E:/AIrailway/100_raind/images/")parser.add_argument('--source_xml_path', type=str, default="E:/AIrailway/100_raind/voc_labels/")parser.add_argument('--save_img_path', type=str, default="E:/AIrailway/100_raind/images/images_crop/")parser.add_argument('--save_xml_path', type=str, default="E:/AIrailway/100_raind/images/voc_labels_crop/")args = parser.parse_args()

--source_img_path:源图像文件夹路径。

--source_xml_path:源 XML 文件夹路径。

--save_img_path:保存增强图像的文件夹路径。

--save_xml_path:保存增强 XML 的文件夹路径。

默认情况下,每张图像增强 1 次。你可以通过修改脚本中的 need_aug_num 变量调整增强次数,例如:

need_aug_num = 3  # 每张图像增强 3 次

效果展示

完整代码

# -*- coding=utf-8 -*-import time
import random
import copy
import cv2
import os
import math
import numpy as np
from skimage.util import random_noise
from lxml import etree, objectify
import xml.etree.ElementTree as ET
import argparse# 显示图片
def show_pic(img, bboxes=None):'''输入:img:图像arraybboxes:图像的所有boudning box list, 格式为[[x_min, y_min, x_max, y_max]....]names:每个box对应的名称'''for i in range(len(bboxes)):bbox = bboxes[i]x_min = bbox[0]y_min = bbox[1]x_max = bbox[2]y_max = bbox[3]cv2.rectangle(img, (int(x_min), int(y_min)), (int(x_max), int(y_max)), (0, 255, 0), 3)cv2.namedWindow('pic', 0)  # 1表示原图cv2.moveWindow('pic', 0, 0)cv2.resizeWindow('pic', 1200, 800)  # 可视化的图片大小cv2.imshow('pic', img)cv2.waitKey(0)cv2.destroyAllWindows()# 图像均为cv2读取
class DataAugmentForObjectDetection():def __init__(self, rotation_rate=0.5, max_rotation_angle=5,crop_rate=0.5, shift_rate=0.5, change_light_rate=0.5,add_noise_rate=0.5, flip_rate=0.5,cutout_rate=0.5, cut_out_length=50, cut_out_holes=1, cut_out_threshold=0.5,is_addNoise=False, is_changeLight=False, is_cutout=False, is_rotate_img_bbox=False,is_crop_img_bboxes=True, is_shift_pic_bboxes=False, is_filp_pic_bboxes=False):# 配置各个操作的属性self.rotation_rate = rotation_rateself.max_rotation_angle = max_rotation_angleself.crop_rate = crop_rateself.shift_rate = shift_rateself.change_light_rate = change_light_rateself.add_noise_rate = add_noise_rateself.flip_rate = flip_rateself.cutout_rate = cutout_rateself.cut_out_length = cut_out_lengthself.cut_out_holes = cut_out_holesself.cut_out_threshold = cut_out_threshold# 是否使用某种增强方式self.is_addNoise = is_addNoiseself.is_changeLight = is_changeLightself.is_cutout = is_cutoutself.is_rotate_img_bbox = is_rotate_img_bboxself.is_crop_img_bboxes = is_crop_img_bboxesself.is_shift_pic_bboxes = is_shift_pic_bboxesself.is_filp_pic_bboxes = is_filp_pic_bboxes# ----1.加噪声---- #def _addNoise(self, img):'''输入:img:图像array输出:加噪声后的图像array,由于输出的像素是在[0,1]之间,所以得乘以255'''# return cv2.GaussianBlur(img, (11, 11), 0)# return random_noise(img, mode='pepper', seed=int(time.time()), clip=True) * 255v = random.uniform(0, 0.2)return random_noise(img, mode='gaussian', mean=0, var=v) * 255# ---2.调整亮度--- #def _changeLight(self, img):# alpha = random.uniform(0.35, 1)alpha = random.uniform(0.3, 2)# alpha = random.uniform(0, 0.5)blank = np.zeros(img.shape, img.dtype)return cv2.addWeighted(img, alpha, blank, 1 - alpha, 0)# ---3.cutout--- #def _cutout(self, img, bboxes, length=100, n_holes=1, threshold=0.5):'''原版本:https://github.com/uoguelph-mlrg/Cutout/blob/master/util/cutout.pyRandomly mask out one or more patches from an image.Args:img : a 3D numpy array,(h,w,c)bboxes : 框的坐标n_holes (int): Number of patches to cut out of each image.length (int): The length (in pixels) of each square patch.'''def cal_iou(boxA, boxB):'''boxA, boxB为两个框,返回iouboxB为bouding box'''# determine the (x, y)-coordinates of the intersection rectanglexA = max(boxA[0], boxB[0])yA = max(boxA[1], boxB[1])xB = min(boxA[2], boxB[2])yB = min(boxA[3], boxB[3])if xB <= xA or yB <= yA:return 0.0# compute the area of intersection rectangleinterArea = (xB - xA + 1) * (yB - yA + 1)# compute the area of both the prediction and ground-truth# rectanglesboxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)iou = interArea / float(boxBArea)return iou# 得到h和wif img.ndim == 3:h, w, c = img.shapeelse:_, h, w, c = img.shapemask = np.ones((h, w, c), np.float32)for n in range(n_holes):chongdie = True  # 看切割的区域是否与box重叠太多while chongdie:y = np.random.randint(h)x = np.random.randint(w)y1 = np.clip(y - length // 2, 0,h)  # numpy.clip(a, a_min, a_max, out=None), clip这个函数将将数组中的元素限制在a_min, a_max之间,大于a_max的就使得它等于 a_max,小于a_min,的就使得它等于a_miny2 = np.clip(y + length // 2, 0, h)x1 = np.clip(x - length // 2, 0, w)x2 = np.clip(x + length // 2, 0, w)chongdie = Falsefor box in bboxes:if cal_iou([x1, y1, x2, y2], box) > threshold:chongdie = Truebreakmask[y1: y2, x1: x2, :] = 0.img = img * maskreturn img# ---4.旋转--- #def _rotate_img_bbox(self, img, bboxes, angle=5, scale=1.):'''参考:https://blog.csdn.net/u014540717/article/details/53301195crop_rate输入:img:图像array,(h,w,c)bboxes:该图像包含的所有boundingboxs,一个list,每个元素为[x_min, y_min, x_max, y_max],要确保是数值angle:旋转角度scale:默认1输出:rot_img:旋转后的图像arrayrot_bboxes:旋转后的boundingbox坐标list'''# 旋转图像w = img.shape[1]h = img.shape[0]# 角度变弧度rangle = np.deg2rad(angle)  # angle in radians# now calculate new image width and heightnw = (abs(np.sin(rangle) * h) + abs(np.cos(rangle) * w)) * scalenh = (abs(np.cos(rangle) * h) + abs(np.sin(rangle) * w)) * scale# ask OpenCV for the rotation matrixrot_mat = cv2.getRotationMatrix2D((nw * 0.5, nh * 0.5), angle, scale)# calculate the move from the old center to the new center combined# with the rotationrot_move = np.dot(rot_mat, np.array([(nw - w) * 0.5, (nh - h) * 0.5, 0]))# the move only affects the translation, so update the translationrot_mat[0, 2] += rot_move[0]rot_mat[1, 2] += rot_move[1]# 仿射变换rot_img = cv2.warpAffine(img, rot_mat, (int(math.ceil(nw)), int(math.ceil(nh))), flags=cv2.INTER_LANCZOS4)# 矫正bbox坐标# rot_mat是最终的旋转矩阵# 获取原始bbox的四个中点,然后将这四个点转换到旋转后的坐标系下rot_bboxes = list()for bbox in bboxes:xmin = bbox[0]ymin = bbox[1]xmax = bbox[2]ymax = bbox[3]point1 = np.dot(rot_mat, np.array([(xmin + xmax) / 2, ymin, 1]))point2 = np.dot(rot_mat, np.array([xmax, (ymin + ymax) / 2, 1]))point3 = np.dot(rot_mat, np.array([(xmin + xmax) / 2, ymax, 1]))point4 = np.dot(rot_mat, np.array([xmin, (ymin + ymax) / 2, 1]))# 合并np.arrayconcat = np.vstack((point1, point2, point3, point4))# 改变array类型concat = concat.astype(np.int32)# 得到旋转后的坐标rx, ry, rw, rh = cv2.boundingRect(concat)rx_min = rxry_min = ryrx_max = rx + rwry_max = ry + rh# 加入list中rot_bboxes.append([rx_min, ry_min, rx_max, ry_max])return rot_img, rot_bboxes# ---5.裁剪--- #def _crop_img_bboxes(self, img, bboxes):'''裁剪后的图片要包含所有的框输入:img:图像arraybboxes:该图像包含的所有boundingboxs,一个list,每个元素为[x_min, y_min, x_max, y_max],要确保是数值输出:crop_img:裁剪后的图像arraycrop_bboxes:裁剪后的bounding box的坐标list'''# 裁剪图像w = img.shape[1]h = img.shape[0]x_min = w  # 裁剪后的包含所有目标框的最小的框x_max = 0y_min = hy_max = 0for bbox in bboxes:x_min = min(x_min, bbox[0])y_min = min(y_min, bbox[1])x_max = max(x_max, bbox[2])y_max = max(y_max, bbox[3])d_to_left = x_min  # 包含所有目标框的最小框到左边的距离d_to_right = w - x_max  # 包含所有目标框的最小框到右边的距离d_to_top = y_min  # 包含所有目标框的最小框到顶端的距离d_to_bottom = h - y_max  # 包含所有目标框的最小框到底部的距离# 随机扩展这个最小框crop_x_min = int(x_min - random.uniform(0, d_to_left))crop_y_min = int(y_min - random.uniform(0, d_to_top))crop_x_max = int(x_max + random.uniform(0, d_to_right))crop_y_max = int(y_max + random.uniform(0, d_to_bottom))# 随机扩展这个最小框 , 防止别裁的太小# crop_x_min = int(x_min - random.uniform(d_to_left//2, d_to_left))# crop_y_min = int(y_min - random.uniform(d_to_top//2, d_to_top))# crop_x_max = int(x_max + random.uniform(d_to_right//2, d_to_right))# crop_y_max = int(y_max + random.uniform(d_to_bottom//2, d_to_bottom))# 确保不要越界crop_x_min = max(0, crop_x_min)crop_y_min = max(0, crop_y_min)crop_x_max = min(w, crop_x_max)crop_y_max = min(h, crop_y_max)crop_img = img[crop_y_min:crop_y_max, crop_x_min:crop_x_max]# 裁剪boundingbox# 裁剪后的boundingbox坐标计算crop_bboxes = list()for bbox in bboxes:crop_bboxes.append([bbox[0] - crop_x_min, bbox[1] - crop_y_min, bbox[2] - crop_x_min, bbox[3] - crop_y_min])return crop_img, crop_bboxes# ---6.平移--- #def _shift_pic_bboxes(self, img, bboxes):'''平移后的图片要包含所有的框输入:img:图像arraybboxes:该图像包含的所有boundingboxs,一个list,每个元素为[x_min, y_min, x_max, y_max],要确保是数值输出:shift_img:平移后的图像arrayshift_bboxes:平移后的bounding box的坐标list'''# 平移图像w = img.shape[1]h = img.shape[0]x_min = w  # 裁剪后的包含所有目标框的最小的框x_max = 0y_min = hy_max = 0for bbox in bboxes:x_min = min(x_min, bbox[0])y_min = min(y_min, bbox[1])x_max = max(x_max, bbox[2])y_max = max(y_max, bbox[3])d_to_left = x_min  # 包含所有目标框的最大左移动距离d_to_right = w - x_max  # 包含所有目标框的最大右移动距离d_to_top = y_min  # 包含所有目标框的最大上移动距离d_to_bottom = h - y_max  # 包含所有目标框的最大下移动距离x = random.uniform(-(d_to_left - 1) / 3, (d_to_right - 1) / 3)y = random.uniform(-(d_to_top - 1) / 3, (d_to_bottom - 1) / 3)M = np.float32([[1, 0, x], [0, 1, y]])  # x为向左或右移动的像素值,正为向右负为向左; y为向上或者向下移动的像素值,正为向下负为向上shift_img = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))#  平移boundingboxshift_bboxes = list()for bbox in bboxes:shift_bboxes.append([bbox[0] + x, bbox[1] + y, bbox[2] + x, bbox[3] + y])return shift_img, shift_bboxes# ---7.镜像--- #def _filp_pic_bboxes(self, img, bboxes):'''平移后的图片要包含所有的框输入:img:图像arraybboxes:该图像包含的所有boundingboxs,一个list,每个元素为[x_min, y_min, x_max, y_max],要确保是数值输出:flip_img:平移后的图像arrayflip_bboxes:平移后的bounding box的坐标list'''# 翻转图像flip_img = copy.deepcopy(img)h, w, _ = img.shapesed = random.random()if 0 < sed < 0.33:  # 0.33的概率水平翻转,0.33的概率垂直翻转,0.33是对角反转flip_img = cv2.flip(flip_img, 0)  # _flip_xinver = 0elif 0.33 < sed < 0.66:flip_img = cv2.flip(flip_img, 1)  # _flip_yinver = 1else:flip_img = cv2.flip(flip_img, -1)  # flip_x_yinver = -1# 调整boundingboxflip_bboxes = list()for box in bboxes:x_min = box[0]y_min = box[1]x_max = box[2]y_max = box[3]if inver == 0:# 0:垂直翻转flip_bboxes.append([x_min, h - y_max, x_max, h - y_min])elif inver == 1:# 1:水平翻转flip_bboxes.append([w - x_max, y_min, w - x_min, y_max])elif inver == -1:# -1:水平垂直翻转flip_bboxes.append([w - x_max, h - y_max, w - x_min, h - y_min])return flip_img, flip_bboxes# 图像增强方法def dataAugment(self, img, bboxes):'''图像增强输入:img:图像arraybboxes:该图像的所有框坐标输出:img:增强后的图像bboxes:增强后图片对应的box'''change_num = 0  # 改变的次数# print('------')while change_num < 1:  # 默认至少有一种数据增强生效if self.is_rotate_img_bbox:if random.random() > self.rotation_rate:  # 旋转change_num += 1angle = random.uniform(-self.max_rotation_angle, self.max_rotation_angle)scale = random.uniform(0.7, 0.8)img, bboxes = self._rotate_img_bbox(img, bboxes, angle, scale)if self.is_shift_pic_bboxes:if random.random() < self.shift_rate:  # 平移change_num += 1img, bboxes = self._shift_pic_bboxes(img, bboxes)if self.is_changeLight:if random.random() > self.change_light_rate:  # 改变亮度change_num += 1img = self._changeLight(img)if self.is_addNoise:if random.random() < self.add_noise_rate:  # 加噪声change_num += 1img = self._addNoise(img)if self.is_cutout:if random.random() < self.cutout_rate:  # cutoutchange_num += 1img = self._cutout(img, bboxes, length=self.cut_out_length, n_holes=self.cut_out_holes,threshold=self.cut_out_threshold)if self.is_filp_pic_bboxes:if random.random() < self.flip_rate:  # 翻转change_num += 1img, bboxes = self._filp_pic_bboxes(img, bboxes)if self.is_crop_img_bboxes:if random.random() < self.crop_rate:  # 裁剪change_num += 1img, bboxes = self._crop_img_bboxes(img, bboxes)return img, bboxes# xml解析工具
class ToolHelper():# 从xml文件中提取bounding box信息, 格式为[[x_min, y_min, x_max, y_max, name]]def parse_xml(self, path):'''输入:xml_path: xml的文件路径输出:从xml文件中提取bounding box信息, 格式为[[x_min, y_min, x_max, y_max, name]]'''tree = ET.parse(path)root = tree.getroot()objs = root.findall('object')coords = list()for ix, obj in enumerate(objs):name = obj.find('name').textbox = obj.find('bndbox')x_min = int(box[0].text)y_min = int(box[1].text)x_max = int(box[2].text)y_max = int(box[3].text)coords.append([x_min, y_min, x_max, y_max, name])return coords# 保存图片结果def save_img(self, file_name, save_folder, img):cv2.imwrite(os.path.join(save_folder, file_name), img)# 保持xml结果def save_xml(self, file_name, save_folder, img_info, height, width, channel, bboxs_info):''':param file_name:文件名:param save_folder:#保存的xml文件的结果:param height:图片的信息:param width:图片的宽度:param channel:通道:return:'''folder_name, img_name = img_info  # 得到图片的信息E = objectify.ElementMaker(annotate=False)anno_tree = E.annotation(E.folder(folder_name),E.filename(img_name),E.path(os.path.join(folder_name, img_name)),E.source(E.database('Unknown'),),E.size(E.width(width),E.height(height),E.depth(channel)),E.segmented(0),)labels, bboxs = bboxs_info  # 得到边框和标签信息for label, box in zip(labels, bboxs):anno_tree.append(E.object(E.name(label),E.pose('Unspecified'),E.truncated('0'),E.difficult('0'),E.bndbox(E.xmin(box[0]),E.ymin(box[1]),E.xmax(box[2]),E.ymax(box[3]))))etree.ElementTree(anno_tree).write(os.path.join(save_folder, file_name), pretty_print=True)if __name__ == '__main__':need_aug_num = 1  # 每张图片需要增强的次数is_endwidth_dot = True  # 文件是否以.jpg或者png结尾dataAug = DataAugmentForObjectDetection()  # 数据增强工具类toolhelper = ToolHelper()  # 工具# 获取相关参数parser = argparse.ArgumentParser()parser.add_argument('--source_img_path', type=str, default="E:/AIrailway/100_raind/images/")parser.add_argument('--source_xml_path', type=str, default="E:/AIrailway/100_raind/voc_labels/")parser.add_argument('--save_img_path', type=str, default="E:/AIrailway/100_raind/images/images_crop/")parser.add_argument('--save_xml_path', type=str, default="E:/AIrailway/100_raind/images/voc_labels_crop/")args = parser.parse_args()source_img_path = args.source_img_path  # 图片原始位置source_xml_path = args.source_xml_path  # xml的原始位置save_img_path = args.save_img_path  # 图片增强结果保存文件save_xml_path = args.save_xml_path  # xml增强结果保存文件# 如果保存文件夹不存在就创建if not os.path.exists(save_img_path):os.mkdir(save_img_path)if not os.path.exists(save_xml_path):os.mkdir(save_xml_path)for parent, _, files in os.walk(source_img_path):files.sort()for file in files:cnt = 0pic_path = os.path.join(parent, file)xml_path = os.path.join(source_xml_path, file[:-4] + '.xml')values = toolhelper.parse_xml(xml_path)  # 解析得到box信息,格式为[[x_min,y_min,x_max,y_max,name]]coords = [v[:4] for v in values]  # 得到框labels = [v[-1] for v in values]  # 对象的标签# 如果图片是有后缀的if is_endwidth_dot:# 找到文件的最后名字dot_index = file.rfind('.')_file_prefix = file[:dot_index]  # 文件名的前缀_file_suffix = file[dot_index:]  # 文件名的后缀img = cv2.imread(pic_path)# show_pic(img, coords)  # 显示原图while cnt < need_aug_num:  # 继续增强auged_img, auged_bboxes = dataAug.dataAugment(img, coords)auged_bboxes_int = np.array(auged_bboxes).astype(np.int32)height, width, channel = auged_img.shape  # 得到图片的属性img_name = '{}_{}{}'.format(_file_prefix, cnt + 1, _file_suffix)  # 图片保存的信息toolhelper.save_img(img_name, save_img_path,auged_img)  # 保存增强图片toolhelper.save_xml('{}_{}.xml'.format(_file_prefix, cnt + 1),save_xml_path, (save_img_path, img_name), height, width, channel,(labels, auged_bboxes_int))  # 保存xml文件# show_pic(auged_img, auged_bboxes)  # 强化后的图print(img_name)cnt += 1  # 继续增强下一张

 

    http://www.dtcms.com/wzjs/786191.html

    相关文章:

  1. 自己有网站做app吗拉新app开发
  2. 怎么找回网站后台密码企业管理体系包含哪些内容
  3. 在线支付网站制作小学网站建设及使用
  4. 哈尔滨网站建设方案维护枣庄网站设计
  5. ps如何做ppt模板下载网站dede做手机网站
  6. 网站备案公司如何 网站优化
  7. 南宁外贸网站建设企业推广产品有什么平台好
  8. 北京建站模板企业软文代写价格
  9. 外包网站建设多少钱腾讯云服务器cvm
  10. 网站不推广如何排名蓝海网站建设
  11. 想建设一个网站什么是网络营销信息
  12. 网站开发设计注册WordPress子目录访问
  13. 类似58同城分类信息网站开发深圳建设注册中心网站
  14. 注册网站用的信用卡Windows wordpress搭建
  15. 二道网站建设wordpress 侧边栏 插件
  16. 织梦做网站的详情网站代运营要多少费用吗
  17. 河源北京网站建设企业危机公关
  18. 淘宝网站建设没法上传怎么办大连企业网站模板
  19. asp手机网站统计代码个人网页制作模板图片代码
  20. 电子商务网站建设基本组成企业管理课程培训
  21. 网站建设与管理职业分析中国电力建设股份有限公司网站
  22. phpcms网站河南5G网站基站建设信息
  23. 淄博网站运营公司建设安全带官方网站
  24. 北京城乡建设和住房门户网站网站开发岗位内容和要求
  25. wordpress缓存seo推广有哪些公司
  26. 企业建站设计达濠网红小吃
  27. 广州网站备案号闵行西安网站建设
  28. 郑州的网站公司哪家好推广代理公司
  29. 服务好的南昌网站制作wordpress 菜单无法保存
  30. WordPress手机站插件域名年费多少网站建设