| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /**
- * 步骤:
- * 1. 创建startCutCheck()函数
- * 2. 创建变量mergedImgPath 用来接收外部传入的需要切割的图片路径(输入)
- * 3. 创建变量cutPanelImgDir 用来接收外部传入的保存切割图片的文件夹路径(输出)
- * 4. 调用cutPanels()函数切割红线(不是绿线)格子
- * 5. 将切割好的图片保存到cutPanelImgDir目录
- * 6. 返回切割好的图片路径数组
- */
- import fs from 'fs';
- import path from 'path';
- import { fileURLToPath } from 'url';
- import { execSync } from 'child_process';
- import { getPythonPath } from './python-path.js';
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = path.dirname(__filename);
- function getProjectRoot() {
- return path.join(__dirname, '..');
- }
- /**
- * 步骤1: 创建startCutCheck()函数
- * @param {string} mergedImgPath - 步骤2: 需要用来切割的图片路径(输入)
- * @param {string} cutPanelImgDir - 步骤3: 保存切割图片的文件夹路径(输出)
- * @returns {Promise<Array>} 步骤6: 返回切割好的图片路径数组
- */
- async function startCutCheck(mergedImgPath, cutPanelImgDir) {
- try {
- console.log('🚀 开始切割格子流程...');
-
- // 步骤2: 创建变量mergedImgPath 用来接收外部传入的需要切割的图片路径(输入)
- console.log('\n📷 步骤2: 验证需要切割的图片路径参数');
- if (!mergedImgPath) {
- throw new Error('步骤2失败: mergedImgPath 参数不能为空');
- }
- if (!fs.existsSync(mergedImgPath)) {
- throw new Error(`步骤2失败: 需要切割的图片文件不存在 - ${mergedImgPath}`);
- }
- console.log(`✅ 需要切割的图片路径: ${mergedImgPath}`);
-
- // 步骤3: 创建变量cutPanelImgDir 用来接收外部传入的保存切割图片的文件夹路径(输出)
- console.log('\n📂 步骤3: 验证切割图片保存目录参数');
- if (!cutPanelImgDir) {
- throw new Error('步骤3失败: cutPanelImgDir 参数不能为空');
- }
- // 确保输出目录存在
- if (!fs.existsSync(cutPanelImgDir)) {
- fs.mkdirSync(cutPanelImgDir, { recursive: true });
- }
- console.log(`✅ 切割图片保存目录: ${cutPanelImgDir}`);
-
- // 步骤4: 调用cutPanels()函数切割红线(不是绿线)格子
- console.log('\n✂️ 步骤4: 开始切割红线格子...');
- const cutFiles = await cutPanels(mergedImgPath, cutPanelImgDir);
- console.log(`✅ 格子切割完成: 生成 ${cutFiles.length} 个图片文件`);
-
- // 步骤5: 将切割好的图片保存到cutPanelImgDir目录
- console.log('\n💾 步骤5: 验证切割结果保存完成...');
- for (let i = 0; i < cutFiles.length; i++) {
- const file = cutFiles[i];
- if (!fs.existsSync(file)) {
- throw new Error(`步骤5失败: 切割文件未生成 - ${file}`);
- }
- const stats = fs.statSync(file);
- console.log(`✅ 格子 ${i + 1}: ${path.basename(file)} (${Math.round(stats.size / 1024)}KB)`);
- }
-
- // 步骤6: 返回切割好的图片路径数组
- console.log('\n📋 步骤6: 准备返回切割好的图片路径数组...');
- console.log(`📄 图片路径列表:`);
- cutFiles.forEach((file, index) => {
- console.log(` ${index + 1}. ${path.basename(file)} -> ${file}`);
- });
- console.log(`✅ 步骤6完成: 图片路径数组已准备就绪 (${cutFiles.length} 个路径)`);
-
- console.log('\n🎉 所有步骤完成!');
- console.log(`📊 共切割 ${cutFiles.length} 个格子图片`);
- console.log(`📁 保存目录: ${cutPanelImgDir}`);
-
- return cutFiles; // 步骤6: 直接返回切割好的图片路径数组
-
- } catch (error) {
- console.error(`\n❌ 切割格子失败: ${error.message}`);
- throw error;
- }
- }
- /**
- * 步骤4: 调用cutPanels()函数切割红线格子
- * @param {string} mergedImgPath - 合并图片路径
- * @param {string} cutPanelImgDir - 输出目录
- * @returns {Array<string>} 切割后的文件路径数组
- */
- async function cutPanels(mergedImgPath, cutPanelImgDir) {
- const projectRoot = getProjectRoot();
- const pythonEnv = getPythonPath();
-
- // 查找格子信息JSON文件(从合并图片同目录查找)
- const mergedImgDir = path.dirname(mergedImgPath);
- const imageName = path.basename(mergedImgPath, path.extname(mergedImgPath)).replace('_merged', '');
- const panelsJsonPath = path.join(mergedImgDir, `${imageName}_text_check_region.json`);
-
- console.log(`📋 查找格子信息: ${path.basename(panelsJsonPath)}`);
-
- if (!fs.existsSync(panelsJsonPath)) {
- throw new Error(`格子信息文件不存在: ${panelsJsonPath}`);
- }
-
- // 读取格子信息
- const panelsData = JSON.parse(fs.readFileSync(panelsJsonPath, 'utf-8'));
- const panels = panelsData.panels || [];
-
- if (panels.length === 0) {
- throw new Error('未找到任何格子信息');
- }
-
- console.log(`✅ 读取到 ${panels.length} 个格子信息`);
-
- // 创建切割Python脚本
- const cutScript = path.join(projectRoot, 'python', 'generate-anim', 'cut_panels.py');
- if (!fs.existsSync(cutScript)) {
- console.log('📝 创建切割Python脚本...');
- createCutScript(cutScript);
- }
-
- // 调用Python脚本切割图片
- const command = `"${pythonEnv}" "${cutScript}" "${mergedImgPath}" "${panelsJsonPath}" "${cutPanelImgDir}"`;
-
- console.log('✂️ 正在切割格子...');
- try {
- execSync(command, {
- encoding: 'utf-8',
- stdio: 'pipe',
- cwd: projectRoot,
- env: {
- ...process.env,
- PYTHONIOENCODING: 'utf-8',
- PYTHONUTF8: '1'
- },
- shell: true
- });
- } catch (error) {
- throw new Error(`Python切割脚本执行失败: ${error.message}`);
- }
-
- // 获取切割结果文件列表
- const cutFiles = fs.readdirSync(cutPanelImgDir)
- .filter(f => f.match(/^check\d+\.png$/)) // 匹配 check1.png, check2.png 等
- .sort((a, b) => {
- const numA = parseInt(a.match(/check(\d+)/)?.[1] || '0');
- const numB = parseInt(b.match(/check(\d+)/)?.[1] || '0');
- return numA - numB;
- })
- .map(f => path.join(cutPanelImgDir, f));
-
- if (cutFiles.length === 0) {
- throw new Error('未生成任何切割文件');
- }
-
- return cutFiles;
- }
- /**
- * 创建切割格子的Python脚本
- */
- function createCutScript(scriptPath) {
- const scriptContent = `#!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- 简单的格子切割脚本
- """
- import cv2
- import json
- import sys
- from pathlib import Path
- import numpy as np
- def cut_panels(image_path, panels_json_path, output_dir):
- """切割格子图片"""
- # 读取图片
- img_data = np.fromfile(str(image_path), dtype=np.uint8)
- img = cv2.imdecode(img_data, cv2.IMREAD_COLOR)
- if img is None:
- raise ValueError(f"无法读取图片: {image_path}")
-
- print(f"[INFO] 图片尺寸: {img.shape[1]}x{img.shape[0]}")
-
- # 读取格子信息
- with open(panels_json_path, 'r', encoding='utf-8') as f:
- data = json.load(f)
-
- panels = data.get('panels', [])
- if not panels:
- raise ValueError("未找到格子信息")
-
- print(f"[INFO] 找到 {len(panels)} 个格子")
-
- # 确保输出目录存在
- Path(output_dir).mkdir(parents=True, exist_ok=True)
-
- # 按从右到左、从上到下排序
- sorted_panels = sorted(panels, key=lambda p: (p.get('y', 0), -p.get('x', 0)))
-
- # 切割每个格子
- for i, panel in enumerate(sorted_panels, 1):
- x = int(panel.get('x', 0))
- y = int(panel.get('y', 0))
- w = int(panel.get('width', 0))
- h = int(panel.get('height', 0))
-
- # 切割区域
- panel_img = img[y:y+h, x:x+w]
-
- # 保存文件
- output_file = Path(output_dir) / f"panel_{i}.png"
- success, encoded = cv2.imencode('.png', panel_img)
- if success:
- encoded.tofile(str(output_file))
- print(f"[{i}/{len(sorted_panels)}] 保存: {output_file.name} ({w}x{h})")
-
- print(f"✅ 切割完成: {len(sorted_panels)} 个格子")
- if __name__ == '__main__':
- if len(sys.argv) != 4:
- print("用法: python cut_panels.py <图片路径> <JSON路径> <输出目录>")
- sys.exit(1)
-
- try:
- cut_panels(sys.argv[1], sys.argv[2], sys.argv[3])
- except Exception as e:
- print(f"[ERROR] {e}")
- sys.exit(1)
- `;
- // 确保目录存在
- const scriptDir = path.dirname(scriptPath);
- if (!fs.existsSync(scriptDir)) {
- fs.mkdirSync(scriptDir, { recursive: true });
- }
-
- fs.writeFileSync(scriptPath, scriptContent, 'utf-8');
- }
- /**
- * 导出函数供外部调用
- */
- export { startCutCheck };
|