You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用FFmpeg判断视频右上角区域平均颜色并选择适配水印?

优化视频水印适配方案:快速计算区域颜色+智能选水印

我来帮你搞定这两个核心问题——大幅提升视频右上角区域平均颜色的计算速度,以及根据颜色结果精准选择适配水印。你的现有思路方向是对的,但效率瓶颈主要在频繁的截图IO和冗余采样上,咱们一步步优化:


一、快速计算视频右上角区域的平均颜色(替代慢截图方案)

你的当前方案每10秒生成一张截图再处理,IO开销极大,长视频肯定慢。推荐用FFmpeg直接在命令行层面处理,跳过临时文件生成,同时优化采样策略:

1. 核心优化思路:用FFmpeg直接提取区域颜色,无需生成截图

FFmpeg的滤镜链可以直接裁剪右上角区域、缩成1x1像素(即平均颜色),并输出颜色值,完全不用生成中间图片。在Ruby里可以通过调用系统命令来获取结果,示例代码如下:

def get_average_color(video_file, region="20%x20%+80%+0")
  # 用FFmpeg裁剪指定区域,缩成1x1像素,输出RGB颜色
  cmd = <<~CMD
    ffmpeg -i "#{video_file}" -vf "crop=#{region},scale=1x1:flags=neighbor" -f framehash -loglevel error -
  CMD
  output = `#{cmd}`.strip
  # 解析输出的framehash格式,提取RGB值(格式类似:0,0,0xRRGGBB)
  return nil unless output.match?(/0,0,0x([0-9A-Fa-f]{6})/)
  hex_color = $1
  [
    hex_color[0..1].to_i(16),  # R
    hex_color[2..3].to_i(16),  # G
    hex_color[4..5].to_i(16)   # B
  ]
end

2. 采样策略优化:只采样关键帧,减少计算量

等间隔采样会浪费很多资源在重复帧上,视频的**关键帧(I帧)**是画面变化的核心节点,采样这些帧足够代表整体颜色分布,数量远少于等间隔采样。获取关键帧时间的命令:

def get_keyframe_times(video_file)
  cmd = <<~CMD
    ffmpeg -i "#{video_file}" -vf "select='eq(pict_type,PICT_TYPE_I)'" -f null - 2>&1 | grep "pts_time" | awk '{print $4}'
  CMD
  `#{cmd}`.split("\n").map(&:to_f).uniq
end

3. 整合后的快速计算流程

black = [0, 0, 0]
white = [255, 255, 255]
video_file = "your_video.mp4"

# 获取所有关键帧时间点
keyframe_times = get_keyframe_times(video_file)
# 如果没有关键帧, fallback到等间隔采样(比如每30秒一次)
keyframe_times = (0..(FFMPEG::Movie.new(video_file).duration.to_i)).step(30).to_a if keyframe_times.empty?

black_distances = []
white_distances = []
gray_values = []

keyframe_times.each do |time|
  # 对每个关键帧,获取右上角区域的平均颜色
  cmd = <<~CMD
    ffmpeg -i "#{video_file}" -ss #{time} -vf "crop=20%x20%+80%+0,scale=1x1:flags=neighbor" -vframes 1 -f framehash -loglevel error -
  CMD
  output = `#{cmd}`.strip
  next unless output.match?(/0,0,0x([0-9A-Fa-f]{6})/)
  
  hex_color = $1
  pixel = [
    hex_color[0..1].to_i(16),
    hex_color[2..3].to_i(16),
    hex_color[4..5].to_i(16)
  ]
  
  # 计算距离和灰度值
  distance_from_black = Math.sqrt(((black[0] - pixel[0])**2 + (black[1] - pixel[1])**2 + (black[2] - pixel[2])**2))
  distance_from_white = Math.sqrt(((white[0] - pixel[0])**2 + (white[1] - pixel[1])**2 + (white[2] - pixel[2])**2))
  gray = 0.299 * pixel[0] + 0.587 * pixel[1] + 0.114 * pixel[2]
  
  black_distances << distance_from_black
  white_distances << distance_from_white
  gray_values << gray
end

# 计算平均值
average_black_distance = black_distances.sum.to_f / black_distances.size
average_white_distance = white_distances.sum.to_f / white_distances.size
average_gray = gray_values.sum.to_f / gray_values.size

二、根据计算结果选择适配水印

这里有两种直观的判断逻辑,选哪种都可以:

1. 基于灰度值的判断(更直接)

RGB转灰度的公式是行业通用的,灰度值范围0(纯黑)到255(纯白):

  • 如果 average_gray > 127:右上角区域整体偏亮,选择偏暗的水印(对比度更高,更清晰)
  • 如果 average_gray <= 127:区域偏暗,选择偏亮的水印

2. 基于你的距离值判断

你已经计算了到黑色和白色的平均距离:

  • 如果 average_black_distance > average_white_distance:说明区域颜色整体离白色更近(偏亮),用深色水印
  • 如果 average_black_distance <= average_white_distance:区域离黑色更近(偏暗),用浅色水印

比如用灰度值的判断代码:

if average_gray > 127
  selected_watermark = "dark_watermark.png"
else
  selected_watermark = "light_watermark.png"
end

额外优化建议

  • 如果视频是直播或者超长篇幅,可以进一步减少采样数量(比如只取前5个、中间1个、最后5个关键帧),平衡速度和准确性
  • 可以缓存计算结果,同一视频不用重复计算
  • 用FFmpeg直接给视频加水印,不用额外工具:比如选好水印后,用ffmpeg -i input.mp4 -i #{selected_watermark} -filter_complex "overlay=W-w-10:H-h-10" output.mp4,右上角偏移10像素

内容的提问来源于stack exchange,提问作者Navid Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:41:07