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

从零开始使用YOLOv11——Yolo检测detect数据集自建格式转换为模型训练格式:20w+图片1w+类别代码测试成功

        在之前的文章中记录了YOLO环境的配置安装和基本命令的一些使用,上一篇博文的地址快速链接:从零开始使用YOLOv8——环境配置与极简指令(CLI)操作:1篇文章解决—直接使用:模型部署 and 自建数据集:训练微调-CSDN博客

        使用YOLO作为目标检测任务的平台一个好处是,其搭建了非常简洁明了的训练命令行模式,可以便捷的对自建数据集进行微调。

        在对自己数据集进行模型训练前,非常重要费时的就是对数据的预处理,包括数据清洗、统计信息分析、数据格式转换。本文专注于将自己数据 json 格式转为YOLO训练支持的 txt 数据格式,并给出可以复用的数据集构建代码,代码已上传至Gitee平台。

        Gitee链接:https://gitee.com/machine-bai-xue/yolo-source-code-analysis

        如果链接失效,访问404拒绝,可以直接在Gitee码云主页搜索——“机器白学”,所有项目中的YOLO源码实验就是本系列所有实验代码。

        

目录

一、初始自建数据集Json格式

        1.文件存放格式

        2.标签JSON格式

二、YOLO训练数据集创建类

        1.直接使用

        2.可视化检查

        3.完整代码与扩展


