小漠的博客
type: channel
link: https://www.liaosearch.com/
description: 关注博客和手机以及互联网，记录生活点滴，分享所见所闻。
items: dart下使用onnxruntime使用ppocrv5的模型进行图片识别
items: onnxruntime编译
items: 验证码特征收集
items: mac m1下使用HY-MT1.5-1.8B翻译工具
items: paddleocrv5解析视频字幕，并且生成srt字幕文件
items: 英文句子语义修复模型
items: 纯真数据库转换
items: mongodb导入
items: 下载到内存中播放图片
items: mtqq docker-compose部署

dart下使用onnxruntime使用ppocrv5的模型进行图片识别
type: item
date: 2026-04-06T18:49:28+08:00
description: // 初始化模型 await PPOCRv5.init(); print("✅ 模型初始化成功"); final startTime = DateTime.now(); print("⏱️ 开始识别：${startTime.toLocal()}"); // 识别 assets 图片 final result = await PPOCRv5.fromAsset('assets/images/test4.png'); print("识别结果：$result"); // 结束计时+打印 final endTime = DateTime.now(); final cost = endTime.difference(startTime); print("🏁 结束识别：${endTime.toLocal()}"); print("⏱️ 耗时：${cost.inMilliseconds}ms / ${cost.inSeconds}s"); print("结果：$result"); import 'dart:math' as math; import 'dart:typed_data'; import 'package:flutter/services.dart' show rootBundle; import 'package:flutter_onnxruntime/flutter_onnxruntime.dart'; import 'package:image/image.dart' as img; import 'package:opencv_dart/opencv.dart' as cv; class PPOCRArgs { String detModel = 'assets/models/det_infer.onnx'; String recModel = 'assets/models/rec_infer.onnx'; String dictPath = 'assets/models/ppocrv5_dict.txt'; String? clsModel; int detLimitSideLen = 960; String detLimitType = 'max'; double detDbThresh = 0.3; double detDbBoxThresh = 0.6; double detDbUnclipRatio = 1.5; bool useDilation = false; String detDbScoreMode = 'fast'; String detBoxType = 'quad'; List<int> recImageShape = [3, 48, 320]; int recBatchNum = 6; bool useSpaceChar = true; double dropScore = 0.5; List<int> clsImageShape = [3, 48, 192]; List<String> labelList = ['0', '180']; int clsBatchNum = 6; double clsThresh = 0.9; bool get useAngleCls => clsModel != null && clsModel!.isNotEmpty; } class _DetPreprocessResult { final Float32List input; final List<int> inputShape; final List<double> shape; _DetPreprocessResult({ required this.input, required this.inputShape, required this.shape, }); } class _RecDecodeResult { final String text; final double score; const _RecDecodeResult(this.text, this.score); } class _ClsDecodeResult { final String label; final double score; const _ClsDecodeResult(this.label, this.score); } class _ImageConverters { static cv.Mat imageToMat(img.Image image) { final rgb = image.hasPalette ? image.convert(numChannels: 3) : image; final …

onnxruntime编译
type: item
date: 2026-04-06T03:04:00+08:00
description: https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.24.1.zipmacos_x64平台的程序export HTTP_PROXY=http://127.0.0.1:6789export HTTPS_PROXY=http://127.0.0.1:6789rm -rf build/macos_x64./build.sh \--osx_arch x86_64 \--config Release \--skip_tests \--skip_submodule_sync \--build_shared_lib \--build_dir build/macos_x64 \--compile_no_warning_as_error \--parallel 8 \--cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVERiosrm -rf build/iosexport HTTP_PROXY=http://127.0.0.1:6789export HTTPS_PROXY=http://127.0.0.1:6789ONNX Runtime 1.24.1 iOS 终极完整命令./build.sh \--ios \--cmake_generator Xcode \--apple_sysroot $(xcrun --sdk iphoneos --show-sdk-path) \--apple_deploy_target 13.0 \--config Release \--build_shared_lib \--skip_tests \--skip_submodule_sync \--build_dir build/ios \--cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER \--parallel 8 \--disable_contrib_ops \--disable_ml_ops \--compile_no_warning_as_errorandroid./build.sh \--android \--android_abi arm64-v8a \--android_api 21 \--android_sdk_path /Users/chenhuan/Library/Android/sdk \--android_ndk_path /Users/chenhuan/Library/Android/sdk/ndk/28.2.13676358 \--config Release \--skip_tests \--skip_submodule_sync \--build_shared_lib \--build_dir build/android \--compile_no_warning_as_error \--parallel 8 \--disable_contrib_ops \--disable_ml_ops \--cmake_extra_defines FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER

