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

thinkphp 企业网站源码开发一款app需要多少人

thinkphp 企业网站源码,开发一款app需要多少人,网件路由器恢复出厂设置,网站首页原型图推荐#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 本文将介绍一种非常有吸引力的机器学习训练数据的替代方案#xff0c;用于为给定的特定应用程序收集数据。 无论应用程序类型如何#xff0c;这篇博文都旨在向读者展示使用 Blender 等开源资源生成合成数据#xff08;S… 推荐用 NSDT编辑器 快速搭建可编程3D场景 本文将介绍一种非常有吸引力的机器学习训练数据的替代方案用于为给定的特定应用程序收集数据。 无论应用程序类型如何这篇博文都旨在向读者展示使用 Blender 等开源资源生成合成数据Synthetic Data的潜力。 事实上对于现实生活中的数据而言在短时间内收集数据并对其进行标记的需求尚未得到完全解决。 这就是为什么为了解决这个问题我们必须转向合成数据生成通过适量的代码它可以提供稍后训练深度学习模型所需的标签和特征。 在本例中我们专注于对象识别问题并使用 Blender 及其脚本功能生成数据。 1、项目概况 对于这个项目我们将生成数据来识别下图中所示的木制玩具。 为了做到这一点我们将创建一种算法以与图片中相同的配置拍摄所有对象的照片并输出与每个图像中每个对象的位置的边界框相对应的标签。 我们希望识别的类如下 Pink flowerBlue squareGreen starYellow hexagonOrange lozengePink ovalBlue rectangleGreen circleOrange triangle上述算法将在渲染软件Blender中用Python实现。 Blender 是一款开源软件用于从动画到产品设计等多种渲染应用程序。 该软件将允许创建上面看到的对象的真实渲染同时也允许我们访问每个对象的位置这是完成标记的关键功能。 本文的 Blender 文件、整个代码和所有必要的资源都可以在这里下载。 2、Blender场景设置 无论你想要识别哪个对象为了生成合成数据来训练其识别器我们都必须在 Blender 中表示这个或这些对象。 因此我们必须创建和设置一个与实际场景最相似的场景我们通常会在其中找到我们想要识别的对象。 为了解释如何做到这一点在本节中我们将引导读者完成设置与自动生成数据的脚本兼容的场景所需的主要步骤。 2.1 CAD模型导入 第一步包括将我们想要识别的对象导入或创建到 Blender 中。 在我们的例子中我们决定在 Catia V5 中创建 CAD 模型然后将其导入为 .stl。 也可以在 Blender 中从头开始创建模型但是由于我们更熟悉 Catia因此我们决定在该软件上执行此操作。 Catia 环境中包含我们要识别的对象的集合 然后我们继续打开 Blender 开始导入我们想要识别的每个 .stl 对象。 我们单击“文件”窗口然后单击“导入”最后选择“Stl(.stl)”选项以导入之前从 Catia V5 导出的模型。 Blender STL 文件导入 在完成 STL 导入之前我们必须指定比例为 0.001因为 Blender 的工作单位为米而 Catia 的工作单位为毫米。 这使我们模型的单位缩放到 Blender 参考系统因此在后期阶段与导入的模型相比相机具有成比例的尺寸。 在 Blender 中导入之前缩放 STL 模型 将每个对象导入 Blender 环境后请确保创建一个平面来描绘场景的表面。 另外请确保命名“场景集合”菜单中的每个对象这些对象可以在下图中的右侧面板中看到。 确保这些名称易于书写、简短并且如果名称包含两个单词则用下划线连接。 所有对象均已导入并命名的最终场景 2.2 场景定义 导入模型后我们将继续定义整个场景以使其看起来最真实。 场景越真实我们的训练数据就越好我们的算法就越能识别我们训练它检测的现实生活中的物体。 因此我们首先通过选择每个对象并进入右侧面板中的“材质”选项来定义每个对象的材质如下所示。 调整对象外观的三个关键参数是基色、镜面反射和粗糙度。 需要调整后两者来定义物体是吸收还是反射光是有光泽的还是无光泽的。 Blender 对象材质定义面板 此外如果需要可以将纹理添加到场景中的对象。 这些纹理可以从图像中导入并从图像中施加基色。 对于我们的项目纹理被添加到地板和容纳物体的平台上。 将纹理指定给 Blender 对象 接下来定义灯光可以通过复制并粘贴第一个灯光来创建第二个灯光该灯光是在 Blender 场景启动时自动创建的。 至于对象请确保为这些灯光分配名称例如 light1 和 light2以便你在进入脚本部分后可以在代码中轻松调用它们。 这两个灯光最重要的参数是“功率”参数一旦我们进入脚本部分它将使我们能够调整灯光的强度。 如下图右下角我们可以看到“灯光属性”面板打开功率参数为16W。 Blender场景及灯光 定义了所有材质并添加了适合目的的灯光后我们可以通过单击下图中用绿色圈出的右上角按钮来继续可视化渲染。 这允许我们在渲染模式下访问视口着色。 渲染后的对象 现在场景搭建完毕 2.3 相机设置 设置 Blender 场景以开始编写脚本的最后一步是设置相机 这一步实际上至关重要因为我们需要能够通过脚本轻松控制相机使其移动并拍照。 我们决定让相机围绕物体旋转从各个角度拍照。 然而如果我们考虑相机设置移动的立方空间 (x,y,z)这种运动就不那么明显了。 这意味着设置定义描述对象周围球形运动的 (x,y,z) 点列表会太复杂。 因此为了简化这项任务我们决定创建一个固定在场景中心的轴相机移动时将遵循该轴。 这意味着当轴在中心旋转时相机将随之旋转。 想象一下将你的肘部放在桌子上并移动你的拳头拳头围绕你的肘部旋转。 现在想象一下肘部是轴拳头是相机相机绕轴旋转。 因此我们创建一个轴并将其放置在场景的中心如下所示。 在 Blender 中将轴添加到场景中心 现在为了使相机的移动服从轴的移动我们按住 Shift 键并首先选择相机然后选择轴。 一旦它们都被选择我们点击 Ctr P 并选择对象保持变换。 这将使该轴成为相机的父轴。 将轴设置为相机的父级 你可以继续测试这是否有效方法是进入相机视图选择轴并进入下图中以绿色突出显示的变换窗口然后更改轴的旋转坐标。 当轴旋转坐标改变时你会看到相机绕物体旋转。 相机绕物体运行的演示 3、Blender Python脚本 现在完整的场景已经设置完毕我们可以开始编写Python脚本了。 此 Blender 功能将使我们能够自动生成渲染以便制作数以万计的图片和标签这些图片和标签将用作我们的对象识别算法的训练数据。 这是非常强大的因为这意味着如果我们有一个足够真实的 Blender 场景我们可以在大约两天内生成多达 20000 个图像和标签这个时间将根据你自己机器的 GPU 的容量而变化。 可以通过单击下图中橙色下划线的脚本窗口来访问此脚本功能。 通过单击此窗口我们将出现三个主要元素以黄色显示的 Blender 控制台、以橙色显示的脚本环境以及以绿色显示的命令跟踪器。 脚本窗口及其主要组件 命令跟踪器允许你跟踪在 Blender 环境中修改特定参数时正在使用的实际脚本命令。 例如在下图中我修改了Axis的位置。 跟踪用于更改轴位置的脚本 脚本环境允许你导入和保存以前创建的 Python 脚本。 脚本环境中的脚本将保存到你选择的外部位置。 此外当脚本准备就绪时你可以通过单击“运行脚本”按钮来运行它 在 Blender 中保存、打开并运行脚本 最后一旦脚本准备好运行请转到“窗口/切换系统控制台”以访问显示代码输出的控制台。 下图可以更好地体现这一点。 切换系统控制台输出 4、Blender 控制台简介 Blender 控制台允许用户使用 Pyhton 语言将算法输入到 Blender 环境中。 当自动生成数据时它非常有用。 在这里我们解释了用于生成数据的不同步骤。 4.1 访问场景信息 在下面的代码中你可以看到如何访问场景信息。 bpy.data.scenes 是在 Blender 中打开的所有场景的集合。 在我们的例子中只创建了一个场景。 你可以使用命令 bpy.data.scenes[0] 或 bpy.data.scenes[‘Scene’] 访问它。 bpy.data bpy_struct, BlendData at 0x000001C251997458 bpy.data.scenes bpy_collection[1], BlendDataScenes bpy.data.scenes[0] bpy.data.scenes[Scene] bpy.data.scenes[1] Traceback (most recent call last):File blender_console, line 1, in module IndexError: bpy_prop_collection[index]: index 1 out of range, size 1 scene bpy.data.scenes[0]scene bpy.data.scenes[Scene]4.2 访问对象信息 我们还需要访问对象信息。 事实上我们将修改几个对象参数例如灯光的亮度和相机的位置。 bpy.data.objects 是场景中所有对象的集合。 在我们的例子中我们有 15 个不同的对象。 因此你可以通过两种不同的方式调用对象 使用语法 bpy.data.objects[x]其中 x 是 0 到 15 之间的数字表示场景中对象的数量或者使用语法 _bpy.data.objects[‘Name’]其中 Name 是你要调用的对象的名称。 然后你可以将刚刚调用的对象存储在变量中以供进一步使用。 bpy.data.objects bpy_collection[15], BlendDataObjects bpy.data.objects[0] bpy.data.objects[Camera] camera bpy.data.objects[Camera]bpy.data.objects[1] bpy.data.objects[Carre bleu] bpy.data.objects[2] bpy.data.objects[Conteneur coupe] bpy.data.objects[3] bpy.data.objects[Empty] axe bpy.data.objects[3]axe bpy.data.objects[Empty] camera bpy.data.objects[Camera] carre_bleu bpy.data.objects[Carre bleu]carre_bleu bpy.data.objects[Carre bleu] bpy.data.objects[Light] bpy.data.objects[Light] light1 bpy.data.objects[Light]light2 bpy.data.objects[Light2]light1 bpy.data.objects[Light] light2 bpy.data.objects[Light2]4.3 修改对象信息 对于每个对象可以在场景编辑器中修改它们的信息位置、位置等。 在下图中可以看到如何修改对象的旋转。 了解对象参数的实用性对于能够创建适当的算法非常重要。 你可以看到当修改参数时其值也会在控制台中更新。 修改对象的参数 我们现在可以使用以下代码行修改控制台中的不同参数 axe.rotation_euler (0,0,0)camera.location (0,0,0.5)light1.data.energy 50light2.data.energy 0light1.data.energy 允许你修改灯光的亮度。 camera.location 修改相机的位置。 axe.rotation_euler 修改对象“斧头”的旋转。 运行前面几行代码后得到的场景 现在可以修改其中一个参数来查看场景如何变化。 axe.rotation_euler (0,0,50)这里我们修改了对象“斧头”的方向。 修改参数后得到的场景 5、生成训练数据的主要过程 为了训练我们的算法我们需要大量数据。 因此我们将创建一种算法通过在场景中移动相机来从不同角度拍摄物体的照片。 我们还将修改灯光的亮度以获得更能代表现实的数据集。 Blender使用环境图 以下算法由三个循环组成每个循环都会修改相机的一个角度请参见上图。 在循环内我们还修改灯光的亮度。 然后对于相机的每个位置我们拍摄场景照片并创建一个包含对象信息的文本文件。 生成物体图片的过程 这里可以看到算法生成的图片 借助该算法我们将获得几张图像其中包含匹配的文本文件其中包含对象的位置及其边界框。 然后该数据集将用于训练深度学习算法。 算法的预期结果 6、渲染类初始定义 为了实现一个包含上述算法并能够访问场景和其中对象的信息的程序我们决定创建一个名为 Render 的完整类。 该类在下面初始化。 我们首先导入所有相关的库。 bpy 是允许我们访问和修改 Blender 元素信息的库。 在 Render 类的初始化期间我们定义了将要操作的主要对象例如定义为 self.camera 的相机、定义为 self.axis 的轴、定义为 self.light1 和 self.light2 的光源 1 和 2 以及保存到 self.objects 变量中的所有对象。 最后但最重要的是我们定义变量 self.camera_z_limits 将相机从 0.3 米移动到 1 米每次 0.1 米。 我们定义 self.beta_limits 将相机沿 x 轴从 80° 旋转到 -80°每个 rot_step 角度。 我们定义 self.gamma_limits 来沿 x 轴从 0° 到 360° 旋转相机每个 rot_step 角度。 此外我们定义 self.images_filepath 和 self.labels_filepath 这将是保存我们的程序生成的图像和标签的文件路径。 ## Import all relevant libraries import bpy import numpy as np import math as m import random## Main Class class Render:def __init__(self):## Scene information# Define the scene informationself.scene bpy.data.scenes[Scene]# Define the information relevant to the bpy.data.objectsself.camera bpy.data.objects[Camera]self.axis bpy.data.objects[Main Axis]self.light_1 bpy.data.objects[Light1]self.light_2 bpy.data.objects[Light2]self.obj_names [Rose Flower, Blue Square, Green star, Yellow hexagon, Orange losange,Rose oval, Blue rectangle, Green circle, Orange triangle]self.objects self.create_objects() # Create list of bpy.data.objects from bpy.data.objects[1] to bpy.data.objects[N]## Render informationself.camera_d_limits [0.2, 0.8] # Define range of heights z in m that the camera is going to pan throughself.beta_limits [80, -80] # Define range of beta angles that the camera is going to pan throughself.gamma_limits [0, 360] # Define range of gamma angles that the camera is going to pan through## Output information# Input your own preferred location for the images and labelsself.images_filepath C:/Users/Federico Arenas/Desktop/Webinar/Tutorial Blender/Blender/Dataself.labels_filepath C:/Users/Federico Arenas/Desktop/Webinar/Tutorial Blender/Blender/Data/Labelsdef set_camera(self):self.axis.rotation_euler (0, 0, 0)self.axis.location (0, 0, 0)self.camera.location (0, 0, 3)7、平移物体并拍照的主要算法 现在我们将 main_rendering_loop() 函数添加到 Render 类中。 该函数是上一节中所示算法的 Python 实现。 通过访问 self.camera、 self.axis、 self.light1 和 self.light2 信息我们将能够在物体周围移动相机、拍照并提取标签。 最初通过使用 self.calculate_n_renders(rot_step) 函数我们能够计算将创建多少个渲染和标签。 rot_step 参数指示程序要拍摄照片的角度。 rot_step 越小创建的渲染和标签就越多。 然后我们打印渲染次数的信息并询问是否开始渲染。 这很重要因为它允许我们估计 rot_step 将为我们提供多少渲染如果太多我们增加 rot_step如果太少我们减少它。 如果用户点击“Y”则算法开始创建数据。 必须注意的是我们必须进行一些重构并调整最初定义的限制 self.camera_z_limits、 self.beta_limits 和 self.gamma_limits。 进行此重构是因为 for 循环既不能循环小数也不能循环负数因此我们被迫将小数乘以 10并使用 10° 到 170°而不是 80° 到 -80°。 def main_rendering_loop(self, rot_step):This function represent the main algorithm explained in the Tutorial, it accepts therotation step as input, and outputs the images and the labels to the above specified locations.## Calculate the number of images and labels to generaten_renders self.calculate_n_renders(rot_step) # Calculate number of imagesprint(Number of renders to create:, n_renders)accept_render input(\nContinue?[Y/N]: ) # Ask whether to procede with the data generationif accept_render Y: # If the user inputs Y then procede with the data generation# Create .txt file that record the progress of the data generationreport_file_path self.labels_filepath /progress_report.txtreport open(report_file_path, w)# Multiply the limits by 10 to adapt to the for loopdmin int(self.camera_d_limits[0] * 10)dmax int(self.camera_d_limits[1] * 10)# Define a counter to name each .png and .txt files that are outputtedrender_counter 0# Define the step with which the pictures are going to be takenrotation_step rot_step# Begin nested loopsfor d in range(dmin, dmax 1, 2): # Loop to vary the height of the camera## Update the height of the cameraself.camera.location (0, 0, d/10) # Divide the distance z by 10 to re-factor current height# Refactor the beta limits for them to be in a range from 0 to 360 to adapt the limits to the for loopmin_beta (-1)*self.beta_limits[0] 90max_beta (-1)*self.beta_limits[1] 90for beta in range(min_beta, max_beta 1, rotation_step): # Loop to vary the angle betabeta_r (-1)*beta 90 # Re-factor the current betafor gamma in range(self.gamma_limits[0], self.gamma_limits[1] 1, rotation_step): # Loop to vary the angle gammarender_counter 1 # Update counter## Update the rotation of the axisaxis_rotation (m.radians(beta_r), 0, m.radians(gamma)) self.axis.rotation_euler axis_rotation # Assign rotation to bpy.data.objects[Empty] object# Display demo information - Location of the cameraprint(On render:, render_counter)print(-- Location of the camera:)print( d:, d/10, m)print( Beta:, str(beta_r) Deg)print( Gamma:, str(gamma) Deg)## Configure lightingenergy1 random.randint(0, 30) # Grab random light intensityself.light_1.data.energy energy1 # Update the bpy.data.objects[Light] energy informationenergy2 random.randint(4, 20) # Grab random light intensityself.light_2.data.energy energy2 # Update the bpy.data.objects[Light2] energy information## Generate renderself.render_blender(render_counter) # Take photo of current scene and ouput the render_counter.png file# Display demo information - Photo informationprint(-- Picture information:)print( Resolution:, (self.xpix*self.percentage, self.ypix*self.percentage))print( Rendering samples:, self.samples)## Output Labelstext_file_name self.labels_filepath / str(render_counter) .txt # Create label file nametext_file open(text_file_name, w) # Open .txt file of the label# Get formatted coordinates of the bounding boxes of all the objects in the scene# Display demo information - Label constructionprint(--- Label Construction)text_coordinates self.get_all_coordinates()splitted_coordinates text_coordinates.split(\n)[:-1] # Delete last \n in coordinatestext_file.write(\n.join(splitted_coordinates)) # Write the coordinates to the text file and output the render_counter.txt filetext_file.close() # Close the .txt file corresponding to the label## Show progress on batch of rendersprint(Progress , str(render_counter) / str(n_renders))report.write(Progress: str(render_counter) Rotation: str(axis_rotation) z_d: str(d / 10) \n)report.close() # Close the .txt file corresponding to the reportelse: # If the user inputs anything else, then abort the data generationprint(Aborted rendering operation)pass你可能已经注意到我们调用了之前创建的两个函数 self.render_blender(render_counter) 和 self.get_all_coordinates(resx, resy)。 第一个函数包含在源代码中它改变图像大小和清晰度拍摄一张照片并定义 _self.xpix、 self.ypix 和 self.percentage 变量这些变量是拍摄的照片的大小及其提供的比例以 表示。 此函数最终将 render_counter.png 文件导出到 self.images_filepath 位置。 我们可以看到当输入到 self.get_all_coordinates(resx, resy) 函数时 resx 和 resy 会考虑这些变量。 进行以下计算以提供图像的最终尺寸 resx final x size of the image self.xpix * self.percentage * 0.01 # Multiply by 0.01 to divide by 100 and scale the final size resy final y size of the image self.ypix * self.percentage * 0.01 # Multiply by 0.01 to divide by 100 and scale the final size第二个功能将在下一节中进一步解释。 坐标恢复后它们将被添加到创建的 render_counter.txt 文件中该文件保存在 self.labels_filepath 位置。 8、从所有对象中提取标签 函数 get_all_coordinates(resx, resy) 循环遍历 self.objects 中的所有对象并尝试使用函数 self.find_bounding_box(obj) 获取每个对象的坐标该函数获取当前对象 obj如果在相机视图中则输出其坐标 。 这个函数在这里进一步解释并集成到我们在这里编写的完整代码中。 接下来如果找到该对象则函数 self.format_coordinates(b_box, i, resx, resy) 从此格式重新格式化每个对象的标签 Name_of_class_0 top_x top_y bottom_x bottom_y Name_of_class_1 top_x top_y bottom_x bottom_y Name_of_class_2 top_x top_y bottom_x bottom_y ... ... Name_of_class_N top_x top_y bottom_x bottom_y标签的初始格式 YOLO 使用的格式 0 center_x center_y bounding_box_width bounding_box_height 1 center_x center_y bounding_box_width bounding_box_height 2 center_x center_y bounding_box_width bounding_box_height ... ... N_classes center_x center_y bounding_box_width bounding_box_height格式化后标签的格式 该函数可以在这里看到 def get_all_coordinates(self):This function takes no input and outputs the complete string with the coordinatesof all the objects in view in the current imagemain_text_coordinates # Initialize the variable where well store the coordinatesfor i, objct in enumerate(self.objects): # Loop through all of the objectsprint( On object:, objct)b_box self.find_bounding_box(objct) # Get current objects coordinatesif b_box: # If find_bounding_box() doesnt return Noneprint( Initial coordinates:, b_box)text_coordinates self.format_coordinates(b_box, i) # Reformat coordinates to YOLOv3 formatprint( YOLO-friendly coordinates:, text_coordinates)main_text_coordinates main_text_coordinates text_coordinates # Update main_text_coordinates variables whith each# line corresponding to each class in the frame of the current imageelse:print( Object not visible)passreturn main_text_coordinates # Return all coordinates最后 main_text_coordinates以字符串形式返回。 9、数据生成获得的结果 运行以下代码该代码调用 Render() 类初始化相机端然后启动数据生成循环。 ## Run data generation if __name__ __main__:# Initialize rendering class as rr Render()# Initialize camerar.set_camera()# Begin data generationrotation_step 5r.main_rendering_loop(rotation_step)该程序会将进度输出到 Blender控制台窗口。 它将把图像和标签输出到指定位置。 控制台窗口中的消息 如前所述标签将具有以下格式。 0 center_x center_y bounding_box_width bounding_box_height 1 center_x center_y bounding_box_width bounding_box_height 2 center_x center_y bounding_box_width bounding_box_height ... ... N_classes center_x center_y bounding_box_width bounding_box_height最后我们运行一个程序将输出的标签绘制到输出的图像上以验证这些标签是否正确指向每个对象。 10、使用 YOLO 和 Google Colab 进行测试 我们决定使用YOLOv3的Darknet架构并在Google Colab中实现。 我们从 AlexeyAB 的 Darknet 存储库下载了 darknet 架构。 本文重点介绍 YOLO 应用程序自动生成训练数据的过程而不是训练本身因此本节的目的是展示我们结果的简短摘要。 为了获得最佳性能我们进行了 5 次不同的测试。 然而受到自己计算机处理能力的极大限制使用Nvidia GeForce MX150 GPU渲染大约10000张图像最多需要一天半的时间。 这意味着创建大约 100000 张图像的数据集是不可行的因为我们还需要将计算机用于其他目的。 无论如何训练输出了以下损失图。 我们对结果进行了总结发现如果不能表示测试中的变化那么增加数据量并不总是有帮助。 这可以从测试 1 和测试 5 的比较中看出。 考虑到我们使用低容量计算机花了大约一周的时间来获取数据并训练我们的模型识别结果是令人满意的。 这意味着合成数据生成的可扩展性非常好。 有了更好的资源我们就可以用更多的数据来训练我们的算法甚至更加逼真。 下面可以看到处理合成测试数据的算法的演示完整的视频可以在此处 查看。 下面可以看到针对真实测试数据的算法演示完整视频可以在此处 查看。 原文链接基于合成数据的YOLO训练 — BimAnt
http://www.tj-hxxt.cn/news/131477.html

