拍出来的照片没有感觉怎么办,那就用 NodeJs 来帮助你裁剪成电影的感觉 😙😙😙

在图像处理和视频剪辑的领域,比例的选择对内容的视觉效果至关重要。2.35:1 的宽高比例作为一种经典的电影画幅,不仅能够展现出更加宽广的视野,还能为观众带来沉浸式的观影体验。这种比例常被用于电影制作以及内容创作者在剪辑大场景时的最佳选择。

图片的 2.35:1 比例是什么

2.35:1 是一种宽高比例,表示图片的宽度与高度之间的关系。具体来说它的宽度是高度的 2.35 倍,用公式表示:

text 复制代码
宽度 / 高度 = 2.35 / 1

如果图片的高度是 1 个单位,那么宽度是 2.35 个单位。例如高度为 100 像素,则宽度为 100 * 2.35 = 235 像素。

它的应用场景一般有电影画幅 2.35:1 是一种经典的电影宽屏比例,又被称为宽银幕比例(Cinemascope 或 Panavision)。很多电影(尤其是史诗类、动作大片等)采用这种比例来增强视觉体验,让观众能看到更多的横向内容。常用于大场景的拍摄,比如风景、战争场面等。

2.35:1 的比例让图片看起来非常"宽",适合强调横向内容。以下是它与其他比例的视觉对比:

diff 复制代码
+--------------------------------------+
|                                      |
|               图片内容               |
|                                      |
+--------------------------------------+

宽大于高,适合横向扩展的内容,如风景、城市景观。

1:1 的正方形比例:

diff 复制代码
+------------------+
|                  |
|     图片内容     |
|                  |
+------------------+

宽度和高度相等,更加集中、平衡。

16:9 的宽屏比例:

diff 复制代码
+--------------------------+
|                          |
|       图片内容           |
|                          |
+--------------------------+

比 2.35:1 稍窄,更适合视频和电视内容。

如何在 NodeJs 中实现将图片和视频转换成 2.35:1 的比例

接下来我们将来使用 sharp 库对图片进行裁剪,使用 ffmpeg 对视频进行裁剪。

图片的 2.35:1 裁剪

在图片中,我们需要借助 sharp 库,首先我们需要安装该库:

bash 复制代码
pnpm add sharp

并编写如下代码:

js 复制代码
const sharp = require("sharp");

async function resizeToAspectRatio(inputPath, outputPath) {
  const aspectRatio = 2.35 / 1;

  try {
    // 获取图片的原始尺寸
    const metadata = await sharp(inputPath).metadata();

    const originalWidth = metadata.width;
    const originalHeight = metadata.height;

    // 计算新尺寸,基于最接近比例的边
    let newWidth, newHeight;
    if (originalWidth / originalHeight > aspectRatio) {
      newHeight = originalHeight;
      newWidth = Math.round(originalHeight * aspectRatio);
    } else {
      newWidth = originalWidth;
      newHeight = Math.round(originalWidth / aspectRatio);
    }

    // 通过裁剪和调整大小处理图片
    await sharp(inputPath)
      .resize(newWidth, newHeight, { fit: "cover" }) // 按照新宽高裁剪
      .toFile(outputPath);

    console.log(
      `图片已调整为 ${newWidth}x${newHeight},并保存到 ${outputPath}`
    );
  } catch (error) {
    console.error("处理图片时出错:", error);
  }
}

resizeToAspectRatio("./62df2cba3774e4d8757be60ed47292.jpg", "output.jpg");

在上面的代码中通过 sharp(inputPath).metadata(),提取图片的元数据(metadata),包括图片的宽度(width)和高度(height)。

接着计算新高度和宽度:

js 复制代码
let newWidth, newHeight;
if (originalWidth / originalHeight > aspectRatio) {
  newHeight = originalHeight;
  newWidth = Math.round(originalHeight * aspectRatio);
} else {
  newWidth = originalWidth;
  newHeight = Math.round(originalWidth / aspectRatio);
}

这部分的目的是根据原始宽高比和目标宽高比,计算裁剪后的新宽度或新高度:

  • 原始宽高比 = originalWidth / originalHeight。

  • 目标宽高比 = aspectRatio(2.35)。

如果原始宽高比大于目标宽高比:说明图片太宽。保持高度不变 newHeight = originalHeight。调整宽度,使其符合目标宽高比:newWidth = originalHeight * aspectRatio

如果原始宽高比小于或等于目标宽高比:说明图片太高。保持宽度不变(newWidth = originalWidth)。调整高度,使其符合目标宽高比:newHeight = originalWidth / aspectRatio

最后通过 sharp 进行裁剪和调整大小并使用裁剪的方式来匹配目标尺寸,保证图片内容不会变形。最后将处理的图片保存到输出路径。

最终效果如下图所示:

因为原图中有一点黑边存在,所以裁出来的照片也带有黑边

给图片添加字幕

要对图片添加字幕,我们还是要借助 sharp 库,如下代码:

js 复制代码
const sharp = require("sharp");
const fs = require("fs");