一、初始自建数据集Json格式

        1.文件存放格式

        首先约定一下数据的初始格式,本文选择最简单的 JSON 列表数据集格式作标签保存。所有图片在一个文件夹(img)下,所有便签在另一个文件夹(json)下。

        2.标签JSON格式

        对标签数据具体来说,坐标数据(下图红框)和类别数据(下图蓝框)存放在同一个列表里,前四个为左上右下两点的xy绝对坐标值,类别字符信息放在后面。

        (如果还有除了类别外的其余信息可以直接在列表后添加,最后构建数据集只需取出对应索引即可。例如,对于同一批图片数据和坐标数据可能存在多种分类任务,就官方coco例子来说,其给的类别是详细分类后的物体名称——长颈鹿、花瓶、杯子......如果想构建一个大致分类的检测模型,如将细化类改为抽象类名称——动物、装饰品、日用品......只需在列表后面继续添加即可,如下第一个图第一个框可以改为——【385, 60, 600, 357, “giraffe”, “动物”】

二、YOLO训练数据集创建类

        将任意训练数据集搭建成初始的Json格式并存放按文件夹存放后,即可使用下面的数据转换类生成多个符合训练标准的数据集格式——包括训练集train和验证集val、yaml配置文件、txt标签文件等部分。

        1.直接使用

        首先总览整个类的使用。首先确保 opencv-python(cv2)和 pillow(PIL)库正确安装在环境里了。

        导入定义的转化类(可在文章最后直接复制,或者在Gitee地址下载对应py文件),实例初始化。初始化中三个关于文件地址的基本参数是必须存在的。

        初始化基本参数按输入顺序含义归纳在下面表格。

img_path所有图片存放文件地址(str)
label_path所有Json格式标签数据文件地址(str)
save_pathYolo数据集结果保存地址(str)

        另外初始化中还有几个可以调整的附加参数。其影响数据集搭建的某些细节部分。

train_ratio训练集占总数据量的比重,小数数据格式(float)
cls_id训练数据集中类别标签在原始数据中的索引位置(int,>=4)
seed设置打乱数据集文件名的随机种子——随机分配训练和验证数据(int)

         配置完参数后,直接使用类下的 dataset_main() 方法就可以自动生成训练验证数据集和yaml配置文件了。

        所有数据按照设定的划分比例随机采样分开。

        其中 cls_freq.json 是统计所有类别出现的频率字典,字典键对应类别名,值对应出现的次数。可以根据其频率查看哪些类训练样本偏少,决定是否要进行数据增强操作。

        .yaml 文件是YOLO训练的配置文件。其中names是按出现频率排序的类别和标签索引对。 

        2.可视化检查

        转换类中还定义了一些可视化函数,可以检查数据是否正确。其中对于初始数据只需直接使用visual_json_main() 方法即可。

        还可以直接使用类中的可视化函数,进行自定义的检查。

        3.完整代码与扩展

        下面将完整类代码放在下面,可以对其中相应函数方法进行修改实现扩展任务。欢迎批评指正。

import os
import json
import random
import cv2
import yaml
import numpy as np
from PIL import Image, ImageDraw, ImageFontclass YOLO_Dataset_Creator:def __init__(self, img_path, label_path, save_path, train_ratio=0.85, cls_id=4, seed=42):self.img_path, self.labels, self.data = img_path, label_path, save_pathself.train = train_ratioself.cls = Noneself.cls_id = cls_idself.seed = seeddef dataset_main(self):# 读取图片信息划分train和val集self.tr_name, self.val_name = self.divide_dataset(self.labels)# 根据json中类别信息生成配置yaml文件self.cls = self.generate_yaml(self.labels)# 生成yolo的txt训练数据集self.dataset_create(self.data)def divide_dataset(self, json_path):# 根据图片获取所有文件名信息total_file_list = []for file in os.listdir(json_path):if file.lower().split('.')[-1] in ['json']:base = file.split('.')[0]total_file_list.append(base)# 随机打乱后按比例生成训练train和验证valrandom.seed(self.seed)random.shuffle(total_file_list)length = len(total_file_list)tr_file_list = [tr for tr in total_file_list[:int(self.train * length)]]val_file_list = [te for te in total_file_list[int(self.train * length):]]return tr_file_list, val_file_listdef statis_info(self, json_path):cls_dict = dict()for file in os.listdir(json_path):if file.lower().split('.')[-1] in ['json']:jsondir = os.path.join(json_path, file)with open(jsondir, 'r', encoding='utf-8') as f:box_cls_list = json.load(f)for box_cls in box_cls_list:cls = box_cls[self.cls_id]if str(cls) not in cls_dict.keys():cls_dict[str(cls)] = 1else:cls_dict[str(cls)] +=1cls_dictdir = os.path.join(self.data, 'cls_freq.json')with open(cls_dictdir, 'w') as f:json.dump(cls_dict, f)return cls_dictdef generate_yaml(self, json_path):# 获得类别频率字典cls_dict = self.statis_info(json_path)# 生成yaml配置文件sorted_cls = sorted(cls_dict, key=cls_dict.get, reverse=True)names_dict = {}clses_dict = {}for id, c in enumerate(sorted_cls):names_dict[id] = cclses_dict[c] = idyaml_dict = {"path":self.data,"train":"images/train","val":"images/val","names":names_dict}yaml_savedir = os.path.join(self.data, 'HP_Data.yaml')with open(yaml_savedir, "w") as f:yaml.dump(yaml_dict,f)print('yaml success')return clses_dictdef dataset_create(self,data_path):# 必要子文件生成tr_img = os.path.join(data_path, 'images/train')va_img = os.path.join(data_path, 'images/val')tr_lab = os.path.join(data_path, 'labels/train')va_lab = os.path.join(data_path, 'labels/val')# 创建文件夹for file in [tr_img, va_img, tr_lab, va_lab]:os.makedirs(file, exist_ok=True)# train和valself._train(tr_img, tr_lab)self._val(va_img, va_lab)def _train(self, tr_img, tr_lab):# 生产训练集trainfor name in self.tr_name:print(name, 'start')# 图片复制保存移动imgsave = os.path.join(tr_img, name+'.jpg')jpgdir = os.path.join(self.img_path, name+'.jpg')# 标签txtsave = os.path.join(tr_lab, name+'.txt')jsondir = os.path.join(self.labels, name+'.json')self.label_create(jsondir, txtsave, name, jpgdir, imgsave)def _val(self, va_img, va_lab):# 生产验证集valfor name in self.val_name:# 图片复制保存移动imgsave = os.path.join(va_img, name + '.jpg')jpgdir = os.path.join(self.img_path, name + '.jpg')# 标签txtsave = os.path.join(va_lab, name + '.txt')jsondir = os.path.join(self.labels, name + '.json')self.label_create(jsondir, txtsave, name, jpgdir, imgsave)def label_create(self, jsondir, txtsave, name, jpgdir, imgsave):# 图片信息img = cv2.imread(jpgdir)if img is None:return print(name, 'jpg is empty')height, width, _ = img.shape# 框信息with open(jsondir, 'r', encoding='utf-8') as f:box_list = json.load(f)box_str_list = []for box_cls in box_list:box = box_cls[:4]conv_box = self.normalize((width, height), box)text = box_cls[self.cls_id]cls = self.cls[str(text)]conv_box.insert(0, int(cls))box_str = " ".join(str(item) for item in conv_box)+'\n'box_str_list.append(box_str)if box_str_list!=[]:with open(txtsave, "w", encoding="utf-8") as f:f.writelines(box_str_list)if os.path.exists(txtsave):cv2.imwrite(imgsave, img)def normalize(self, size, box):  # size:(原图w,原图h) , box:(xmin,xmax,ymin,ymax)# 锚框归一化dw = 1. / size[0]  # 1/wdh = 1. / size[1]  # 1/hx = (box[0] + box[2]) / 2.0  # 物体在图中的中心点x坐标y = (box[1] + box[3]) / 2.0  # 物体在图中的中心点y坐标w = box[2] - box[0]  # 物体实际像素宽度h = box[3] - box[1]  # 物体实际像素高度x = x * dw  # 物体中心点x的坐标比(相当于 x/原图w)w = w * dw  # 物体宽度的宽度比(相当于 w/原图w)y = y * dh  # 物体中心点y的坐标比(相当于 y/原图h)h = h * dh  # 物体宽度的宽度比(相当于 h/原图h)return [x, y, w, h]  # 返回 相对于原图的物体中心点的x坐标比,y坐标比,宽度比,高度比,取值范围[0-1]def denormalize(self, size, normalized_box):  # size: (原图w, 原图h), normalized_box: [x, y, w, h]# 提取原图的宽度和高度w, h = size# 将中心点坐标和宽高还原为原图的像素坐标x_center = normalized_box[0] * wy_center = normalized_box[1] * hbox_width = normalized_box[2] * wbox_height = normalized_box[3] * h# 计算还原后的边界框坐标xmin = x_center - box_width / 2.0xmax = x_center + box_width / 2.0ymin = y_center - box_height / 2.0ymax = y_center + box_height / 2.0return [int(xmin), int(ymin), int(xmax), int(ymax)]  # 返回还原后的边界框坐标def visual_json_main(self, visfile):for file in os.listdir(self.img_path):base = file.split('.')[0]jpgdir = os.path.join(self.img_path, file)img = cv2.imread(jpgdir)if img is None:return print('img is empty')jsondir = os.path.join(self.labels, base+'.json')with open(jsondir, 'r', encoding='utf-8') as f:boxes_list = json.load(f)box_list = []for box_ in boxes_list:box = box_[:4]text = box_[self.cls_id]box.append(text)box_list.append(box)visdir = os.path.join(visfile, file)vis = self.visual_word(box_list, img ,(0,255,0))cv2.imwrite(visdir, vis)print(file,' success')def visual_box(self, box_list, img, color):for idx, box in enumerate(box_list):if len(box) == 4:l, t, r, b = box# 图片画框cv2.rectangle(img, (int(l), int(t)), (int(r), int(b)), color, thickness=2, lineType=cv2.LINE_AA)elif len(box) == 8:pts = np.array(box, np.int32)pts = pts.reshape((-1, 1, 2))cv2.polylines(img, [pts], isClosed=True, color=color, thickness=2)return imgdef visual_word(self, word_list, img, color):font_path = "C:\Windows\Fonts\SimHei.ttf"font_size = 45for idx, box in enumerate(word_list):l, t, r, b, word = box# 图片画框cv2.rectangle(img, (l, t), (r, b), color, thickness=2, lineType=cv2.LINE_AA)caption = f"{word}"pil_img = Image.new('RGB', (0, 0))draw = ImageDraw.Draw(pil_img)text_size = draw.textbbox((0, 0), caption, font=ImageFont.truetype(font_path, font_size))text_width = text_size[2] - text_size[0]text_height = text_size[3] - text_size[1]# 使用 OpenCV 画文本背景框cv2.rectangle(img, (r, t), (r + text_width, t + text_height), color, -1)# 使用 PIL 绘制中文标签img = self.put_chinese_text(img, caption, (r, t), font_size, font_path, (0, 0, 0))return imgdef put_chinese_text(self, img, text, position, font_size, font_path, text_color):# 创建一个 PIL 图像pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(pil_img)# 加载字体font = ImageFont.truetype(font_path, font_size)# 绘制文本draw.text(position, text, font=font, fill=text_color)# 将 PIL 图像转换回 OpenCV 格式img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)return img

相关文章:

从零开始使用YOLOv11——Yolo检测detect数据集自建格式转换为模型训练格式:20w+图片1w+类别代码测试成功

在之前的文章中记录了YOLO环境的配置安装和基本命令的一些使用,上一篇博文的地址快速链接:从零开始使用YOLOv8——环境配置与极简指令(CLI)操作:1篇文章解决—直接使用:模型部署 and 自建数据集&#xff1a…...

自动化新时代:机器取代工作,我们该如何重塑自我?

内容概要 在自动化时代的浪潮中,技术的飞速发展对传统工作模式产生了深远影响。我们眼前浮现的是一个充满机遇与挑战的新世界。许多岗位面临被机器取代的威胁,然而,这一变化并不仅仅是消极的。在这个背景下,个体不仅需要重新审视…...

GEE 土地分类——利用Sentinel-2数据进行土地分类

目录 简介 函数 ee.Classifier.smileRandomForest(numberOfTrees, variablesPerSplit, minLeafPopulation, bagFraction, maxNodes, seed) Arguments: Returns: Classifier 代码 结果 简介 利用Sentinel-2数据进行土地分类的流程大致可分为以下几个步骤: 1. 数据获取…...

《C++ 游戏开发》

一、引言 在当今的数字娱乐时代,游戏开发已经成为一个充满活力和创新的领域。C 作为一种强大的编程语言,在游戏开发中占据着重要的地位。它具有高效的性能、丰富的功能和广泛的适用性,能够满足游戏开发中对性能和灵活性的高要求。本文将深入探…...

2024年11月10日系统架构设计师考试题目回顾

案例分析 试题一:质量属性 基于描述填空是什么质量属性,常规题。(性能,功能,安全,可用等等)可用性而言,王工建议采用 ping/echo 机制检测,不过从资源使用角度&#xff…...

测试实项中的偶必现难测bug--苹果支付丢单问题

问题描述: app支付后,由于某种原因(可能是网络、流量不稳定、或者用户快速频繁操作。。。)会造成一定概率性的回调苹果支付结果失败的情况出现,表现的直观现象就是客户反馈已经支付了,包括苹果支付也是有记录,但是我们的后台显示的是已取消状态的订单 验证难点:测试和…...

Elasticsearch的数据类型

Elasticsearch(简称 ES)支持多种数据类型,主要分为以下几类: 1. 基本数据类型 Text:用于全文搜索的文本字段。ES 会对其内容进行分词处理。Keyword:适用于精确匹配的字段,例如名称、标签等。ES 不会对其内容分词处理。Integer:整数类型,包括 byte、short、integer 和…...

SSL 证书申请以及配置流程

SSL 证书申请以及配置流程 手动申请免费 SSL 证书的简明指南 如果你希望手动为你的网站申请免费的 SSL 证书,Let’s Encrypt 提供了一个很棒的免费服务。而 Certbot 则是官方推荐的工具,可以帮助你完成证书的申请和配置。以下是如何一步步完成的详细说…...

[Docker#4] 镜像仓库 | 部分常用命令

目录 什么是 Docker Registry 镜像仓库生活案例 镜像仓库分类 镜像仓库工作机制 常用的镜像仓库 私有仓库 镜像仓库命令 镜像命令[部分] 容器命令[部分] 什么是 Docker Registry 定义:Docker Registry 负责存储、管理和分发镜像,并提供了登录认…...

工业通信协议对比:OPC-UA、Modbus、MQTT、HTTP

综合对比表 对比项OPC-UAModbusMQTTHTTP通信效率低,带宽消耗高高高,开销低,效率高低,带宽消耗大实时性一般,延迟较高高,延迟低高,低延迟低,延迟高性能消耗高,需要高性能…...

docker 常用方法

目录 docker参数解释 基础信息和环境变量设置 容器运行和管理相关参数 数据卷挂载 GPU 相关参数 镜像相关参数 查看现有的镜像 docker images 查看正在运行的docker docker ps 1、docker启动停止及查看状态 启动docker: systemctl start docker 停止docker…...

区块链技术入门:以太坊智能合约详解

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 区块链技术入门:以太坊智能合约详解 区块链技术入门:以太坊智能合约详解 区块链技术入门:以太…...

特定数据库的备份脚本

该脚本 mysql_backup.sh 是一个 MySQL 数据库的备份脚本,以下是它的工作原理和需要注意的细节: 脚本内容分析 1.设置时间变量 TIME : TIMEdate %F_%H-%M-%S该变量 TIME 存储当前日期和时间,格式为 YYYY-MM-DD_HH-MM-SS,用于生…...

uni-app打包后报错云服务空间未关联

使用uni-app打包到h5 项目里面用到了uni-app的云端一体城市选择组件,这个组件数据用到了uniCloud云服务空间,在本地运行没问题,打包之后测试环境报错: 一顿查,查到了官网是这样说的: cli publish --platfo…...

FPGA学习(10)-数码管

前3节视频目的是实现显示0~F的数码管仿真,后3节是用驱动芯片驱动数码管。 目录 1.数码管显示原理 2.代码过程 2.1仿真结果 3.串行移位寄存器原理 3.1原理 ​编辑 3.2 数据手册 3.3 先行设计思路 4.程序 4.1确定SRCLK的频率 4.2序列计数器 4.3 不同coun…...

C++(继承)

继承的语法 继承的好处:减少重复代码 语法: class 子类 : 继承方法 父类 子类 也称为 派生类 父类 也成为 基类 继承方式 公共继承 保护继承 私有继承 结论:父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后…...

华为OD机试 - RSA加密算法(Java 2024 E卷 100分)

long n (long) Math.sqrt(num); 与long n (long) Math.floor(Math.sqrt(num)); 这两行代码的目的都是计算 num 的平方根,并将结果转换为 long 类型的整数。然而,它们在处理方式上有一些微小的差别。 long n (long) Math.sqrt(num);long n (long) M…...

分组校验在Spring中的应用详解

目录 前言1. 什么是分组校验2. 分组校验的基本原理3. 分组校验的实现步骤3.1 定义分组接口3.2 在校验项中指定分组3.3 校验时指定要校验的分组3.4 默认分组和分组的继承 4. 分组校验的优势和适用场景4.1 优势4.2 适用场景 5. 常见问题与解决方案5.1 校验未生效5.2 无法识别默认…...

torch.nn.**和torch.nn.functional.**的区别

torch.nn.** torch.nn.**是一个继承了torch.nn.Module的类,使用前必须先构造对象,然后再调用。如果直接使用则会报错 例如 a torch.randn(3,4) print(a) sigmoid torch.nn.Sigmoid() a sigmoid(a) print(a) a torch.nn.Sigmoid(a)tensor([[ 0.2462…...

Air780E基于LuatOS编程开发

Air780E基于LuatOS编程开发 Air780E开发板下载固件版本开发板刷机开发调试源码编译下载源码编译工具编译工具链 Air780E开发板 合宙通信推出的 LTE Cat.1 bis通信模块,采用移芯EC618平台,支持4G全网通, 包括的模组有: Air780E – 4G Cat.1Air780EG – …...

贪心算法-汽车加油

这道题目描述了一个汽车旅行场景,需要设计一个有效的算法来决定在哪几个加油站停车加油,以便最小化加油次数。题目给出了汽车加满油后的行驶距离n公里,以及沿途若干个加油站的位置。我们需要找出一个方案,使得汽车能够完成整个旅程…...

Qt信号和槽-->day04

Qt信号和槽 标准的信号和槽函数Qt中的槽函数Qt中的信号 connect案例 自定义信号和槽案例分析 信号槽的拓展信号连接信号案例 信号槽的两种连接方式Qt5中的处理方式Qt4中的处理方式Qt5处理信号槽重载问题案例 lambda表达式简单案例Qt中的应用 补充知识点 标准的信号和槽函数 QW…...

【Linux】为终端命令自定义快件键并弹窗提醒 设置快捷键切换网络代理(Network Proxy)Disabled/Manual 并弹窗提醒

【Linux】为终端命令自定义快件键并弹窗提醒 设置快捷键切换网络代理(Network Proxy)Disabled/Manual 并弹窗提醒 可以自定义快捷键执行终端命令,执行完毕会有弹窗提醒。下面给一个例子,设置快捷键切换网络代理(Netwo…...

十六:Spring Boot依赖 (1)-- spring-boot-starter 依赖详解

1. 简介: spring-boot-starter 是 Spring Boot 项目中的基础启动器依赖,它为开发者提供了 Spring Boot 应用所需的核心功能和自动配置 spring-boot-starter 不是一个具体的功能模块,而是一个基础的启动器。 Spring Boot 提供了一系列的 sta…...

讲讲关于SNMP与智能PDU插座

什么是SNMP 简单网络管理协议 (SNMP) 是一种应用层协议,主要用于网络管理中的设备监控和控制。通过 SNMP,网络管理员可以从管理站远程访问网络中的设备,获取设备的状态信息、配置参数,甚至控制设备的行为。SNMP 被广泛应用于 TCP/…...

在CentOS下安装RabbitMQ

在CentOS下安装RabbitMQ 在CentOS下安装RabbitMQ可以按照以下步骤进行:步骤 1: 更新系统步骤 2: 安装Erlang步骤 3: 添加RabbitMQ仓库步骤 4: 安装RabbitMQ步骤 5: 启动RabbitMQ服务步骤 6: 检查RabbitMQ状态步骤 7: 启用RabbitMQ管理插件(可选&#xff…...

前端使用Canvas实现网页电子签名(兼容移动端和PC端)

实现效果: 要使用Canvas实现移动端网页电子签名,可以按照以下步骤: 在HTML文件中创建一个Canvas元素,并设置其宽度和高度,以适配移动设备的屏幕大小。 // 创建一个canvas元素 let canvas document.createElement(&q…...

什么是OSTRPT报文?

OSTRPT(Order Status Report)是一种 EDI(电子数据交换)报文,用于在供应链管理中向采购商报告订单状态。这种报文通常由供应商发送给采购商,目的是告知订单的当前处理状态、预期交货时间、订单中的变化等信息…...

PICO+Unity MR空间锚点

官方链接:空间锚点 | PICO 开发者平台 注意:该功能只能打包成APK在PICO 4 Ultra上真机运行,无法通过串流或PICO developer center在PC上运行。使用之前要开启视频透视。 在 Inspector 窗口中的 PXR_Manager (Script) 面板上,勾选…...

electron 中 contextBridge 作用

1. 安全地实现渲染进程和主进程之间的通信 在 Electron 应用中,主进程和渲染进程是相互隔离的,这是为了安全和稳定性考虑。 然而,在很多情况下,渲染进程需要访问主进程中的某些功能,例如系统级别的操作或者一些应用级…...

北京海淀建设银行数据中心/深圳百度搜索排名优化

Java中的异常处理机制已经比较成熟,我们的Java程序到处充满了异常的可能,如果对这些异常不做预先的处理,那么将来程序崩溃就无从调试,很难找到异常所在的位置。本文将探讨一下Java中异常与错误的处理方法,一起来看看。…...

找代码的网站/seo挖关键词

执行UPDATE语句时,在SQL DEVELOPER时正常的,但是程序执行时卡死。 是因为之前执行UPADATE语句时,没有COMMIT,oracle将该条记录锁住了。后续程序无法执行。 我的解决方案: 在数据库中执行COMMIT命令即可解决。 网络解决…...

做早餐煲汤网站/企业网站的作用和意义

方法一,Eclipse开发环境中整合: 1. 工程原来是两个APK,InnerAPK和OuterAPK,里面都只有一个Activity,分别为InnerActivity和OuterActivity。 2. 在InnerAPK工程上选择右键,选择Properties->Android&#…...

衡水网站建设公司联系电话/青岛网页搜索排名提升

快手小店通,是快手新出的一款商业化广告产品,是快手把自身的商业化广告业务跟快手小店的链路打通,从而可以直接推行快手小店的一种新式的商业形式。 快手小店针对店铺商家推出小店通功能,该功能可以帮助商家推广带有商品链接的作…...

cpa推广app赚钱联盟平台/北京网站建设东轩seo

帮粉丝找问题,卧推半年多了,还是没找到胸肌发力的感觉,看看你是否跟他一样。一位好友的微信截图一个好友的求助前几天,一个微信上的粉丝给我发消息说:自己卧推已经半年多了,可还是找不到好的胸肌发力感觉&a…...

实业公司网站模板/十种网络推广的方法

首先我在数据库里建了两张表,一个是用户的积分表,一个是签到状态表,分来用来记录用户的积分数和先到状态 在用户签到状态表中我们有一个字段,last_sign_time,即上一次签到时间,每次可以签到的时候把这个时间与当前时间进行比较 如果相差为0天,则说明今天已签到(这个签到是24小时…...