相关文章:

  • 做企业网站大约多少钱centos wordpress 500
  • 东莞网站制作哪里找wordpress 缺少样式表
  • 电子商务网站开发主要实现功能百度宁波运营中心
  • 濮阳免费网站建设网站开发业务ppt
  • 比较好的设计网站有哪些网址站
  • 网站关键词优化软件网站开发后端怎么开发
  • 集团高端网站建设公众号开发所需技术
  • 电商设计就是网站设计吗枣庄网站开发公司
  • 广州网站建设骏域网站wordpress 好的相册
  • 网站开发毕设结论wordpress购物
  • 标签模板制作石家庄seo网站优化电话
  • 企业做的网站费入什么科目空气能空调如何做网站
  • 达州住房和城乡建设部网站网站建设站长之家
  • 杨浦区网站建设建网站广州
  • 好的建筑设计网站百度数据中心
  • 合肥软件开发wordpress joomla seo
  • 建站网址丹阳建设局官方网站
  • 郑州网站推广方案wordpress新建功能页面
  • 卢氏县住房和城乡规划建设局网站长春网站建设sok
  • 无锡高端网站建设公司哪家好wordpress多个菜单menu
  • 搜索引擎对网站推广的作用wordpress 未登录提示
  • 流媒体网站建设规划微信公众号商城制作
  • 深圳做h5网站公司百度推广登录账号首页
  • 手机 网站 开发品牌网站设计有哪些建议
  • 淄博好的建网站公司网线制作的步骤
  • 潍坊企业模板建站江门网站建设定制
  • 国内设计欣赏网站昆明网站建设猫咪科技
  • 公司官方网站制作上海网络推广报价
  • 湛江网站建设方案优化wordpress关于我们插件
  • 安全的小网站小程序页面设计用什么软件