async function addSubtitleToImage(
  inputPath,
  outputPath,
  subtitleZh,
  subtitleEn
) {
  try {
    // 获取原始图片的尺寸
    const metadata = await sharp(inputPath).metadata();
    const imageWidth = metadata.width;

    // 创建字幕区域 (透明背景)
    const subtitleHeight = 100; // 字幕所占的高度
    const svgText = `
    <svg width="${imageWidth}" height="${subtitleHeight}">
      <style>
        .title {
          fill: white;
          stroke: black;
          stroke-width: 1px;
          font-size: 28px;
          font-family: "Hiragino Sans", "Arial", sans-serif;
          font-weight: 500;
        }
        .subtitle {
          fill: white;
          stroke: black;
          stroke-width: 1px;
          font-size: 22px;
          font-family: "Hiragino Sans", "Arial", sans-serif;
          font-weight: 400;
        }
      </style>
      <text x="50%" y="40%" text-anchor="middle" alignment-baseline="middle" class="title">
        ${subtitleZh}
      </text>
      <text x="50%" y="80%" text-anchor="middle" alignment-baseline="middle" class="subtitle">
        ${subtitleEn}
      </text>
    </svg>
    `;

    // 将 SVG 转换为 Buffer
    const svgBuffer = Buffer.from(svgText);

    // 合成原始图片和字幕区域
    await sharp(inputPath)
      .composite([
        { input: svgBuffer, top: metadata.height - subtitleHeight, left: 0 },
      ])
      .toFile(outputPath);

    console.log(`图片已添加字幕并保存到 ${outputPath}`);
  } catch (error) {
    console.error("添加字幕时出错:", error);
  }
}

const inputImage = "./output.jpg"; // 输入图片路径
const outputImage = "./output_with_subtitle.jpg"; // 输出图片路径
const subtitleZh = "街边太多人与车,繁华闹市人醉夜"; // 中文字幕
const subtitleEn =
  "Too many people and cars by the street, a bustling city intoxicates the night."; // 英文字幕

addSubtitleToImage(inputImage, outputImage, subtitleZh, subtitleEn);

这段代码通过 sharp 库为图片底部添加中英文字幕。首先,它读取输入图片的元数据,获取图片的宽度和高度,用于生成与图片宽度一致的字幕区域(高度固定为 100 像素)。接着,动态生成一个包含中英文字幕的 SVG,其中设置了字体样式(如大小、颜色、边框等)和字幕内容,并将这段 SVG 转换为二进制数据。然后,代码通过 sharp 的 composite 方法,将生成的字幕区域合成到图片底部,确保字幕居中对齐,且不改变图片的主体内容。最终处理后的图片会保存到指定路径,实现了自动化、高质量的图片字幕添加功能。

这段代码最终的输出结果如下图所示:

视频的 2.35:1 裁剪

在 Node.js 中,可以使用 FFmpeg 结合相关库(如 fluent-ffmpeg)将视频裁剪成 2.35:1 的宽高比例。

使用前我们首先要安装 fluent-ffmpeg,它是一个 NodeJs 库,用于方便地在 NodeJs 项目中使用 FFmpeg:

bash 复制代码
pnpm add fluent-ffmpeg

安装的同时需要确保你的电脑上已经存在 FFmpeg 的环境变量:

保证这些都没有问题之后我们编写如下代码:

js 复制代码
const ffmpeg = require("fluent-ffmpeg");

/**
 * 将视频裁剪为 2.35:1 的比例
 * @param {string} inputPath - 输入视频路径
 * @param {string} outputPath - 输出视频路径
 */
function cropVideoToAspectRatio(inputPath, outputPath) {
  const aspectRatio = 2.35 / 1; // 目标宽高比例

  // 获取视频的元数据(宽高)
  ffmpeg.ffprobe(inputPath, (err, metadata) => {
    if (err) {
      console.error("获取视频元数据失败:", err);
      return;
    }

    const originalWidth = metadata.streams[0].width;
    const originalHeight = metadata.streams[0].height;

    // 计算裁剪尺寸
    let newWidth, newHeight, xOffset, yOffset;
    if (originalWidth / originalHeight > aspectRatio) {
      // 如果宽高比超出目标比例(视频太宽),裁剪左右
      newHeight = originalHeight;
      newWidth = Math.floor(originalHeight * aspectRatio);
      xOffset = Math.floor((originalWidth - newWidth) / 2); // 水平居中裁剪
      yOffset = 0;
    } else {
      // 如果宽高比低于目标比例(视频太高),裁剪上下
      newWidth = originalWidth;
      newHeight = Math.floor(originalWidth / aspectRatio);
      xOffset = 0;
      yOffset = Math.floor((originalHeight - newHeight) / 2); // 垂直居中裁剪
    }

    console.log(
      `裁剪区域:宽 ${newWidth}px,高 ${newHeight}px,偏移 (${xOffset}, ${yOffset})`
    );

    // 调用 ffmpeg 进行裁剪
    ffmpeg(inputPath)
      .videoFilters(`crop=${newWidth}:${newHeight}:${xOffset}:${yOffset}`) // 设置裁剪参数
      .on("end", () => {
        console.log("视频裁剪完成,保存为:", outputPath);
      })
      .on("error", (err) => {
        console.error("视频裁剪失败:", err);
      })
      .save(outputPath); // 保存裁剪后的视频
  });
}

cropVideoToAspectRatio("./DSC_1483.MOV", "output.mp4");

这代码还是跟前面的差不多,首先使用 ffmpeg.ffprobe(inputPath, (err, metadata) => { ... }); 来提取视频元数据,例如宽高。

剩下的都是和之前差不多的思路了。

最终输出结果如下图所示:

总结

在上面的内容中我们借助了 sharp 和 FFmpeg 对图片和视频进行了 2.35:1 的比例进行裁剪,效果明显会比原图好看多了,后期可以加上字幕的效果就更有看电影的感觉了。

最后再来提一下这两个开源项目,它们都是我们目前正在维护的开源项目:

如果你想参与进来开发或者想进群学习,可以添加我微信 yunmz777,后面还会有很多需求,等这个项目完成之后还会有很多新的并且很有趣的开源项目等着你。

相关推荐
hackeroink18 分钟前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者2 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-2 小时前
验证码机制
前端·后端
燃先生._.3 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
高山我梦口香糖4 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235244 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240255 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar5 小时前
纯前端实现更新检测
开发语言·前端·javascript