验证码特征收集
type: item
date: 2026-01-12T22:28:00+08:00
description: 控制器var captchaRequest request.VerifyCodeRequest if err := ctx.ShouldBindWith(&captchaRequest, binding.JSON); err != nil { helper.Info(err.Error()) ReturnError(ctx, defined.ERROR_500) return } var jsonStr, err = service.CaptchaAESDecrypt(captchaRequest.Ciphertext) if err != nil { helper.Error(err) ReturnSuccess(ctx, nil) return } // 1. 初始化 RawData 变量 var rawData service.CaptchaRawData // 2. 解析 JSON 字符串到结构体 // Unmarshal 要求 JSON 字段类型与结构体字段类型匹配（如数字 vs int64，字符串 vs string） err = json.Unmarshal([]byte(jsonStr), &rawData) if err != nil { ReturnError(ctx, defined.ERROR_500) return } service.CaptchaConsole(rawData, rawData.Env.SessionId, ctx) ReturnSuccess(ctx, rawData) 服务package service import ( "activity/cache" "activity/dao" "activity/etc" "activity/helper" "activity/model" "activity/third/idc" "activity/third/ip2region" "activity/utils" "crypto/aes" "crypto/cipher" "encoding/hex" "errors" "fmt" "github.com/gin-gonic/gin" "github.com/google/uuid" log "github.com/sirupsen/logrus" "gonum.org/v1/gonum/stat" "math" ) // 定义常量：统一阈值，便于维护 const ( // 通用规则阈值（无类型差异的基础规则） DeviceIPBindCountMaxNormal = 2 // 正常IP绑定数上限 DeviceIPBindCountMinRisk = 3 // 风险IP绑定数下限 DeviceIPBindCountMaxRisk = 4 // 风险IP绑定数上限 DeviceIPBindCountMinAbnormal = 5 // 异常IP绑定数下限 IPVerifyFreqMinRisk = 10 // 风险验证频率下限 IPVerifyFreqMaxRisk = 19 // 风险验证频率上限 IPVerifyFreqMinAbnormal = 20 // 异常验证频率下限 // ------------ 按验证类型划分的 load_to_first_delay 阈值（ms）------------ // 滑动（swipe）：操作简单，延迟最短 SwipeDelayMinRisk = 80 // 滑动-风险延迟下限（略短） SwipeDelayMinNormal = 200 // 滑动-正常延迟下限 SwipeDelayMaxNormal = 2000 // 滑动-正常延迟上限 SwipeDelayMaxRisk = 3000 // 滑动-风险延迟上限（略长） SwipeDelayMinAbnormal = 50 // 滑动-异常延迟下限（过短） SwipeDelayMaxAbnormal = 4000 // 滑动-异常延迟上限（过长） // 文字点选（click）：需识别目标，延迟中等 ClickDelayMinRisk = 100 // 点选-风险延迟下限 ClickDelayMinNormal = 300 // 点选-正常延迟下限 ClickDelayMaxNormal = 7000 // 点选-正常延迟上限 ClickDelayMaxRisk = 8000 // 点选-风险延迟上限 ClickDelayMinAbnormal = 70 // 点选-异常延迟下限 ClickDelayMaxAbnormal = 9000 // 点选-异常延迟上限 // 文字输入（input）：需阅读+输入，延迟最长 InputDelayMin…

mac m1下使用HY-MT1.5-1.8B翻译工具
type: item
date: 2026-01-11T20:14:09+08:00
description: from fastapi import FastAPI, HTTPException from pydantic import BaseModel from mlx_lm import load, generate import uvicorn import os # 禁用 Flask 相关环境变量（避免环境污染） os.environ.pop("FLASK_APP", None) os.environ.pop("FLASK_ENV", None) # 初始化纯 FastAPI 应用，确保无 Flask 混入 app = FastAPI(title="MLX 翻译服务", version="1.0") # 预加载模型（只加载一次） print("正在加载翻译模型...") try: model, tokenizer = load("alexgusevski/HY-MT1.5-1.8B-q4-mlx") print("模型加载完成！") except Exception as e: raise RuntimeError(f"模型加载失败：{str(e)}") # 定义请求体模型 class TranslationRequest(BaseModel): source_term: str = "英文" target_term: str = "中文" source_text: str # 翻译接口（异步封装同步函数，避免阻塞事件循环） @app.post("/translate") async def translate(request: TranslationRequest): try: # 构建提示词 prompt = f"""{request.source_term} 翻译成 {request.target_term} 将以下文本翻译为{request.target_term}，注意只需要输出翻译结果，不要额外解释： {request.source_text}""" # 处理聊天模板 if hasattr(tokenizer, "apply_chat_template") and tokenizer.chat_template is not None: messages = [{"role": "user", "content": prompt}] prompt = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) # mlx_lm.generate 是同步函数，用 run_in_threadpool 避免阻塞 import asyncio response = await asyncio.get_event_loop().run_in_executor( None, lambda: generate( model, tokenizer, prompt=prompt, verbose=False, max_tokens=1024, ) ) return { "success": True, "source_text": request.source_text, "target_text": response.strip(), "source_language": request.source_term, "target_language": request.target_term } except Exception as e: raise HTTPException(status_code=500, detail=f"翻译失败：{str(e)}") # 健康检查接口 @app.get("/health") async def health_check(): return {"status": "healthy", "message": "翻译服务正常运行"} # 启动服务 if __name__ == "__main__": uvicorn.run( app="main:app", # 若文件名为其他（如 translate_api.py），改为 "translate_api:app" host="0.0.0.0", port=8000, reload=False, # 关闭重载，避免异步冲突 workers=1 # 单进程运行，适配 mlx 模型 )

