瀏覽代碼

Merge remote-tracking branch 'origin/master'

kindring 8 月之前
父節點
當前提交
f9a065d3c5

+ 1 - 1
js/date.js

@@ -27,4 +27,4 @@ function dateFormat(date, format = 'YYYY-MM-DD H:m:s') {
         format = format.replace(key, config[key])
     }
     return format
-}
+}

+ 40 - 0
js/dom抽奖排列.js

@@ -0,0 +1,40 @@
+// 生成数组, 从大到小顺时针螺旋
+function generateSpiralArray(rows, cols) {
+    let arr = Array.from({ length: rows }, () => Array.from({ length: cols }));
+    let value = 1;
+    let top = 0, bottom = rows - 1, left = 0, right = cols - 1;
+
+    while (top <= bottom && left <= right) {
+        // 左 => 右
+        for (let i = left; i <= right; i++) {
+            arr[top][i] = value++;
+        }
+        top++;
+        // 上 => 下
+        for (let i = top; i <= bottom; i++) {
+            arr[i][right] = value++;
+        }
+        right--;
+
+        // 右 => 左
+        if (top <= bottom) {
+            for (let i = right; i >= left; i--) {
+                arr[bottom][i] = value++;
+            }
+            bottom--;
+        }
+
+        // 下 => 上
+        if (left <= right) {
+            for (let i = bottom; i >= top; i--) {
+                arr[i][left] = value++;
+            }
+            left++;
+        }
+
+    }
+
+    return arr;
+}
+
+const result2 = generateSpiralArray(6, 6);

+ 211 - 0
js/prototype/prototype原型链.md

@@ -0,0 +1,211 @@
+# js原型
+#tag prototype 原型 __proto__
+
+## 原型对象
+#d 何为原型对象  
+原型对象是每个函数都有的属性, js中的函数会有一个`protorype`属性,  
+这个属性是一个对象, 这个对象有一个属性为`constructor`,意为构造函数.  
+专门指向函数本身. 在原型对象中, 存放着构造函数的公共属性和方法.
+
+#l(构造函数)
+
+
+#e 原型示意
+构造函数 FN
+```javascript
+function FN(){
+    this.name = 'p'
+    this.age = 18
+}
+FN.prototype.say = function (){
+    console.log(`hello my name is ${this.name}`)
+}
+```
+此时构造函数 FN 的原型对象可以视作如下内容  
+
+| 属性          | 值      |
+|-------------|--------|
+| constructor | FN     |
+| __proto__   | Object |
+| 公共属性        |
+| name        | p      |
+| age         | 18     |
+| 公共方法       | say    |
+
+```javascript
+let fn = new FN()
+fn.say() // hello my name is p
+```
+
+#d 原型对象作用
+1. 存放构造函数的公共属性和方法
+2. 用于构建实例对象
+3. 构造函数的实例对象, 都会共享原型对象中的属性和方法
+
+## 原型链
+
+#d 什么是原型链  
+在js中, 每个对象都有一个`__proto__`的属性,    
+这个属性指向了该对象的[原型对象], 但是这个[原型对象]本身是一个对象,  
+所以它也有[原型]属性, 所以我们将之称为[原型链].  
+这个链条的最终会指向`Object`对象, Object也会有一个[原型]属性,  
+但是这个属性为`null`
+
+#e 原型链示意
+下面是一个简单的原型链示意图
+```javascript
+function FN(){}
+FN.prototype.say = function (){}
+let fn = new FN()
+// fn实例的原型
+fn.__proto__ === FN.prototype // true
+// FN对象的原型
+FN.prototype.__proto__ === Object.prototype // true
+// Object对象的原型
+Object.prototype.__proto__ === null // true
+```
+
+#d 原型链作用
+1. 实现继承效果  
+当你试图访问一个对象的属性时:  
+如果在对象本身中找不到该属性,就会在原型中搜索该属性。  
+如果仍然找不到该属性,那么就搜索原型的原型,  
+以此类推,直到找到该属性,或者到达链的末端,  
+在这种情况下,返回 undefined。
+2. 构造函数的实例对象, 共享原型对象中的属性和方法  
+可以直接通过原型链访问修改原型的属性和方法(不建议用这种邪道方法修改)
+
+
+
+#e 原型链应用示例
+```javascript
+function FN(){}
+FN.prototype.say = function (){
+console.log(`hello`)
+}
+let fn = new FN()
+fn.say() // hello
+fn.say === fn.__proto__.say // true
+fn.say === FN.prototype.say // true
+// 无法在 object 对象中找到
+fn.say === Object.prototype.say // false
+fn.age = 18
+console.log(fn) // FN { age: 18 }
+let fn1 = new FN()
+console.log(fn1.age) // undefined
+fn1.say() // hello
+// 修改原型
+FN.prototype.say = function (){
+    console.log(`hello my age is ${this.age}`)
+}
+fn.say() // hello my age is 18
+fn1.say() // hello my age is undefined
+FN.prototype.age = 19
+// fn1 中无法找到age属性, 所以用fn1.age访问到的是FN.prototype.age属性
+console.log(fn1.age) // 19
+fn1.say() // hello my age is 19
+// 修改fn1中的的age属性, 之后在访问该属性, 访问的是fn1.age属性
+fn1.age = 20
+console.log(fn1.age) // 20
+```
+#d 原型链的缺点
+1. 引用类型属性, 会被所有实例共享  
+2. 构造函数的实例对象, 共享原型对象中的属性和方法, 会导致原型对象中的属性和方法被修改  
+
+#c 继承效果  
+我们可以通过原型链实现继承效果, 父类中的属性和方法, 都会继承给子类.
+
+#e 继承  
+1. 父类构造函数
+```javascript
+function Person(name){
+    this.name = name
+    this.say = function (){
+        console.log(`hello my name is ${this.name}`)
+    }
+}
+let p = new Person('p')
+p.say() // hello my name is p
+```
+2. 子类构造函数
+```javascript
+
+function Student(name, age){
+    this.age = age
+}
+Student.prototype = new Person()
+let s = new Student('s', 18)
+// 父类属性会被继承给子类, 但是name属性暂时未赋值
+s.say() // hello my name is undefind
+console.log(s) // Student { age: 18 }
+```
+3. 父类属性赋值
+```javascript
+Student.prototype.name = 's'
+s.say() // hello my name is s
+```
+4. 另一种写法  
+该写法, 会将父类构造函数中的属性和方法, 赋值给子类构造函数的原型对象中,  
+其属性与方法, 都是子类实例对象所共享的, 不会影响父类构造函数中的属性和方法
+```javascript
+function Student2(name, age){
+    Person.call(this, name)
+    this.age = age
+}
+let s2 = new Student2('s2', 19)
+s2.say() // hello my name is s2
+console.log(s2) // Student2 { age: 19, name: 's2', say: [Function (anonymous)] }
+```
+5. 组合继承  
+父类的构造函数被调用了两次(创建子类原型时调用了一次,创建子类实例时又调用了一次),  
+导致子类原型上会存在父类实例属性,浪费内存。
+```javascript
+function Parent(value) {
+    this.value = value;
+}
+
+Parent.prototype.getValue = function() {
+    console.log(this.value);
+}
+
+function Child(value) {
+    Parent.call(this, value)
+}
+
+Child.prototype = new Parent();
+
+const child = new Child(1)
+child.getValue();
+child instanceof Parent;
+```
+
+6. 寄生组合继承  
+使用 Object.create(Parent.prototype) 创建一个新的原型对象赋予子类从而解决组合继承的缺陷:
+```javascript
+// 寄生组合继承实现
+
+function Parent(value) {
+    this.value = value;
+}
+
+Parent.prototype.getValue = function() {
+    console.log(this.value);
+}
+
+function Child(value) {
+    Parent.call(this, value)
+}
+
+Child.prototype = Object.create(Parent.prototype, {
+    constructor: {
+        value: Child,
+        enumerable: false, // 不可枚举该属性
+        writable: true, // 可改写该属性
+        configurable: true // 可用 delete 删除该属性
+    }
+})
+
+const child = new Child(1)
+child.getValue();
+child instanceof Parent;
+```

+ 25 - 0
js/rtp 解析/loadTxt.js

@@ -0,0 +1,25 @@
+const fs = require("fs");
+
+/**
+ * 读取文件
+ * @param filePath
+ * @param option_encode
+ * @returns {Promise<unknown>}
+ */
+function loadFileLines(filePath,option_encode = 'utf-8') {
+    return new Promise((resolve, reject) => {
+        fs.readFile(filePath, option_encode, (err, data) => {
+            if (err) {
+                reject(err)
+                return
+            }
+            let lines = data.split(/\r?\n/);
+            resolve(lines);
+        })
+    })
+}
+
+
+module.exports = {
+    loadFileLines
+}

+ 190 - 0
js/rtp 解析/rtpCheck.js

