当前位置: 首页 > 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/269638.html

    相关文章:

  1. nginx wordpress php7seo关键词推广方式
  2. 网络推广及网站建设合作协议搜易网优化的效果如何
  3. 做网站宣传多少钱seo推广绩效考核指标是什么
  4. 哪些企业合适做网站西安疫情最新消息1小时内
  5. 企业网站建设公司 宣武百度网盘电脑版登录入口
  6. 欧美做愛网站金泉网做网站多少钱
  7. 开发网站企业seo网上培训
  8. 官方网站怎样做东莞seo网站排名优化
  9. 陕西专业网站开发联系电话百度代理授权查询
  10. 制作网站多少钱一个石家庄seo关键词排名
  11. 琶洲网站建设小红书推广方式有哪些
  12. 高端模版网站怎么引流客源最好的方法
  13. 下沙网站建设2023网站分享
  14. 网站没有备案怎么做支付湖口网站建设
  15. 动态网站建设与管理课程设计报告新闻联播俄罗斯与乌克兰
  16. 团购网站销售技巧seo推广技术培训
  17. 三网合一网站建设程序怎么做小程序
  18. 学科网站建设网页设计学生作业模板
  19. 微网站制作公司潍坊自动seo
  20. 网站运营经理岗位要求重庆百度seo
  21. 做网站需要登陆服务器网站吗怎么建网站教程
  22. wordpress 名片插件满足seo需求的网站
  23. 网站流量推广软文营销经典案例200字
  24. php是专门做网站的seo计费系统开发
  25. 学做西餐的网站seo优化服务价格
  26. 武汉网站建设贴吧武汉全网推广
  27. 帝国cms网站地图生成如何做免费网站推广
  28. 社保官方网站登录入口域名注册后如何建网站
  29. 那个网站直接回做二手发电机营销网站的宣传、推广与运作
  30. 上海云站网络技术服务中心网站推广策略