paddleocrv5解析视频字幕，并且生成srt字幕文件
type: item
date: 2026-01-11T20:03:00+08:00
description: import os import subprocess import cv2 import numpy as np import re import time import random import sys import io from datetime import datetime from pathlib import Path from PIL import Image from difflib import SequenceMatcher from collections import defaultdict from functools import lru_cache from concurrent.futures import ThreadPoolExecutor # 导入ImgOcr类（请确保imgocr.py在当前目录） from imgocr import ImgOcr # ===================== 全局变量（优化：复用OCR实例） ===================== ocr_det = None # 检测用OCR实例（效率模式） ocr_rec = None # 识别用OCR实例（精准模式） group_text_cache = {} # 缓存分组最优文本结果 # ===================== 新增：有效单个英文字母定义 ===================== # 仅保留有语义的单个英文字母，其余单个字母全部过滤 VALID_SINGLE_ENGLISH_CHARS = {'a', 'i', 'o', 'A', 'I', 'O'} # ===================== 日志重定向类（保留原有功能） ===================== class Tee: """重定向stdout/stderr，同时输出到控制台和日志文件""" def __init__(self, filename, mode='a', encoding='utf-8'): self.file = open(filename, mode, encoding=encoding) self.stdout = sys.stdout self.stderr = sys.stderr sys.stdout = self sys.stderr = self def write(self, message): self.file.write(message) self.stdout.write(message) self.file.flush() self.stdout.flush() def flush(self): self.file.flush() self.stdout.flush() def close(self): sys.stdout = self.stdout sys.stderr = self.stderr self.file.close() # ===================== 配置参数（优化：调整关键参数） ===================== VIDEO_PATH = "/Users/chenhuan/Downloads/中英双字幕版第1季52集/CE003 Best Friend.mp4" FRAME_OUTPUT_DIR = "extracted_frames" CROPPED_20_DIR = "cropped_20_percent" # 兼容旧逻辑，实际内存处理为主 FINAL_SUBTITLE_DIR = "final_subtitle_boxes" SRT_OUTPUT_PATH = "subtitles.srt" LOG_FILE_PATH = f"subtitle_extract_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log" FPS = 1 # 提取帧率（每秒1帧） AREA_THRESHOLD = 4000 MIN_SIZE_THRESHOLD = 100 SIMILARITY_THRESHOLD = 0.8 CONFIDENCE_THRESHOLD = 0.65 INVALID_CHARS = r'[\[\]\(\)\\\/\^\$\#\@\!\~\_\*]' LINE_Y_THRESHOLD = 10 EXTRA_WIDTH = 50 EXTRA_HEIGHT = 20 SAMPLE_COUNT = 20 # 优化：从35改为20（足够判断字幕类型） BILINGUAL_THRESHOLD = 0.6 SRT_END_OFFSET = 0.1 # 英文高频错误补全规则库 ENG…