@@ -0,0 +1,190 @@
+// 读取 rtpPack1.txt 文件,解析其中的 rtp 包,检查其中的序列号是否连续
+const path = require("path");
+const loadTxt = require("./loadTxt");
+const rtp = require("./rtpParse");
+function handle(promise){
+    return promise.then(data=>[null,data]).catch(err=>[err,null])
+}
+let filePath1 = path.join(__dirname, 'rtpPack1.txt');
+let filePath2 = path.join(__dirname, 'rtpPack2.txt');
+
+async function parseRtpPackTxt(filePath) {
+    let rtpPacks = [];
+    let [err,lines] = await handle( loadTxt.loadFileLines(filePath) );
+    if (err){
+        return console.log(err);
+    }
+    // 读取一行,跳过一行
+    for (let i = 0; i < lines.length; i+=2) {
+        let line = lines[i];
+        if (line === "***end***"){
+            console.log("文件解析结束");
+            return rtpPacks;
+        }
+        rtpPacks.push(line);
+    }
+    return rtpPacks;
+}
+
+// 格式化输出内容 用于打印表格 例如
+// 表单长度 用于打印表格 例如 [20,20] 则再打印表格时,每个单元格的长度为 20
+let tbLen = [50, 50];
+let lineLen = 2;
+function printf() {
+    let str = "";
+    str += "|";
+    for (let i = 0; i < lineLen; i++) {
+        let arg = arguments[i] || "";
+        let len = tbLen[i];
+        let argLen = arg.length;
+        let spaceLen = len - argLen;
+        let space = "";
+        for (let j = 0; j < spaceLen; j++) {
+            space += " ";
+        }
+        //
+        // 如果长度超过了表单长度,则截取字符串
+        if (argLen > len){
+            arg = arg.substr(0,len);
+        }
+        str += arg + space + "|";
+
+    }
+    console.log(str);
+}
+
+// 解析剩余数据
+let last_data = ["",""];
+let bitLen = 2;
+
+// rtp over tcp 包的前两个字节为rtp包的长度
+function parseRtpOverTcp(no, package){
+    // 合并旧数据
+    if(last_data[no] !== ""){
+        package = last_data[no] + package;
+        last_data[no] = "";
+    }
+    let allLen = package.length;
+    console.log(`---*-*-*-*-*------ allLen: ${allLen} ------*-*-*-*-*-*---`)
+    let len = 0;
+    // 读取包长度
+    let packageLenStr = package.substr(0,4);
+    let packageLen = parseInt(packageLenStr,16);
+    console.log(`packageLenStr: ${packageLenStr} >>> ${packageLen}`);
+    // package_len + i  <= left_len + data_len
+    while ( (len + packageLen) * 2 <= allLen ){
+
+        console.log('----')
+        // 解析rtp包
+        let rtpPackage = package.substr(len * 2 + 4, packageLen * bitLen);
+        let rtpHeaderInfo = rtp.parseRtpHeader(rtpPackage.substr(0,24));
+        console.log(rtpHeaderInfo)
+        // i += package_len + 2;
+
+        // 读取下一个包长度
+
+        len += packageLen + 2;
+        console.log(`len: ${len}`)
+        packageLenStr = package.substr(len * 2, 4);
+        packageLen = parseInt(packageLenStr,16);
+        console.log(`packageLenStr: ${packageLenStr} >>> ${packageLen} `);
+
+
+
+        // 如果剩余数据不足以解析一个包,则跳出循环
+
+    }
+    // 读取剩余数据
+    last_data[no] = package.substr(len * 2 );
+    console.log(`last_data: ${last_data[no].length}`);
+}
+
+async function main(){
+    let rtpPackages_1 = await parseRtpPackTxt(filePath2);
+    let rtpPackages_2 = await parseRtpPackTxt(filePath2);
+    printf(" black"," ok");
+    printf("------------------------------","------------------------------");
+    // 开始解析rtp数据
+    for (let i = 0; i < rtpPackages_1.length; i++) {
+        console.log(`---*-*-*-*-*------ ${i} ------*-*-*-*-*-*---`)
+        let rtpPackage_1 = rtpPackages_1[i];
+        // 输出前10个字节
+        console.log(rtpPackage_1.substr(0,20));
+        let rtpPackage_2 = rtpPackages_2[i];
+        // rtpPackage_1 = rtpPackage_1.substr(4,24);
+        // rtpPackage_2 = rtpPackage_2.substr(4,24);
+        // printf(
+        //     `   ${rtpPackage_1}`,
+        //     `   ${rtpPackage_2}`,
+        // );
+        let rtpHeaderInfo_1 = parseRtpOverTcp(0, rtpPackage_1);
+        // let rtpHeaderInfo_2 = rtp.parseRtpOverTcp(1, rtpPackage_2);
+        let rtpHeaderInfo_2 = {};
+        // printf(
+        //     `${i} version: ${rtpHeaderInfo_1.version}`,
+        //     `${i} version: ${rtpHeaderInfo_2.version}`,
+        // );
+        //
+        // printf(
+        //     `${i} padding: ${rtpHeaderInfo_1.padding}`,
+        //     `${i} padding: ${rtpHeaderInfo_2.padding}`,
+        // );
+        //
+        // printf(
+        //     `${i} extension: ${rtpHeaderInfo_1.extension}`,
+        //     `${i} extension: ${rtpHeaderInfo_2.extension}`,
+        // );
+        //
+        // printf(
+        //     `${i} csrcCount: ${rtpHeaderInfo_1.csrcCount}`,
+        //     `${i} csrcCount: ${rtpHeaderInfo_2.csrcCount}`,
+        // );
+        //
+        // printf(
+        //     `${i} csrcCount: ${rtpHeaderInfo_1.csrcCount}`,
+        //     `${i} csrcCount: ${rtpHeaderInfo_2.csrcCount}`,
+        // );
+        //
+        // printf(
+        //     `${i} marker: ${rtpHeaderInfo_1.marker}`,
+        //     `${i} marker: ${rtpHeaderInfo_2.marker}`,
+        // );
+        //
+        // printf(
+        //     `${i} payloadType: ${rtpHeaderInfo_1.payloadType}`,
+        //     `${i} payloadType: ${rtpHeaderInfo_2.payloadType}`,
+        // );
+        //
+        // printf(
+        //     `${i} sequenceNumber: ${rtpHeaderInfo_1.sequenceNumber}`,
+        //     `${i} sequenceNumber: ${rtpHeaderInfo_2.sequenceNumber}`,
+        // );
+        //
+        // printf(
+        //     `${i} timestamp: ${rtpHeaderInfo_1.timestamp}`,
+        //     `${i} timestamp: ${rtpHeaderInfo_2.timestamp}`,
+        // );
+        //
+        // printf(
+        //     `${i} ssrc: ${rtpHeaderInfo_1.ssrc}`,
+        //     `${i} ssrc: ${rtpHeaderInfo_2.ssrc}`,
+        // );
+        //
+        // printf(
+        //     `${i} csrc: ${rtpHeaderInfo_1.csrc}`,
+        //     `${i} csrc: ${rtpHeaderInfo_2.csrc}`,
+        // );
+        //
+        // printf(
+        //     `${i} time: ${rtpHeaderInfo_1.time}`,
+        //     `${i} time: ${rtpHeaderInfo_2.time}`,
+        // );
+        //
+        // printf("------------------------------","------------------------------");
+        // printf("------------------------------","------------------------------");
+        // printf("------------------------------","------------------------------");
+    }
+
+}
+
+main()

File diff suppressed because it is too large
+ 0 - 0
js/rtp 解析/rtpPack1.txt


File diff suppressed because it is too large
+ 6 - 0
js/rtp 解析/rtpPack2.txt


+ 44 - 0
js/rtp 解析/rtpParse.js

@@ -0,0 +1,44 @@
+const time = require('../time.js');
+function hexStrToBitStr(str){
+    // 移除0x 以及空格
+    str = str.replace(/0x/g,'').replace(/\s/g,'');
+    // 转换为二进制
+    return parseInt(str,16).toString(2);
+}
+
+function parseRtpHeader(hexStr){
+    // console.log(hexStr);
+    let bitStr = hexStrToBitStr(hexStr);
+    let version = parseInt(bitStr.substr(0,2),2);
+    let padding = bitStr.substr(2,1);
+    let extension = bitStr.substr(3,1);
+    let csrcCount = bitStr.substr(4,4);
+    let marker = bitStr.substr(8,1);
+    let payloadType = parseInt(bitStr.substr(9,7),2);
+    let sequenceNumber = parseInt(bitStr.substr(16,16),2);
+    let timestamp = parseInt(bitStr.substr(32,32),2);
+    // 格式化时间戳
+    let _time = time.timestr(timestamp )
+    let ssrc = bitStr.substr(64,32);
+    let csrc = bitStr.substr(96,32);
+    return {
+        version,
+        padding,
+        extension,
+        csrcCount,
+        marker,
+        payloadType,
+        sequenceNumber,
+        timestamp,
+        ssrc,
+        csrc,
+        time: _time
+    }
+}
+
+
+
+module.exports = {
+    parseRtpHeader,
+
+}

+ 0 - 0
js/进制转换/time.js → js/time.js


+ 613 - 0
js/yolo 数据集工具/parse.js

