UNPKG

librawspeed

Version:

Node.js Native Addon for LibRaw - Process RAW image files with JavaScript

318 lines (270 loc) 11.2 kB
const LibRaw = require("../lib/index"); const fs = require("fs"); const path = require("path"); /** * API 比较:基于文件 vs 基于缓冲区 * * 此示例演示了传统基于文件的 API 和新的基于缓冲区的 API 之间的差异, * 突出了每种方法的优势。 */ async function apiComparisonExample(inputFile, outputDir) { console.log("🔄 API 比较:文件 vs 缓冲区操作"); console.log("============================================"); console.log(`📁 输入: ${inputFile}`); console.log(`📂 输出目录: ${outputDir}\n`); // 确保输出目录存在 if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const processor = new LibRaw(); const baseName = path.basename(inputFile, path.extname(inputFile)); try { // 加载 RAW 文件一次 console.log("🔄 加载 RAW 文件..."); await processor.loadFile(inputFile); console.log("✅ RAW 文件已加载\n"); // ============== 旧方式:基于文件的 API ============== console.log("📁 旧方式:基于文件的操作"); console.log("=================================="); const fileStartTime = Date.now(); // 传统方法:处理并写入文件 console.log("1️⃣ 处理图像..."); await processor.processImage(); console.log("2️⃣ 写入 TIFF 文件..."); const tiffPath = path.join(outputDir, `${baseName}_traditional.tiff`); await processor.writeTIFF(tiffPath); console.log("3️⃣ 写入 PPM 文件..."); const ppmPath = path.join(outputDir, `${baseName}_traditional.ppm`); await processor.writePPM(ppmPath); console.log("4️⃣ 写入缩略图..."); const thumbPath = path.join(outputDir, `${baseName}_traditional_thumb.jpg`); await processor.writeThumbnail(thumbPath); // 传统 JPEG 转换 console.log("5️⃣ 转换为 JPEG..."); const jpegPath = path.join(outputDir, `${baseName}_traditional.jpg`); await processor.convertToJPEG(jpegPath, { quality: 85, width: 1920, }); const fileEndTime = Date.now(); const fileProcessingTime = fileEndTime - fileStartTime; console.log(`✅ 基于文件的处理完成,用时 ${fileProcessingTime}ms`); // 检查文件大小 const fileSizes = { tiff: fs.statSync(tiffPath).size, ppm: fs.statSync(ppmPath).size, thumb: fs.statSync(thumbPath).size, jpeg: fs.statSync(jpegPath).size, }; console.log("📊 文件大小:"); Object.entries(fileSizes).forEach(([format, size]) => { console.log( ` ${format.toUpperCase()}: ${(size / 1024 / 1024).toFixed(2)} MB` ); }); // ============== 新方式:基于缓冲区的 API ============== console.log("\n🚀 新方式:基于缓冲区的操作"); console.log("==================================="); const bufferStartTime = Date.now(); // 现代方法:在内存中创建缓冲区 console.log("1️⃣ 创建 TIFF 缓冲区..."); const tiffBuffer = await processor.createTIFFBuffer({ compression: "lzw", }); console.log("2️⃣ 创建 PPM 缓冲区..."); const ppmBuffer = await processor.createPPMBuffer(); console.log("3️⃣ 创建缩略图缓冲区..."); const thumbBuffer = await processor.createThumbnailJPEGBuffer({ quality: 85, }); console.log("4️⃣ 创建 JPEG 缓冲区..."); const jpegBuffer = await processor.createJPEGBuffer({ quality: 85, width: 1920, }); // 额外:并行创建多种格式 console.log("5️⃣ 并行创建其他格式..."); const [webpBuffer, avifBuffer, pngBuffer] = await Promise.all([ processor.createWebPBuffer({ quality: 80, width: 1920 }), processor.createAVIFBuffer({ quality: 50, width: 1920 }), processor.createPNGBuffer({ width: 800, compressionLevel: 6 }), ]); const bufferEndTime = Date.now(); const bufferProcessingTime = bufferEndTime - bufferStartTime; console.log( `✅ 基于缓冲区的处理完成,用时 ${bufferProcessingTime}ms` ); // 将缓冲区保存到文件进行比较(可选步骤) console.log("6️⃣ 将缓冲区保存到文件进行比较..."); fs.writeFileSync( path.join(outputDir, `${baseName}_buffer.tiff`), tiffBuffer.buffer ); fs.writeFileSync( path.join(outputDir, `${baseName}_buffer.ppm`), ppmBuffer.buffer ); fs.writeFileSync( path.join(outputDir, `${baseName}_buffer_thumb.jpg`), thumbBuffer.buffer ); fs.writeFileSync( path.join(outputDir, `${baseName}_buffer.jpg`), jpegBuffer.buffer ); fs.writeFileSync( path.join(outputDir, `${baseName}_buffer.webp`), webpBuffer.buffer ); fs.writeFileSync( path.join(outputDir, `${baseName}_buffer.avif`), avifBuffer.buffer ); fs.writeFileSync( path.join(outputDir, `${baseName}_buffer.png`), pngBuffer.buffer ); console.log("📊 缓冲区大小:"); const buffers = { TIFF: tiffBuffer.buffer, PPM: ppmBuffer.buffer, Thumbnail: thumbBuffer.buffer, JPEG: jpegBuffer.buffer, WebP: webpBuffer.buffer, AVIF: avifBuffer.buffer, PNG: pngBuffer.buffer, }; Object.entries(buffers).forEach(([format, buffer]) => { console.log( ` ${format}: ${(buffer.length / 1024 / 1024).toFixed(2)} MB` ); }); // ============== 性能比较 ============== console.log("\n⚡ 性能比较"); console.log("========================"); console.log(`📁 基于文件的方法: ${fileProcessingTime}ms`); console.log(`🚀 基于缓冲区的方法: ${bufferProcessingTime}ms`); const speedImprovement = ((fileProcessingTime - bufferProcessingTime) / fileProcessingTime) * 100; if (speedImprovement > 0) { console.log( `🏆 缓冲区方法快 ${speedImprovement.toFixed(1)}%!` ); } else { console.log( `📊 性能差异: ${Math.abs(speedImprovement).toFixed(1)}%` ); } // ============== 使用案例建议 ============== console.log("\n💡 何时使用每种方法"); console.log("============================="); console.log("\n📁 使用基于文件的 API 当:"); console.log(" • 您需要将最终图像保存到磁盘"); console.log(" • 处理大图像(内存限制)"); console.log(" • 构建传统桌面应用程序"); console.log(" • 创建永久存档"); console.log(" • 与基于文件的工作流程集成"); console.log("\n🚀 使用基于缓冲区的 API 当:"); console.log(" • 构建 Web 服务和 API"); console.log(" • 上传到云存储"); console.log(" • 创建图像处理管道"); console.log(" • 实时图像处理"); console.log(" • 无服务器/ Lambda 函数"); console.log(" • 流式图像数据"); console.log(" • 从单一源创建多种格式"); // ============== 代码示例 ============== console.log("\n📝 代码示例"); console.log("================"); console.log("\n📁 基于文件的方法:"); console.log("```javascript"); console.log("// 传统方式"); console.log('await processor.loadFile("input.raw");'); console.log("await processor.processImage();"); console.log( 'await processor.convertToJPEG("output.jpg", { quality: 85 });' ); console.log('// 文件现在在磁盘上的 "output.jpg"'); console.log("```"); console.log("\n🚀 基于缓冲区的方法:"); console.log("```javascript"); console.log("// 现代方式"); console.log('await processor.loadFile("input.raw");'); console.log( "const result = await processor.createJPEGBuffer({ quality: 85 });" ); console.log("// result.buffer 包含 JPEG 数据"); console.log("// 直接使用,无需文件 I/O"); console.log("```"); console.log("\n🌐 Web 服务示例:"); console.log("```javascript"); console.log('app.post("/convert", async (req, res) => {'); console.log(" const processor = new LibRaw();"); console.log(" await processor.loadBuffer(req.body);"); console.log(" const result = await processor.createJPEGBuffer({"); console.log(" quality: 85,"); console.log(" width: 1920"); console.log(" });"); console.log(' res.set("Content-Type", "image/jpeg");'); console.log(" res.send(result.buffer);"); console.log("});"); console.log("```"); console.log("\n☁️ 云存储示例:"); console.log("```javascript"); console.log( "const result = await processor.createJPEGBuffer({ quality: 90 });" ); console.log( 'await cloudBucket.file("processed.jpg").save(result.buffer, {' ); console.log(" metadata: {"); console.log(' contentType: "image/jpeg",'); console.log(' cacheControl: "public, max-age=31536000"'); console.log(" }"); console.log("});"); console.log("```"); // ============== FORMAT COMPARISON ============== console.log("\n🎨 Format Efficiency Comparison"); console.log("==============================="); const formats = [ { name: "AVIF", buffer: avifBuffer.buffer, extension: ".avif" }, { name: "WebP", buffer: webpBuffer.buffer, extension: ".webp" }, { name: "JPEG", buffer: jpegBuffer.buffer, extension: ".jpg" }, { name: "PNG", buffer: pngBuffer.buffer, extension: ".png" }, { name: "TIFF", buffer: tiffBuffer.buffer, extension: ".tiff" }, ]; // Sort by file size (smallest first) formats.sort((a, b) => a.buffer.length - b.buffer.length); console.log("📊 Formats ranked by file size (smallest to largest):"); formats.forEach((format, index) => { const sizeMB = (format.buffer.length / 1024 / 1024).toFixed(2); const emoji = index === 0 ? "🏆" : index === 1 ? "🥈" : index === 2 ? "🥉" : "📊"; console.log(` ${emoji} ${format.name}: ${sizeMB} MB`); }); // Cleanup await processor.close(); console.log("\n✅ API comparison complete!"); console.log("\n🎯 Key Takeaway: Choose the right API for your use case"); console.log(" • File-based: Traditional workflows, disk storage"); console.log(" • Buffer-based: Modern web services, cloud applications"); } catch (error) { console.error("\n❌ Error:", error.message); console.error("\nMake sure you have:"); console.error("1. Built the addon: npm run build"); console.error("2. Installed Sharp: npm install sharp"); console.error("3. Provided a valid RAW file"); } } // 使用说明 if (process.argv.length < 3) { console.log( "用法: node api-comparison-example.js <raw文件路径> [输出目录]" ); console.log( "示例: node api-comparison-example.js ../sample-images/IMG_1234.CR2 ./comparison-output" ); process.exit(1); } const inputFile = process.argv[2]; const outputDir = process.argv[3] || "./comparison-output"; apiComparisonExample(inputFile, outputDir);