英文句子语义修复模型
type: item
date: 2026-01-11T18:24:00+08:00
description: import torch from transformers import AutoTokenizer, AutoModelForSeq2SeqLM import re import time class GECTORErrorCorrectorM1: def __init__(self, model_name="512duncanl/gec-flan-t5-large"): """初始化GECToR模型，适配M1 Mac GPU加速""" # 设备优先级：MPS > CUDA > CPU if torch.backends.mps.is_available() and torch.backends.mps.is_built(): self.device = torch.device("mps") elif torch.cuda.is_available(): self.device = torch.device("cuda") else: self.device = torch.device("cpu") print(f"使用设备: {self.device}") # 加载模型和tokenizer（无量化，避免MPS报错） self.tokenizer = AutoTokenizer.from_pretrained(model_name) self.model = AutoModelForSeq2SeqLM.from_pretrained(model_name).to(self.device) self.model.eval() # 推理模式 def preprocess_text(self, text): """预处理：仅过滤句尾无意义数字，保留语义数字""" text = re.sub(r"\s+\d+$", "", text) # 只删句尾冗余数字 text = re.sub(r",(\S)", r", \1", text) # 逗号后加空格 text = re.sub(r"\s+", " ", text).strip() # 合并多余空格 return text def _get_diff(self, original, corrected): """对比文本差异，展示纠错点""" import difflib diff = difflib.ndiff(original.split(), corrected.split()) return [d for d in diff if d.startswith(('+', '-'))] def batch_correct(self, text_list, batch_size=4): """M1 优化的批量推理函数（含单条耗时统计）""" total_start_time = time.time() # 总耗时起始时间 results = [] processed_texts = [self.preprocess_text(t) for t in text_list] # 分批次处理，统计每个批次的耗时 for i in range(0, len(processed_texts), batch_size): batch_start = time.time() # 单个批次起始时间 batch_texts = processed_texts[i:i + batch_size] batch_origs = text_list[i:i + batch_size] # 批量编码 inputs = self.tokenizer( batch_texts, return_tensors="pt", padding=True, truncation=True, max_length=128 ).to(self.device) # 批量推理 with torch.no_grad(): outputs = self.model.generate( **inputs, max_length=128, num_beams=5, early_stopping=True, use_cache=False # MPS内存优化 ) # 批量解码 corrected_batch = self.tokenizer.batch_decode(outputs, skip_special_tokens=True) corrected_batch = [t.strip() for t in corrected_batch] # 计算当前批次总耗时，均分至每条文本 batch_total_time = (time.time() - batch_start) * 1000 # 毫秒 single_text_time =…

纯真数据库转换
type: item
date: 2025-09-12T20:19:00+08:00
description: 1、获取最新的qqwry.dat 可以从https://github.com/metowolf/qqwry.dat/releases 这里下载最新版 2、使用c++把 qqwry.dat转成文本 原文章有问题不能把内容全部提取完，调整为 #include <stdio.h> #define DAT_FILE "路径/qqwry.dat" #define OUT_FILE "./out.txt" FILE *out = NULL; void showIp(unsigned int ip) { char buffer[20] = {0}; sprintf(buffer, "%u.%u.%u.%u ", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); fputs(buffer, out); } void getString(FILE *fp) { char temp[128] = {0}; int index = 0; while ((temp[index] = fgetc(fp)) != 0) { index++; } fputs(temp, out); } // 重构 getInfo() 函数（非递归版本） void getInfo(FILE *fp) { unsigned char mod; long offset; long preOffset; int count = 0; // 记录已读取的字符串数量 while (count < 2) { mod = fgetc(fp); if (mod == 0x01 || mod == 0x02) { offset = 0; fread(&offset, 3, 1, fp); preOffset = ftell(fp); fseek(fp, offset, SEEK_SET); } else { fseek(fp, -1, SEEK_CUR); // 回退非重定向字节 getString(fp); // 读取字符串 count++; if (count == 1 && mod != 0x01 && mod != 0x02) { // 若第一个字符串后无重定向，继续读第二个 getString(fp); count++; } } if (mod == 0x02 && count == 1) { // 模式2且只读了一个字符串时，返回原位置继续 fseek(fp, preOffset, SEEK_SET); } } } void parseRecord(FILE *fp, long int recordOffset) { unsigned int endIp = 0; fseek(fp, recordOffset, SEEK_SET); //seek到数据区 fread(&endIp, 4, 1, fp); //读取结束IP showIp(endIp); //显示结束IP getInfo(fp); //获取信息 fputs("\n", out); } void parseDat(FILE *fp) { long int startIndexOffset = 0; long int endIndexOffset = 0; long int indexOffset = 0; long int recordOffset = 0; unsigned int startIp = 0; fseek(fp, 0L, SEEK_SET); //seek文件头 fread(&startIndexOffset, 4, 1, fp); //读索引开始偏移量 fread(&endIndexOffset, 4, 1, fp); //读索引结束偏移量 indexOffset = startIndexOffset; // 修改索引区遍历逻辑 while (indexOffset < endIndexOffset) // 使用 < 而不是 <= { fseek(fp, indexOffset, SEEK_SET); fread(&startIp, 4, 1, fp); recordOffset = 0; // 关键：清空高位字节 fread(&recordOffset, 3, 1, fp); indexOffset = ftell(fp); showIp(startIp); parseRecord(fp, recordOffset); } } int main(void) { FILE *fp = NULL; out = fopen(OUT_FILE, "wb"); fp = fopen(DAT_FILE, "rb"); if (…