@@ -0,0 +1,613 @@
+// 1. 读取指定目录文件, 获取文件内容.
+// 2. 解析内容, 获取图片的标注信息
+// 3. 统计标注信息
+
+const fs = require('fs');
+const path = require('path');
+const readline = require('readline');
+const xml2js = require('xml2js');
+
+function readFile(filePath) {
+    return new Promise((resolve, reject) => {
+        fs.readFile(filePath, 'utf-8', (err, data) => {
+            if (err) return reject(err);
+            resolve(data);
+        });
+    })
+}
+
+function readDir(filePath) {
+    return new Promise((resolve, reject) => {
+        fs.readdir(filePath, (err, files) => {
+            if (err) return reject(err);
+            resolve(files);
+        });
+    })
+}
+
+function parseYolo(fileData) {
+    // 解析文件内容, 获取图片的标注信息
+    // 类型 x坐标 y坐标 宽度 高度
+    // 1 0.391297 0.095892 0.280578 0.179688
+    let result = [];
+    let lines = fileData.split('\n');
+    lines.forEach(line => {
+        // 判断是否为空行
+        if (line.trim() === '') return;
+        let [type, x, y, width, height] = line.split(' ');
+        result.push({type, x, y, width, height});
+    });
+    return result;
+}
+
+
+async function writeFile(filePath, fileData) {
+    return new Promise((resolve, reject) => {
+        // 判断父级目录是否存在
+        let dirPath = path.dirname(filePath);
+        if (!fs.existsSync(dirPath)) {
+            fs.mkdirSync(dirPath, {recursive: true});
+        }
+
+        fs.writeFile(filePath, fileData, (err, data) => {
+            if (err) return reject(err);
+            resolve(data);
+        });
+    })
+}
+
+async function mvFile(filePath, targetPath) {
+    return new Promise((resolve, reject) => {
+        // 判断父级目录是否存在
+        let dirPath = path.dirname(filePath);
+        if (!fs.existsSync(dirPath)) {
+            fs.mkdirSync(dirPath, {recursive: true});
+        }
+        fs.rename(filePath, targetPath, (err, data) => {
+            if (err) return reject(err);
+            resolve(data);
+        });
+    })
+}
+
+// 写入文件
+async function writeFile(filePath, fileData) {
+    return new Promise((resolve, reject) => {
+        // 判断父级目录是否存在
+        let dirPath = path.dirname(filePath);
+        if (!fs.existsSync(dirPath)) {
+            fs.mkdirSync(dirPath, {recursive: true});
+        }
+        fs.writeFile(filePath, fileData, (err, data) => {
+            if (err) return reject(err);
+            resolve(data);
+        });
+    })
+}
+
+
+async function cpFile(filePath, targetPath) {
+    return new Promise((resolve, reject) => {
+        // 判断父级目录是否存在
+        let dirPath = path.dirname(targetPath);
+        if (!fs.existsSync(dirPath)) {
+            console.log(`路径${dirPath} 不存在`)
+            fs.mkdirSync(dirPath, {recursive: true});
+            console.log('路径不存在')
+        }
+        // console.log('路径不存在')
+
+        fs.copyFile(filePath, targetPath, (err, data) => {
+            if (err) return reject(err);
+            resolve(data);
+        });
+    })
+}
+
+
+// 删除指定目录
+function rmDir(path) {
+    return new Promise((resolve, reject) => {
+        fs.rmdir(path, {recursive: true}, (err, data) => {
+            if (err) return reject(err);
+            resolve(data);
+        });
+    })
+}
+
+
+const dimensionType = {
+    "-1": "无标注",
+    "0": "吊车",
+    "1": "塔吊",
+    "2": "烟火",
+    "3": "施工机械",
+    "4": "导线异物",
+    "5": "烟雾"
+}
+
+
+async function main() {
+
+    // let labelsPath = `E:\\图库\\ai\\epower_v2\\newLabels`
+    // let labelsPath = `E:\\图库\\ai\\epower_v2\\处理labels`
+    // let labelsPath = `E:\\图库\\ai\\epower_v2\\labels`
+    let labelsPath = `E:\\图库\\ai\\epower\\all_tmpLabels_2`
+
+    let imagesPath = `E:\\图库\\ai\\epower\\images`
+    let emptyPath = `E:\\图库\\ai\\epower\\empty`
+    let imageExts = ['jpg']
+    let logsPath = `E:\\图库\\ai\\epower\\logs`
+
+    // 数据转移目录
+    let transferPath = `E:\\图库\\ai\\epower\\parse`
+    // 清除数据转移目录
+    // 判断数据转移目录是否存在
+    if (fs.existsSync(transferPath)) {        // 删除数据转移目录
+        await rmDir(transferPath);
+    }
+
+    let countMap = {};
+
+    let logStrArr = [];
+    let resultStrArr = [];
+    // 获取文件列表
+    let labelFiles = await readDir(labelsPath);
+    let imagesFiles = await readDir(imagesPath);
+
+    // 获取当前时间
+    let now = new Date();
+    let nowStr = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ${now.getHours()}点${now.getMinutes()}分${now.getSeconds()}秒`;
+    resultStrArr.push(`[I] [START] 开始统计分析数据标注信息 ${nowStr}`);
+    resultStrArr.push(`[I] 原标注信息路径: ${labelsPath}`);
+    resultStrArr.push(`[I] 原图片文件路径: ${labelsPath}`);
+    resultStrArr.push(`[I] 输出文件路径: ${labelsPath}`);
+    resultStrArr.push(`[I] 输出文件路径: ${logsPath}`);
+    resultStrArr.push(`[I] 标注文件数量: ${labelFiles.length}`);
+    resultStrArr.push(`[I] 图片文件数量: ${imagesFiles.length}`);
+    resultStrArr.push(`[I] 图片后缀: ${imageExts}`);
+
+    // 判断labels中是否包含 classes.txt. 如果有则 将文件 复制至 transferPath 目录
+    if (fs.existsSync(path.join(labelsPath, 'classes.txt'))) {
+        await cpFile(path.join(labelsPath, 'classes.txt'), path.join(transferPath, 'classes.txt'))
+    }
+
+    // 将 resultArr 的内容, 转移至 logStrArr
+    resultStrArr.forEach(item => {
+        logStrArr.push(item);
+    });
+
+    for (const [i, labelFile] of labelFiles.entries()) {
+
+        let filePath = path.join(labelsPath, labelFile);
+        // 获取对应的图片路径, 与 label 文件的 名称相同
+        let labelName = labelFile.replace('.txt', '');
+        // 读取文件内容
+        let fileData = await readFile(filePath);
+        // 解析文件内容
+        let result = parseYolo(fileData);
+
+        // 图片后缀可能为 jpg, png 等
+        let imagePath = '';
+        let imageName = labelName;
+        for (let j = 0; j < imageExts.length; j++) {
+            imagePath = path.join(imagesPath, `${labelName}.${imageExts[0]}`);
+            imageName = `${labelName}.${imageExts[0]}`;
+            if (!fs.existsSync(imagePath)) {
+                imagePath = '';
+                continue;
+            }
+        }
+        // 判断图片是否存在
+        if (!imagePath) {
+            logStrArr.push(`[E] 图片不存在 ${filePath} ${fileData} 可能是图片后缀异常 [ ${imageExts.join(', ')} ]`);
+            continue;
+        }
+
+        readline.cursorTo(process.stdout, 0);
+        readline.clearScreenDown(process.stdout);
+        process.stdout.write(`${i} / ${labelFiles.length} | 解析文件: ${filePath} 中\n`);
+        // console.log(result);
+        // 空数据统计
+        if (result.length === 0) {
+            countMap['空'] = (countMap['空'] || 0) + 1;
+            // 复制图片文件到 emptyPath
+
+            let emptyFilePath = path.join(emptyPath, `${labelName}.${imageExts[0]}`);
+            logStrArr.push(`[E] ${labelName}内容为空 空文件 ${emptyFilePath}`);
+            console.log(`空文件: ${emptyFilePath}`)
+            cpFile(imagePath, emptyFilePath).then(_ => _)
+            continue;
+        }
+        // 获取文件标注类型
+        let fileType = {};
+        for (let j = 0; j < result.length; j++) {
+            // let type = item.type;
+            let typeName = dimensionType[result[j].type];
+            if (!typeName) {
+                console.log('未知类型')
+                logStrArr.push(`[E] 未知类型 ${filePath} ${fileData}`);
+                continue;
+            }
+            if (fileType[typeName]) {
+                fileType[typeName]++;
+            } else {
+                fileType[typeName] = 1;
+            }
+        }
+        console.log(fileType)
+        if (Object.keys(fileType).length === 0) {
+            countMap['异常标注'] = (countMap['异常标注'] || 0) + 1;
+            continue;
+        }
+
+        // 获取标注的所有类型, 将图片进行区分  类型1:数量  类型2:数量 类型1-类型2:数量
+        let fileTypeKeys = Object.keys(fileType);
+        // 排序
+        fileTypeKeys.sort();
+        // 数据转换
+        fileTypeKeys = fileTypeKeys.join('-');
+
+        // 转换类型
+        if (countMap[fileTypeKeys]) {
+            countMap[fileTypeKeys]++;
+        } else {
+            countMap[fileTypeKeys] = 1;
+        }
+
+
+        // 尝试复制文件
+        let transferFilePath = path.join(transferPath, fileTypeKeys);
+        let transferImagePath = path.join(transferFilePath, `images/${imageName}`);
+        let transferLabelPath = path.join(transferFilePath, `labels/${labelFile}`);
+        // 文件转移
+        // 同步复制文件
+
+        cpFile(filePath, transferLabelPath).then(_ => _)
+        cpFile(imagePath, transferImagePath).then(_ => _)
+        logStrArr.push(`${fileTypeKeys}: ${countMap[fileTypeKeys]} ${filePath}  ${imagePath} ===>  ${transferLabelPath} ${transferImagePath} `);
+        console.log(logStrArr[logStrArr.length - 1]);
+    }
+
+    // 输出结果
+    console.log(countMap);
+    // 将分析结果写入文件
+    logStrArr.push(`\r\n${JSON.stringify(countMap, null, 4)}`);
+    resultStrArr.push(`${JSON.stringify(countMap, null, 4)}`);
+    // 保存结果
+    let logStr = logStrArr.join('\r\n');
+    let resultStr = resultStrArr.join('\r\n');
+    await writeFile(path.join(logsPath, 'result.txt'), resultStr);
+    await writeFile(path.join(logsPath, 'log.txt'), logStr);
+    console.log('end');
+}
+
+
+// 将新处理的labels 重新写回至原目录
+function reCopyToRaw() {
+    let labelsPath = `E:\\图库\\ai\\epower\\newLabels`
+    // 数据转移目录
+    let transferPath = `E:\\图库\\ai\\epower\\parse`
+    // "0": "吊车",
+    // "1": "塔吊",
+    // "2": "烟火",
+    // "3": "施工机械",
+    // "4": "导线异物",
+    // 吊车-导线异物
+    // 施工机械-导线异物
+    // 塔吊-导线异物
+    // 塔吊-施工机械
+
+    let fileTypeKeys = [1, 3]
+    fileTypeKeys = fileTypeKeys.map(item => dimensionType[item] ? dimensionType[item] : '未知类型');
+    let targetType = fileTypeKeys.join('-');
+    let targetPath = path.join(transferPath, targetType);
+    let targetLabelPath = path.join(targetPath, 'labels');
+    let logStrArr = [];
+    // 遍历目录
+    let labelFiles = fs.readdirSync(targetLabelPath);
+    for (let i = 0; i < labelFiles.length; i++) {
+        let labelFilePath = path.join(targetLabelPath, labelFiles[i]);
+        // 拷贝文件
+        cpFile(labelFilePath, path.join(labelsPath, labelFiles[i])).then(_ => _)
+    }
+    console.log(targetPath);
+}
+
+
+// 复制分类好的label文件至指定目录
+async function copyLabel() {
+    // let allLabelsPath = `E:\\图库\\ai\\epower_v2\\parse`
+    let allLabelsPath = `E:\\图库\\ai\\epower\\parse`
+    // let transferPath = `E:\\图库\\ai\\epower_v2\\tmpLabels_2`
+    let transferPath = `E:\\图库\\ai\\epower\\tmpLabels_2`
+    // 获取子目录
+    let subDirs = fs.readdirSync(allLabelsPath);
+    for (let i = 0; i < subDirs.length; i++) {
+
+        if (subDirs[i] == 'classes.txt') {
+            console.log(`skip classes.txt`)
+            continue;
+        }
+        let subDir = path.join(allLabelsPath, subDirs[i], 'labels');
+        console.log(subDir)
+        // 获取子目录下的labels
+        let labelFiles = fs.readdirSync(subDir);
+        for (let j = 0; j < labelFiles.length; j++) {
+            // 获取文件路径
+            let labelFilePath = path.join(subDir, labelFiles[j]);
+            // 复制文件 至新目录 新目录: 新目录/子目录名称
+            let newDir = path.join(transferPath, subDirs[i]);
+            let newFilePath = path.join(newDir, labelFiles[j]);
+
+            // 复制文件
+            await cpFile(labelFilePath, newFilePath)
+        }
+    }
+}
+
+/**
+ * 将分类好的label文件复制至指定目录
+ * @param allClassLabelsPath
+ * @param baseImagesPath
+ * @param resultPath
+ * @return {Promise<void>}
+ */
+async function copyLabelToAll(allClassLabelsPath, baseImagesPath, resultPath) {
+    console.log(`将尝试通过分类后的label文件进行最终数据获取\n
+     labels:${allClassLabelsPath} \n
+     images:${baseImagesPath} \n
+     复制至 ${resultPath} 下`)
+    let result_labelsPath = path.join(resultPath, 'labels');
+    let result_imagesPath = path.join(resultPath, 'images');
+    // 创建目录
+    let imageExts = ['jpg']
+
+    // 获取子目录
+    let subDirs = fs.readdirSync(allClassLabelsPath);
+    for (let i = 0; i < subDirs.length; i++) {
+
+        if (subDirs[i] == 'classes.txt') {
+            console.log(`skip classes.txt`)
+            continue;
+        }
+        let subDir = path.join(allClassLabelsPath, subDirs[i]);
+        console.log(subDir)
+        // 获取子目录下的labels
+        let labelFiles = fs.readdirSync(subDir);
+        for (let j = 0; j < labelFiles.length; j++) {
+            if (labelFiles[j] == 'classes.txt') {
+                console.log(`skip classes.txt`)
+                continue;
+            }
+            let labelName = labelFiles[j].replace('.txt', '');
+            // 获取文件路径
+            let labelFilePath = path.join(subDir, labelFiles[j]);
+            // 复制文件 至新目录 新目录: 新目录/子目录名称
+            let newFilePath = path.join(result_labelsPath, labelFiles[j]);
+            // 复制文件
+            cpFile(labelFilePath, newFilePath)
+
+            // 复制图片
+            // 图片后缀可能为 jpg, png 等
+            let imagePath = '';
+            let imageName = `${labelName}.jpg`;
+            imagePath = path.join(baseImagesPath, imageName);
+
+            // 复制图片
+            let newImagePath = path.join(result_imagesPath, imageName);
+            console.log(`移动图片 ${imagePath} => ${newImagePath}`)
+            // 判断图片是否存在
+            if (!imagePath) {
+                console.log(`[E] 图片不存在 ${labelFilePath} 可能是图片后缀异常 [ ${imageExts.join(', ')} ]`);
+                continue;
+            }
+            cpFile(imagePath, newImagePath)
+        }
+    }
+}
+
+// 通过labels文件获取图片
+async function getImageByLabel(labelsPath, baseImagePath, resultPath) {
+    console.log(`将尝试通过label文件进行最终数据获取\n
+     labels:${labelsPath} \n
+     images:${baseImagePath} \n
+     复制至 ${resultPath} 下`)
+    let labelFiles = fs.readdirSync(labelsPath);
+    let result_labelsPath = path.join(resultPath, 'labels');
+    let result_imagesPath = path.join(resultPath, 'images');
+
+    for (let i = 0; i < labelFiles.length; i++) {
+        let labelFilePath = path.join(labelsPath, labelFiles[i]);
+        if (!fs.statSync(labelFilePath).isFile()) {
+            console.log(`[E] ${labelFiles[i]} 不是文件, 该函数暂不支持`)
+            continue;
+        }
+        // 获取文件名
+        let labelName = labelFiles[i].replace('.txt', '');
+        // 文件名转换为图片名
+        let imageName = `${labelName}.jpg`;
+        let result_labelFilePath = path.join(result_labelsPath, labelFiles[i]);
+        let imagePath = path.join(baseImagePath, imageName);
+        if (!fs.statSync(imagePath).isFile()) {
+            console.error(`[E] 无法找到图片文件${imagePath}`)
+            continue;
+        }
+        cpFile(labelFilePath, result_labelFilePath)
+        cpFile(imagePath, path.join(result_imagesPath, imageName))
+    }
+}
+
+/**
+ *
+ * @param input
+ * @return {*}
+ */
+
+function replaceNewlines(input) {
+    return input.replace(/(\r\n|\r|\n)/g, '\r\n');
+}
+
+// 将label 文件中的换行 从 \n 替换为 \r\n
+function relineLabel() {
+    let labelsPath = `E:\\图库\\ai\\epower\\all_tmpLabels_2`
+    // 遍历目录
+    let labelFiles = fs.readdirSync(labelsPath);
+    for (let i = 0; i < labelFiles.length; i++) {
+        let labelFilePath = path.join(labelsPath, labelFiles[i]);
+        if (!fs.statSync(labelFilePath).isFile()) {
+            continue;
+        }
+        console.log(`start reline ${labelFilePath}`);
+        let fileContent = fs.readFileSync(labelFilePath, 'utf-8');
+        fileContent = replaceNewlines(fileContent);
+        fs.writeFileSync(labelFilePath, fileContent)
+    }
+}
+
+
+// 读取classes.txt
+// 将classes.txt 中的的内容进行分组
+// 随后读取对应的文件目录, 解析xml文件 , 将其中的坐标等信息转换为yolo格式
+async function xmlToYolo(basePath, resultPath) {
+    // 读取classes.txt
+    let classesPath = path.join(basePath, 'classes.txt');
+    let classes = fs.readFileSync(classesPath, 'utf-8').split('\n');
+    let classesMap = {};
+    for (let i = 0; i < classes.length; i++) {
+        let className = classes[i].trim();
+        className = className.replace(/\s|\r\n|\n|\r/g, '');
+        classesMap[className] = i;
+    }
+    // 拷贝classes.txt
+    cpFile(classesPath, path.join(resultPath, 'classes.txt'))
+    console.log(classesMap)
+    // 遍历目录
+    let labelFiles = fs.readdirSync(basePath);
+    for (let i = 0; i < labelFiles.length; i++) {
+        let fileName = labelFiles[i];
+        let labelFilePath = path.join(basePath, fileName);
+        // console.log(`start parse ${labelFilePath}`)
+        if (fs.statSync(labelFilePath).isDirectory()) {
+            continue;
+        }
+        // 判断文件是否为xml格式
+        if (!fileName.endsWith('.xml')) {
+            console.log(`[E] ${fileName} 不是xml, 该函数暂不支持解析`)
+            continue;
+        }
+        let yoloFileName = fileName.replace('.xml', '.txt');
+        let resultFilePath = path.join(resultPath, yoloFileName);
+        await yoloXml2yolo(labelFilePath, classesMap, resultFilePath);
+    }
+
+}
+
+
+function convert(size, box) {
+    const dw = 1 / size.width;
+    const dh = 1 / size.height;
+    // 数据类型转换为float
+    box.xmin = parseFloat(box.xmin);
+    box.ymin = parseFloat(box.ymin);
+    box.xmax = parseFloat(box.xmax);
+    box.ymax = parseFloat(box.ymax);
+    const x = (box.xmin + box.xmax) / 2; // (x_min + x_max) / 2.0
+    const y = (box.ymin + box.ymax) / 2; // (y_min + y_max) / 2.0
+    const w = box.xmax - box.xmin; // x_max - x_min
+    const h = box.ymax - box.ymin; // y_max - y_min
+    const newX = x * dw;
+    const newW = w * dw;
+    const newY = y * dh;
+    const newH = h * dh;
+    return [newX, newY, newW, newH];
+}
+function _xmlToJson(xml) {
+    return new Promise((resolve, reject) => {
+        let parser = new xml2js.Parser({
+            explicitArray: false,
+            mergeAttrs: true,
+            explicitRoot: false
+        });
+        parser.parseString(xml, function (err, result) {
+            // console.log(result)
+            resolve(result);
+        });
+    })
+}
+/**
+ * 将yolo xml文件转换为yolo格式
+ * @param xmlPath
+ * @param classMap
+ * @param resultPath
+ */
+async function yoloXml2yolo(xmlPath, classMap, resultPath) {
+    let xml = fs.readFileSync(xmlPath, 'utf-8');
+    // console.log(xml)
+    let obj = await _xmlToJson(xml)
+    // 直接生成对应的 x1,y1, x2,y2
+    // console.log(obj)
+    let width = obj.size.width;
+    let height = obj.size.height;
+    let str = "";
+    // 如果只有一个对象则
+    let objects = []
+    if (!obj.object.length){
+        console.log(`${obj.filename} object is object`)
+        console.log(obj)
+        objects.push(obj.object)
+    }else {
+        objects = obj.object
+    }
+    for (let i = 0; i < objects.length; i++){
+        let sub = objects[i];
+        // console.log(sub)
+        let objName = classMap[sub.name];
+        let arr = convert(obj.size, sub.bndbox)
+        str += `${objName} ${arr.join(" ")}\r\n`
+    }
+    // console.log(str)
+    // 创建并写入文件
+    await writeFile(resultPath, str)
+}
+
+
+
+xmlToYolo("E:\\图库\\验证数据集\\labels - 副本", "E:\\图库\\验证数据集\\result")
+
+
+// main();
+
+/**将分类好的label文件, 拷贝至新目录 parse  => newLabels
+ 只会拷贝对应类型的文件
+ */
+// reCopyToRaw();
+
+
+/**
+ * 将分类好的label文件, 拷贝至新目录 parse  => tmpLabels_2
+ * 只拷贝对应的label文件
+ *
+ */
+// copyLabel();
+
+/**
+ * 将分类好的label文件, 统一合并至一个文件夹内, 分类好的labels 应该是labels的子目录
+ * 并且拷贝对应的图片
+ * 分类好的label文件目录: tmpLabels_2
+ * 基础图片目录: images
+ * 输出label目录: all_tmpLabels_2
+ * 输出图片目录: all_tmpImages_2
+ */
+
+// 分类好的label文件目录 E:\图库\ai\epower\tmpLabels_2
+let allClassLabelsPath = `E:\\图库\\ai\\epower\\验证数据集_labels`
+// 基础图片目录
+let baseImagesPath = `E:\\图库\\ai\\epower\\images`
+// 输出目录
+let resultPath = `E:\\图库\\ai\\epower\\验证数据集`
+// copyLabelToAll(allClassLabelsPath, baseImagesPath, resultPath)
+
+// getImageByLabel(`E:\\图库\\ai\\epower\\验证数据集_labels`, `E:\\图库\\ai\\epower\\images`, resultPath)
+// relineLabel();
+

+ 47 - 0
js/数独计算器.js

@@ -0,0 +1,47 @@
+// 杀手数独计算器
+//
+// 规则
+// 1. 杀手框内的数字不能重复
+// 2. 杀手框内的数字之和等于杀手框的和
+
+/**
+ * 计算杀手框 中可能的组合
+ * @param sum 总和
+ * @param itemNum 元素数量
+ */
+function cal(sum, itemNum){
+    let result = [];
+    let arr = [];
+    let temp = [];
+    for(let i = 1; i <= 9; i++){
+        arr.push(i);
+    }
+    // console.log(arr);
+    function dfs(arr, temp, sum, itemNum){
+        if(temp.length === itemNum){
+            if(temp.reduce((a, b) => a + b, 0) === sum){
+                result.push(temp.slice());
+            }
+            return;
+        }
+        for(let i = 0; i < arr.length; i++){
+            temp.push(arr[i]);
+            dfs(arr.slice(i + 1), temp, sum, itemNum);
+            temp.pop();
+        }
+    }
+    dfs(arr, temp, sum, itemNum);
+    return result;
+}
+
+console.log(cal(6, 3));// 1, 2, 3
+console.log(cal(7, 2));//1,6 | 2,5 | 3,4
+console.log(cal(8, 2));// 1,7 | 2,6 | 3,5
+
+console.log(cal(12, 2));// 3, 9  | 4, 8  | 5, 7
+
+console.log(cal(13, 2));//4,9 | 5,8 | 6,7
+console.log(cal(16, 4));
+console.log(cal(24, 3));// 7,8,9
+console.log(cal(24, 4))//1689,25892679
+

+ 60 - 14
js/进制转换/test.js

@@ -34,20 +34,66 @@ function parseRtpHeader(hexStr){
         time: _time
     }
 }
-console.log(parseInt(hexStrToBitStr('80 08'),2));
-console.log(parseInt(hexStrToBitStr('00 5b'),2));
-console.log(parseInt(hexStrToBitStr('00 5d'),2));
-console.log(parseInt(hexStrToBitStr('01 e2'),2));
-console.log(parseInt(hexStrToBitStr('00 5a'),2));
-console.log(parseInt(hexStrToBitStr('00 5a'),2));
-console.log(parseInt(hexStrToBitStr('00 5a'),2));
-
-console.log(parseRtpHeader('80080106b387a72000000194'));
-console.log(parseRtpHeader('80080108b387a86000000194'));
-console.log(parseRtpHeader('8008010bb387aa4000000194'));
-console.log(parseRtpHeader('8008010cb387aae000000194'));
-console.log(parseRtpHeader('8008010db387ab8000000194'));
-console.log(parseRtpHeader('8008010eb387ac2000000194'));
+console.log(parseInt(hexStrToBitStr('00 10'),2));
+// console.log(parseInt(hexStrToBitStr('00 5b'),2));
+// console.log(parseInt(hexStrToBitStr('00 5d'),2));
+// console.log(parseInt(hexStrToBitStr('01 e2'),2));
+// console.log(parseInt(hexStrToBitStr('00 5a'),2));
+// console.log(parseInt(hexStrToBitStr('00 5a'),2));
+// console.log(parseInt(hexStrToBitStr('00 5a'),2));
+
+let lineStr = "| 黑屏设备包头解析 | 不黑屏设备解析 |"
+console.log(lineStr);
+
+// rtp黑屏
+// 00 10 80 e0 00 02 00 00 46 50 01 e1 5e 79 68 ce 31 b2 00 11 80 e0 00 03 00 00 69 78 01 e1 5e 79 06 e5 01 42 80 05 78 80 60 00 04 00
+//
+
+
+// console.log(parseRtpHeader('00 10 80 e0 00 02 00 00 46 50 01 e1 5e 79 68 ce 31 b2 00 11 80 e0 00'));
+// console.log(parseRtpHeader('80 e0 00 02 00 00 46 50 01 e1 5e 79 68 ce 31 b2'));
+console.log(parseRtpHeader('80 e0 00 02 00 00 46 50 01 e1 5e 79'));
+// {
+//     version: 2,
+//     padding: '0',
+//     extension: '0',
+//     csrcCount: '0000',
+//     marker: '1',
+//     payloadType: 96,
+//     sequenceNumber: 2,
+//     timestamp: 18432,
+//     ssrc: '00000000000000000000000000000000',
+//     csrc: '',
+//     time: '1970-01-01 08:00:18'
+// }
+
+// 正常播放
+// 00 10 80 60 00 02 3b 09 a5 d0 01 df 5a d9 68 ee 31 b2 00 11 80 60 00 03 3b 09 a5 d0 01 df 5a d9 06 e5 01 cb 80 05 86 80 60 00 04 3b
+// console.log(parseRtpHeader('00 10 80 60 00 02 3b 09 a5 d0 01 df 5a d9 68 ee 31 b2 00 11 80 60 00'));
+// console.log(parseRtpHeader('00 10 80 60 00 02 3b 09 a5 d0 01 df 5a d9 68 ee'));
+console.log(parseRtpHeader('80 60 00 02 3b 09 a5 d0 01 df 01'));
+
+// {
+//     version: 2,
+//     padding: '0',
+//     extension: '0',
+//     csrcCount: '0000',
+//     marker: '0',
+//     payloadType: 96,
+//     sequenceNumber: 2,
+//     timestamp: 990488576,
+//     ssrc: '000000000000000000000000',
+//     csrc: '',
+//     time: '1970-01-12 19:08:08'
+// }
+
+
+
+// console.log(parseRtpHeader('80080108b387a86000000194'));
+// console.log(parseRtpHeader('8008010bb387aa4000000194'));
+// console.log(parseRtpHeader('8008010cb387aae000000194'));
+// console.log(parseRtpHeader('8008010db387ab8000000194'));
+// console.log(parseRtpHeader('8008010eb387ac2000000194'));
 
 // console.log(parseRtpHeader('00 04 d4 91 5e 08 07 27 11 e8 d6 d4'));
 // console.log(parseRtpHeader('00 05 d4 91 5e a8 07 27 11 e8 d5 57'));

+ 0 - 0
linux/多版本软件安装.md


+ 13 - 9
linux/常用命令.md

@@ -1,5 +1,5 @@
 # linux 下常用命令笔记
-## cp
+## 复制 `cp`
 ### 作用
 复制文件或目录至指定位置
 ### 示例 复制目录
@@ -25,21 +25,20 @@ cp /home/abc /home/def /home/ghi /home/jkl # 复制多个文件至指定目录
 ```
 
 ### 可选参数
-#### -r
-1. 作用  
-> 复制目录,复制目录时必须加上此参数
-2. 示例
+#### 复制目录 `-r`
+##### `#d` 目录复制  
+复制目录,复制目录时必须加上此参数
 ```shell
 cp -r /home/abc /home/def # 复制目录
 ```
-#### -f
-1. 作用
-> 强制复制,如果目标文件已经存在,不会询问用户,直接覆盖
+#### 强制复制 `-f`
+##### `#d` 强制复制
+如果目标文件已经存在,不会询问用户,直接覆盖
 2. 示例
 ```shell
 cp -f /home/abc /home/def # 强制复制
 ```
-#### -i
+#### 交互式操作 `-i`
 1. 作用
 > 交互式复制,在覆盖已存在的目标文件之前提示用户
 2. 示例
@@ -47,3 +46,8 @@ cp -f /home/abc /home/def # 强制复制
 cp -i /home/abc /home/def # 交互式复制
 ```
 
+## 管理磁盘空间 
+### 查询指定目录下文件占用大小
+```shell
+sudo du -lh /home/ubuntu/ --max-depth=2
+```

+ 69 - 0
linux/环境管理.md

@@ -0,0 +1,69 @@
+# 环境管理记录文档
+> 该文档记录了在实际维护使用过程中记录下的一些关键的环境配置相关信息
+
+## 环境维护
+
+### ssh服务安装
+> ssh服务是linux系统自带的,但是部分软件需要手动安装ssh服务
+#e 安装示例
+```shell
+sudo apt-get install openssh-server
+```
+#e 启动服务
+```shell
+sudo service ssh start
+```
+
+### openssl 管理
+> 因为openssl是系统自带的,但是在某些情况下, 部分软件所依赖的openssl可能是较新的版本,所以需要手动安装。
+
+#c 获取openssl  
+我们可以通过 git clone 获取openssl源码,该方法会获取最新版本,可能不适用
+也可以通过 `wget` 获取指定版本的openssl源码
+这些两种方法都可以获取到openssl源码, 但是都需要编译安装后才能使用
+
+
+#e 安装示例  
+wget获取源码
+```shell
+wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz
+tar -zxvf openssl-1.1.1g.tar.gz
+cd openssl-1.1.1g
+```
+git获取源码
+```shell
+git clone https://github.com/openssl/openssl.git
+cd openssl
+```
+编译安装
+```shell
+
+./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared zlib zlib-dynamic
+make -j 8
+make install
+```
+配置软链接
+```shell
+ln -s /usr/local/openssl/lib /usr/bin/openssl
+```
+
+
+#### openssl安装管理
+> 如果按照往上的方法安装后, 导致openssl版本移除, 则需要手动软链接, 保证其能够切换至对应版本
+
+#d `whereis`管理  
+该命令用于查找指定内容,并显示其路径。可以显示多个内容,用空格分隔。
+用于在不知道指定内容在哪个路径下时使用。
+
+#e 寻找openssl
+```shell
+whereis openssl
+```
+输出:
+```shell
+openssl: /usr/bin/openssl /usr/lib64/openssl /usr/local/openssl /usr/share/man/man1/openssl.1ssl.gz
+```
+
+#c 管理
+
+

+ 84 - 0
linux/环境管理_软连接.md

@@ -0,0 +1,84 @@
+# 软链接管理
+> [!tips] 该文档测试环境为 center os
+
+## 软链接管理
+#d 软链接是什么  
+软链接是文件系统中的一种特殊类型文件,它指向另一个文件或目录,而不是直接存储数据。
+访问软链接,会直接访问被软链接的文件或目录。
+
+### 软连接创建与移除
+#d 软链接创建
+软链接创建:将指向另一个文件或目录的软链接,可以使用 ln 命令。
+在创建后访问软链接,会直接访问被软链接的文件或目录。
+
+#e openssl
+```shell
+sudo ln -s /usr/local/openssl/lib/libcrypto.so.1.1 /usr/lib/libcrypto.so.1.1
+
+```
+
+#d 移除软链接
+移除软链接:可以使用 rm 命令。也可以使用 unlink 命令。
+在使用 rm 命令移除软链接时,会删除软链接本身,而不会删除被软链接的文件或目录。
+
+#e 使用rm 命令移除软链接
+```shell
+rm /usr/local/bin/openssl
+```
+
+#d unlink介绍  
+unlink命令是 rm 命令的替代品,用于删除软链接。
+unlink命令不会删除被软链接的文件或目录,只会删除软链接本身。
+所以unlink命令更适合删除软链接。
+
+#e 使用unlink命令移除软链接
+```shell
+unlink /usr/local/bin/openssl
+```
+
+```shell
+sudo unlink /usr/lib/libssl.so
+sudo unlink /usr/lib64/libssl.so
+sudo unlink /usr/lib/libcrypto.so
+sudo unlink /usr/lib64/libcrypto.so
+
+sudo unlink /usr/lib/libssl.so.3
+sudo unlink /usr/lib64/libssl.so.3
+sudo unlink /usr/lib/libcrypto.so.3
+sudo unlink /usr/lib64/libcrypto.so.3
+```
+
+#e 软连接创建
+```shell
+sudo ln -s /usr/local/openssl/lib/libcrypto.so /usr/lib/libcrypto.so
+sudo ln -s /usr/local/openssl/lib/libssl.so /usr/lib/libssl.so
+sudo ln -s /usr/local/openssl/lib64/libcrypto.so /usr/lib64/libcrypto.so
+sudo ln -s /usr/local/openssl/lib64/libssl.so /usr/lib64/libssl.so
+
+sudo ln -s /usr/local/openssl/lib/libcrypto.so /usr/lib/libcrypto.so.3
+sudo ln -s /usr/local/openssl/lib64/libcrypto.so /usr/lib64/libcrypto.so.3
+sudo ln -s /usr/local/openssl/lib/libssl.so /usr/lib/libssl.so.3
+sudo ln -s /usr/local/openssl/lib64/libssl.so /usr/lib64/libssl.so.3
+```
+
+
+#此镜像为github action 持续集成自动编译推送,跟代码(master分支)保持最新状态
+不映射端口模式
+docker run -id -p 30010-30300:30010-30300 \
+--name zlmediakit \
+--net=host \
+-e TZ="Asia/Shanghai" \
+-v /home/kindring/zlm/run/log:/opt/media/bin/log \
+-v /home/kindring/zlm/run/conf:/opt/media/conf \
+zlmediakit/zlmediakit:master
+
+
+docker run -id --net=host \
+--name zlmediakit \
+-e TZ="Asia/Shanghai" \
+-v /home/kindring/zlm/run/log:/opt/media/bin/log \
+-v /home/kindring/zlm/run/conf:/opt/media/conf \
+zlmediakit/zlmediakit:master
+
+#c 刷新环境信息
+`ldconfig` 用于刷新环境信息,使系统能够正确识别和加载动态链接库。

+ 28 - 0
mysql/语法.md

@@ -30,3 +30,31 @@ select * from user
 select id,name,descript from user
 ```
 
+## 新增mysql 用户
+```mysql
+create user '用户名'@'%' identified by '密码';
+```
+#e 例子
+创建用户名为`site` 密码为`2468123`的mysql用户,  
+%表示允许用户从任何主机连接, 可以替换为ip地址等  
+localhost表示只允许本机连接
+```mysql
+CREATE USER 'site'@'%' IDENTIFIED BY '2468123';
+```
+### 授权用户
+```mysql
+grant all on 数据库名.表名 to '用户名'@'%';
+```
+#e 例子
+授权用户名为`site` 数据库名为`test` 表名为`user`的权限  
+也可以替换为 test.* 表示所有表
+```mysql
+grant all on test.user to 'site'@'%';
+```
+
+### 刷新权限
+```mysql
+flush privileges;
+```
+
+

+ 46 - 0
python相关/ai识别/ai识别训练环境配置.md

@@ -0,0 +1,46 @@
+# ai识别环境配置
+## 查看环境
+#c 环境检查
+ai识别训练因为使用`cuda`,和`python`, 所以我们需要检查一下对应的版本
+#e `python`版本查看
+```shell
+python -V
+```
+或者
+```shell
+python --version
+```
+
+#e `cuda`版本查看
+```shell
+nvcc -V
+```
+
+#e `torch`版本查看
+```shell
+pip show torch
+```
+#c 切换`torch`版本
+可能有时候需要使用特定的`torch`版本,  
+在此时需要先使用`pip`卸载`torch`随后在安装新的`torch`版本
+#e 卸载`torch`
+```shell
+pip uninstall torch
+```
+
+#e 安装特定`torch`版本
+```shell
+pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
+```
+
+## 安装必要的环境
+#c 安装 cuda
+#c 安装 cudnn
+
+
+
+
+
+
+
+

+ 116 - 0
python相关/py环境处理.md

@@ -0,0 +1,116 @@
+# python 环境搭建
+#c py环境复杂  
+在实际的工程应用中,  
+经常会需要依赖python环境,  
+但是不同的工程需要不同的python环境,  
+所以我们需要针对性的创建对应的python环境.
+
+#d 系统多样性  
+在不同的系统下,我们有不同的python环境安装管理方式
+
+## ubuntu下管理
+#d python版本查看  
+一般在命令行输入`python -V`或者`python --version`   
+即可查看python版本
+
+#e 查看已经安装的python  
+下面命令可以列出已经安装的python
+```shell
+ls /usr/bin/python*
+```
+
+#d 配置默认`python`版本  
+在ubuntu下,默认的python版本是2.7,   
+但是部分软件可能无法正确处理依赖去使用`python3`,  
+需要手动配置一下默认的python版本.
+
+#d 用户环境与系统环境  
+用户环境一般使用`~/.bashrc`文件来配置,  
+用户环境配置下仅针对当前用户生效,  
+可能有的软件需要配置系统的环境
+
+
+#e 设置用户默认python  
+编辑`~/.bashrc`文件  
+```shell
+vim ~/.bashrc
+```
+输入`i`进入编辑模式,
+在文件末尾添加如下内容:
+```shell
+alias python=/usr/bin/python3
+alias pip=/usr/bin/pip3
+```
+刷新环境
+```shell
+source ~/.bashrc
+```
+查看配置是否生效
+```shell
+python -V
+pip -V
+```
+
+#e 配置系统环境
+使用软连接的方式配置系统环境  
+获取本机安装的python环境路径
+```shell
+ls /usr/bin/python*
+```
+安装指定版本的python, 例如python3.7
+```shell
+sudo apt install python3.7
+```
+
+移除默认的python环境
+```shell
+rm /usr/bin/python
+rm /usr/bin/pip
+```
+
+连接新的python环境
+```shell
+ln -s /usr/bin/python3.7 /usr/bin/python
+```
+查看配置是否生效
+```shell
+python -V
+```
+
+#d 配置`pip`  
+一般情况下`pip`会跟随`python`环境一起安装,   
+切换python环境时`pip`也会跟着切换,
+但是有时候我们可能需要手动配置`pip`的环境,  
+可以输入`pip -V`查看`pip`的版本,以及对应的python版本,
+
+#e `pip`环境查看  
+```shell
+pip -V
+```
+可以看见如下输出, 可以看见当前的pip版本是23.2.1, 对应的python版本是3.8
+```result
+pip 23.2.1 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8)
+```
+
+### `python`虚拟环境
+#d 虚拟环境  
+在实际工程中,我们可以为每一个工程创建一个虚拟环境,  
+这样该工程可以使用单独的python环境  
+虚拟环境可以隔离不同工程之间的依赖  
+
+#e 安装虚拟环境  
+安装虚拟环境需要依赖`venv`模块,
+```shell
+sudo apt install python3.7-venv
+```
+
+#e 设置虚拟环境  
+创建并使用虚拟环境
+```shell
+python3.7 -m venv ./.env
+source ./.env/bin/activate
+```
+#e 退出虚拟环境
+```shell
+deactivate
+```

+ 45 - 0
vue/vue2 下css技巧.md

@@ -0,0 +1,45 @@
+# vue下动态css技巧
+> 该文章主要适用于vue2.x 的项目
+## 不倾入的方式修改style属性
+#c 直接修改style属性  
+在vue 项目中,可以通过`:style`的方式动态修改style的属性.  
+但是这种方法会因为`style`属性在css选择器中的优先级问题,  
+导致后续其它功能无法修改style属性. 所以这种方法在某些情况下,  
+不能满足需求. 虽然可以给后续的属性添加`!important`属性来达到覆盖的目的,  
+但是这样写代码,会增加代码的复杂度.所以我们可以通过`css变量`的方式来达到修改style属性的目的.
+
+#d css变量  
+css变量是css3新特性,通过`--`开头的变量,可以定义变量,
+然后通过`var()`函数,来引用变量.
+
+#e css变量修改style属性  
+示例vue代码, 修改data中的color属性,  
+从而实现文字默认颜色可以根据data中的color属性来修改文字颜色,  
+并且在鼠标移动上去时,字体颜色为蓝色
+```vue
+<script>
+  export default {
+      data(){
+          return {
+              color:'red'
+          }
+      }
+  }
+</script>
+<template>
+  <span class="test"
+       :style="`--color: ${color};`"
+  >
+    hello
+  </span>
+</template>
+<style>
+  .test{
+    --color:red;
+    color:var(--color);
+  }
+  .test:hover{
+    color:blue;
+  }
+</style>
+```

+ 139 - 0
vue/vue下使用SVG.md

@@ -0,0 +1,139 @@
+# 在vue项目中使用SVG
+> 本文包含了在vue项目中使用SVG的方法, 包含vue2.x和vue3.x
+
+## vue2.x使用
+#c 依赖组件介绍
+我们主要使用 `svg-sprite-loader` 来引入组件  
+使用webpack中的require.context来批量引入svg文件
+
+#d 新旧vue cli 差异
+> [新版本vue cli项目无法生效参考文章](https://juejin.cn/post/7133523073680015396)  
+
+在新版本的vue cli项目中更改了svg文件的加载方式  
+所以我们需要将新版本的svg加载项进行更改, 删除新增的内容
+
+
+#e 配置svg文件的加载
+vue.config.js 配置如下
+```js
+
+const path = require('path')
+function resolve(dir) {
+    // console.log(dir);
+    return path.join(__dirname, dir);
+}
+
+
+module.exports = defineConfig({
+    ...outherConfig,
+    chainWebpack: config => {
+        config.plugin('define').tap(args=>{
+            const argv = process.argv
+            const icourt = argv[argv.indexOf('--icourt-mode') + 1]
+            args[0]['process.env'].MODE = `"${icourt}"`
+            return args
+        })
+        const svgRule = config.module.rule('svg')
+        svgRule.uses.clear()
+        // 清除已有的svg loader https://juejin.cn/post/7133523073680015396
+        svgRule.delete('type');
+        svgRule.delete('generator');
+        svgRule.exclude.add(/noe_modules/)
+        svgRule
+            .test(/\.svg/)
+            .use('svg-sprite-loader')
+            .loader('svg-sprite-loader')
+            .options({
+                symbolId: 'icon-[name]'
+            })
+        const imagesRule = config.module.rule('images')
+        imagesRule.exclude.add(resolve('./src/assets/svg'))
+        // console.log('imagesRule',imagesRule)
+        config.module
+            .rule('images')
+            .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
+
+    }
+})
+```
+
+
+#c 注册并引用svg组件
+使用 webpack 中的require.context来批量引入svg文件,  
+并将svg组件注册为vue组件, 这样我们就可以在项目中使用svg了  
+为了能够同时使用网络上的svg图标, 设置组件参数`svgHref`属性,  
+用于直接引入网络上的svg图标  
+
+#e 注册与组件
+这里我们将svg文件统一放置于`src/assets/svg`目录中, `@`为`src`的别名(alias)
+iconIndex.js
+```js
+import Vue from 'vue'
+import SvgIcon from './iconSvg' // svg组件
+
+// 注册到全局
+Vue.component('svg-icon', SvgIcon)
+
+const requireAll = requireContext => requireContext.keys().map(requireContext)
+const req = require.context('@/assets/svg', false, /\.svg$/)
+requireAll(req)
+```
+svg-icon.vue
+```vue
+<template>
+  <svg class="svg-icon" aria-hidden="true">
+    <image v-if="svgHref" :xlink:href="svgHref" />
+
+    <use v-else :xlink:href="_iconName"/>
+<!--    使用 链接 加载 svg-->
+  </svg>
+</template>
+
+<script>
+export default {
+  name: 'SvgIcon',
+  props: {
+    iconClass: {
+      type: String,
+      default: '',
+    },
+    svgHref: {
+      type: String,
+      default: '',
+    },
+  },
+  computed: {
+    _iconName () {
+      return `#icon-${this.iconClass}`
+    },
+  },
+}
+</script>
+
+<style scoped>
+.svg-icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+.svg-icon image {
+  width: 100%;
+  height: 100%;
+  fill: currentColor;
+}
+</style>
+```
+
+#e 使用组件
+在对应的svg目录放置好svg文件后, 在需要使用的地方直接调用组件即可  
+例如放置 user.svg, 在使用的时候只需要使用如下代码即可,  
+`icon-class`属性填写svg文件的文件名即可
+```vue
+<template>
+  <div class="div">
+    <svg-icon icon-class="user" />
+  </div>
+</template>
+```

+ 48 - 0
安卓开发/安卓开发记录.md

@@ -0,0 +1,48 @@
+# 安卓开发记录踩坑
+
+## 注意事项
+
+### 布局文件管理
+
+#### `#d` 安卓特性 | 为什么这样做 
+
+在安卓项目中`布局文件`全部平铺放在 `res.layout` 目录下,  
+所以我们需要对`布局文件`设定对应的命名规范, 方便管理维护`布局文件`
+
+#### `#d` 命名原则
+为了方便辨识, 我们使用 `作用域_[?作用]..._[?名称]` 的方式进行命名
+
+#### `#e` 公共模块 | 顶部导航栏
+
+这是一个公共文件所以我们用 `com` 进行开头,  
+表明是个公共组件, 因为是起到布局作用的 `xml` 文件
+所以我们使用用 `layout` 来标识.  
+顶部导航栏我们可以用 `header` 或者 `nav_header` 来表示
+最终的文件名可以是 `com_layout_header`  或者 `com_layout_nav_top`  
+
+#### `#e` 回放页面 | 页面主要部分
+
+这是一个功能页面的功能页面部分,  
+我们可以直接用该功能页面的名称来进行命名起始 `playback`  
+因为是主要的布局, 所以后面可以直接接一个特殊的关键字进行标识  
+`view` , `page` 或者 `layout` 都是可以的  
+该文件的命名可以为 `playback_view` `.xml`  
+
+#### `#e` 回放页组件 | 回放列表项
+
+因为安卓的 `RecyclerView` 组件的特性,  
+我们可以将`列表项`的布局部分给分离出来, 以方便管理  
+这是回放页面中独有的组件, 所以我们将作用域设置为 `playback`  
+这是一个列表相关的组件,所以我们用 `list` 来表示主作用,  
+随后我们再用 `item` 来表示这是单独的项,  
+随后可以接这部分的名字 `playbacks`,用以区分(也可以不进行区分,如果内容较少的话)  
+该文件可以命名为 `playback_list_item_playbacks` `.xml` 或者 `playback_list_item`
+
+## 使用技巧
+
+### 回调函数
+
+#### `#d` 实现原理 | 依赖注入
+
+通过实现接口函数的方法将对应的方法实现,  
+将对象作为参数传递进去,用于实现回调函数的效果  

二進制
日常/img.png


+ 7 - 3
渐构/拆书/cPrimerPlus → 渐构/拆书/cPrimerPlus.md

@@ -1,5 +1,7 @@
 # C Primer Plus
+
 ## 转换说明
+
 ### printf 格式化原理
 
 示例代码
@@ -13,14 +15,16 @@ printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
 ```
 
 1. 在调用 printf 时 程序将传入的值放入 [栈]
-2. 不同的类型的值分配不同的 内存 
+2. 不同的类型的值分配不同的 内存  
+
 > `float` 将会被转换为 `double` 类型, 所以占据八个字节  
 
 内存空间表示,单位字节
 | 0 - 7 | 8 - 15 | 16 - 19 | 19 - 23|
 | --- | --- | --- | --- |
 | n1 | n2 | n3 | n4 |
-3. printf 函数开始根据 [转换说明] 从 [栈] 中读取对应的值
+
+1. printf 函数开始根据 [转换说明] 从 [栈] 中读取对应的值
 `%ld`说明应该读取 4字节  
 但是 n1 所占空间为 8字节,所以只能读取 n1 的前半部分
-四个`%ld`只能读取16字节, 所以 n3 与 n4 是无法被读取的
+四个`%ld`只能读取16字节, 所以 n3 与 n4 是无法被读取的  

+ 5 - 0
游戏制作/地图生成研究.md

@@ -0,0 +1,5 @@
+# 地图生成研究
+## 关于如何存储地图数据
+1. 存储地图数据
+> 使用chunk 进行存储, 一个区块配置 
+

+ 138 - 0
游戏制作/游戏设定.md

@@ -0,0 +1,138 @@
+# 玩法设定
+## 基础玩法
+
+通过探索,挖掘,战斗 搜集素材, 武器, 道具  
+随后将搜集到的素材用于锻造道具( 方块, 武器, 工具)  
+组合法术用于大幅度提升战斗力.  
+
+修建庇护所. 抵抗更多怪物  
+升级角色获取基础属性, 以及永久buff
+永久buff可以重复获取,但是技能的buff只能获取一次.  
+祭坛任务获取部分永久buff或者技能
+击败6个祭坛守护者. 使用祭坛召唤最终boss. (不同的祭品召唤不同的boss)  
+游戏关卡划分.一共七层  
+击败最终boss获取最终胜利
+
+
+## 核心机制
+组合法术  
+射击击杀怪物  
+破坏创建方块.用于获取更多优势  
+
+## 玩家能力
+1. 角色升级
+2. 跳跃
+
+## 装备系统
+> 一个玩家只能同时携带4件装备. 不限制类型
+> 不同的
+### 装备分类  
+近战
+> 对方块伤害正常  
+
+- 给法术添加切割伤害  
+- 法术速度增加  
+- 射程减少  
+- 拥有重击模组  
+- 散射少
+- 无下坠
+- 法术格少
+
+枪(法杖)  
+- 拥有后座, 连续释放会让子弹超一个方向散射
+- 射程正常  
+- 法术速度正常  
+- 自瞄(需要再一定距离内, 且与法线内的)
+- 手动发射速度远
+- 法术格多
+- 无法安装近战法术
+
+
+工具  
+> 采集材料, 挖掘
+- 方块伤害两倍
+- 可以安装地形类法术
+- 法术格一般
+
+护甲
+- 提供最大生命值
+- 伤害减免,(减少收到的部分伤害)
+- 部分套装拥有特殊效果
+- 无法嵌套
+- 减少摔落伤害
+
+
+### 装备获取
+1. 怪物掉落(几率低, 千分制)
+2. 奖励箱获取
+3. 击败boss获取
+
+### 装备等级
+装备可以通过锻造进行升级  
+装备品质决定装备可升级的上限(前期爆出一个神器可以用到死)
+
+装备等级决定如下内容
+1. 法力恢复速度
+2. 施法间隔
+3. 释放延迟
+4. 装备基础伤害
+5. 法术释放速度
+
+### 装备分解
+
+
+## 法术系统
+### 攻击类型
+1. 刺杀(从背后攻击即可, 无弱点攻击)
+2. 切割(对肉体伤害高,但是可能自伤, 对护甲伤害少)
+3. 爆炸(高效攻击手段)
+4. 打击(肉体伤害低, 护甲伤害高, 拥有一定的击退效果)
+5. 属性伤害(再面对部分怪物时,属性伤害极为高效)
+6. 普通伤害(虽然没什么特殊效果, 但是可以施加各种的效果)
+
+### 法术分类
+1. 投射物
+- 触发
+- 定时
+
+2. 爆炸物
+- 定时
+
+3. 刀气(近战专属)
+
+4. 回绕触发法术
+
+5. 强化类法术
+- 伤害强化
+- 持续时间增加(可强化定时)
+- 添加特殊效果, 法术追钟
+- 多发法术
+
+
+## 游戏背景
+在2292年,外交部的天文学家在观察遥远的GMX星球时,意外发现了该星球上频繁而规律的闪烁现象。  
+这一现象引起了科学界的广泛兴趣,经过数年的系统观察,  
+研究团队通过指定的起始频率和精准的时间间隔,成功解码出了一系列符号,  
+前四位质数(2, 3, 5, 7)被清晰传达。
+甚至研究团队发现, 这些符号中居然包含了人类目前
+这种信息的传递被推测为符合部分物理定律,尤其是信息传递理论和量子通信原理。  
+研究者们猜测,GMX星球的生物或文明可能利用量子纠缠现象进行远距离通信,从而实现了高效的信息传递。
+
+随着研究的深入,语言学家们对闪烁信息进行解读,发现其中还有一个复杂的句子结构,神秘的邀请信似乎不仅仅是简单的接触请求,  
+而是传递了一则巨大的警告,暗示着星球上存在着不可预测的力量和潜在的危险。  
+这一新信息勾勒出了一幅图景:在遥远的星空背后,隐藏着古老的文明和失落的科技,值得探索。
+
+为了揭开这一谜团,主星决定组建多支探索小队,  
+集合来自不同领域的顶尖人才,包括科学家、工程师、探险家和历史学家。  
+玩家作为其中一支探索小队的成员,配备顶尖科技装备,踏上了前往GMX星球的旅程。  
+然而,这并不是一条简单的旅程。
+
+在玩家小队即将登陆GMX星球时,意外遭遇了一种未知的异常生物,导致小队全员被团灭,  
+令人震惊的失利让人心中沉重。  
+而此时,玩家在辅助机器人“阿尔法”的帮助下开始探索这个神秘的星球,  
+逐渐揭开隐藏在其表层的魔幻秘密。  
+在探险的过程中,玩家会收集解密信件中的信息,  
+并利用物理学原理来理解和利用星球上的奇异现象,如重力波动、暗能量等,
+从而找到前往更深层区域的方法。
+
+

Some files were not shown because too many files changed in this diff