mongodb导入
type: item
date: 2025-06-26T17:01:04+08:00
description: 导入脚本../mongodb/bin/mongorestore --uri "mongodb://admin:hudongtang2020@localhost:27011/" --drop --dir tmp/backup/data

下载到内存中播放图片
type: item
date: 2025-06-24T21:16:00+08:00
description: #include <WiFiManager.h> #include <WiFi.h> #include <TFT_eSPI.h> #include <PubSubClient.h> #include "esp_system.h" #include <Arduino.h> #include <TJpg_Decoder.h> #include <HTTPClient.h> #include <src/CommonUtils.h> TFT_eSPI tft = TFT_eSPI(); // 创建TFT对象 WiFiManager wifiManager; unsigned long buttonPressTime = 0; bool isConnected = false; // 正在连接标记 bool resetTriggered = false; #define BOOT_BUTTON_PIN 0 // 显示状态常量 #define STATUS_WIFI_CONNECTING 0 #define STATUS_WIFI_CONNECTED 1 #define STATUS_WIFI_DISCONNECTED 2 #define STATUS_MQTT_CONNECTING 3 #define STATUS_MQTT_CONNECTED 4 #define STATUS_DOWNLOADING 5 #define STATUS_DISPLAYING_IMAGE 6 #define STATUS_PLAYING_MJPEG 7 int currentStatus = STATUS_WIFI_CONNECTING; // MQTT 配置 const char* mqttServer = "xxxx"; const int mqttPort = 1883; const char* mqttUser = "xxxx"; // 无需认证留空 const char* mqttPassword = "xxxx"; const char* clientId = "arduino_client_1232"; // 客户端ID需唯一 WiFiClient espClient; PubSubClient client(espClient); bool isVideo = false; // 内存缓冲区 uint8_t* jpegBuffer = NULL; // JPEG原始数据缓冲区 (PSRAM) size_t jpegSize = 0; // JPEG图片大小 uint8_t* mjpegBuffer = NULL; // MJPEG文件缓冲区 (PSRAM) size_t mjpegSize = 0; // MJPEG文件大小 size_t mjpegPosition = 0; // 当前MJPEG文件位置 // 帧缓冲区 uint8_t* frameBuffer = NULL; // 单帧数据缓冲区 size_t frameSize = 0; // 当前帧大小 // 流缓冲区 uint8_t* streamBuffer = NULL; // 流数据缓冲区 size_t streamBufferSize = 0; // 流缓冲区大小 size_t streamBufferPos = 0; // 流缓冲区位置 // 定义常量以提高可读性和可维护性 #define BUFFER_SIZE 4096 // 增大缓冲区大小 #define DOWNLOAD_TIMEOUT 15000 // 下载超时时间(毫秒) #define DATA_TIMEOUT 5000 // 数据超时时间(毫秒) #define PROGRESS_INTERVAL 500 // 进度显示间隔(毫秒) // 帧处理相关 #define MAX_FRAME_SIZE (1024 * 1024) // 最大帧大小1MB #define STREAM_BUFFER_SIZE (16 * 1024) // 流缓冲区大小16KB #define FRAME_DELAY 40 // 帧延迟(毫秒)，影响播放速度 // 默认MJPEG URL const char* defaultMjpegUrl = "https://chenhuan-esp.oss-cn-beijing.aliyuncs.com/output.avi"; // 当前播放状态 bool isPlayingMjpeg = false; unsigned long lastFrameTime = 0; unsigned long frameCount = 0; unsigned long mjpegStartTime = 0…

mtqq docker-compose部署
type: item
date: 2025-06-07T10:31:00+08:00
description: git clone https://github.com/emqx/emqx-usage-example cd emqx-usage-example/mqtt-lb-nginx/ docker compose up -d 部署验证部署成功我们可以访问服务器的 18083 端口，访问 MQTT 集群的 dashboard, 默认账号是 admin，密码是 public，登录以后会提示你修改密码连接验证我们可以使用 MQTT cli 进行连接验证，下面是验证方法安装curl -LO https://www.emqx.com/en/downloads/MQTTX/v1.9.10/mqttx-cli-linux-x64 install ./mqttx-cli-linux-x64 /usr/local/bin/mqttx 发起 10 个连接mqtt bench conn -c 10 -h loclhost -p 1883