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

如何在Flutter中使用camera包调整实时相机的色调与饱和度

解决方案

一、实时预览的色调/饱和度调整

camera 包的 CameraPreview 本身不自带滤镜API,但可以用Flutter原生的 ColorFiltered 组件包裹预览,通过颜色矩阵实现实时色调与饱和度调整,性能开销低且易实现。

核心代码实现

import 'dart:math' as math;

// 生成饱和度调整矩阵(1为原始值,<1降低饱和度,>1增强饱和度)
Matrix4 saturationMatrix(double saturation) {
  final double r = 0.299 * (1 - saturation);
  final double g = 0.587 * (1 - saturation);
  final double b = 0.114 * (1 - saturation);
  return Matrix4.fromList([
    r + saturation, g, b, 0, 0,
    r, g + saturation, b, 0, 0,
    r, g, b + saturation, 0, 0,
    0, 0, 0, 1, 0,
  ]);
}

// 生成色调旋转矩阵(angle为弧度,如math.pi/3对应60度)
Matrix4 hueRotateMatrix(double angle) {
  final double cosA = math.cos(angle);
  final double sinA = math.sin(angle);
  final double lumR = 0.299;
  final double lumG = 0.587;
  final double lumB = 0.114;
  return Matrix4.fromList([
    lumR + cosA * (1 - lumR) + sinA * (-lumR), lumG + cosA * (-lumG) + sinA * (-lumG), lumB + cosA * (-lumB) + sinA * (1 - lumB), 0, 0,
    lumR + cosA * (-lumR) + sinA * (0.143), lumG + cosA * (1 - lumG) + sinA * (0.140), lumB + cosA * (-lumB) + sinA * (-0.283), 0, 0,
    lumR + cosA * (-lumR) + sinA * (-(1 - lumR)), lumG + cosA * (-lumG) + sinA * (lumG), lumB + cosA * (1 - lumB) + sinA * (lumB), 0, 0,
    0, 0, 0, 1, 0,
  ]);
}

// 组合色调与饱和度矩阵
Matrix4 combineFilters(double hueAngle, double saturation) {
  return saturationMatrix(saturation) * hueRotateMatrix(hueAngle);
}

// 应用到CameraPreview
ColorFiltered(
  colorFilter: ColorFilter.matrix(combineFilters(math.pi/3, 1.5).storage),
  child: CameraPreview(controller),
)

二、拍摄照片的滤镜应用

拍摄得到的 XFile 需要通过 image 库处理像素,调整后再保存。

步骤与代码

  1. 添加依赖:在 pubspec.yaml 中加入 image: ^4.0.17(版本以最新为准)
  2. 处理照片:
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart';
import 'dart:io';

Future<XFile> applyFilterToPhoto(XFile rawPhoto, double hueAngle, double saturation) async {
  // 读取原始照片 bytes
  final bytes = await rawPhoto.readAsBytes();
  img.Image image = img.decodeImage(bytes)!;

  // 调整饱和度与色调(image库的hue参数为0-360的角度值)
  img.adjustColor(image, saturation: saturation);
  img.adjustColor(image, hue: hueAngle * 180 / math.pi);

  // 保存处理后的照片到临时目录
  final tempDir = await getTemporaryDirectory();
  final tempPath = '${tempDir.path}/filtered_photo.jpg';
  final file = File(tempPath);
  await file.writeAsBytes(img.encodeJpg(image, quality: 90));

  return XFile(tempPath);
}

// 使用示例
final XFile rawPhoto = await controller.takePicture();
final XFile filteredPhoto = await applyFilterToPhoto(rawPhoto, math.pi/3, 1.5);
// 后续可将filteredPhoto保存到相册

三、录制视频的滤镜应用

camera 包的原生录制不支持实时滤镜,需先录制原始视频,再通过 flutter_ffmpeg 工具处理添加滤镜。

步骤与代码

  1. 添加依赖:在 pubspec.yaml 中加入 flutter_ffmpeg: ^0.4.0-lts(选择全功能LTS版本避免功能缺失)
  2. 处理视频:
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
import 'package:path_provider/path_provider.dart';

final FlutterFFmpeg _ffmpeg = FlutterFFmpeg();

Future<String> applyFilterToVideo(String rawVideoPath, double hueAngle, double saturation) async {
  final tempDir = await getTemporaryDirectory();
  final outputPath = '${tempDir.path}/filtered_video.mp4';

  // FFmpeg命令:用hue滤镜调整色调与饱和度,复用原音频编码
  final command = '-i $rawVideoPath -vf "hue=${hueAngle*180/math.pi}:s=$saturation" -c:v libx264 -c:a copy $outputPath';
  
  final int resultCode = await _ffmpeg.execute(command);
  if (resultCode == 0) {
    return outputPath;
  } else {
    throw Exception('视频滤镜处理失败,FFmpeg返回码:$resultCode');
  }
}

// 使用示例
await controller.startVideoRecording();
// 录制结束后
final XFile rawVideo = await controller.stopVideoRecording();
final String filteredVideoPath = await applyFilterToVideo(rawVideo.path, math.pi/3, 1.5);
// 后续可将filteredVideoPath对应的文件保存到相册

补充提示

  • 如果需要更复杂的GPU加速滤镜,可使用 flutter_image_filters 库,它支持将滤镜直接绑定到CameraPreview的Texture上,实时性能更优。
  • 若追求录制时的实时滤镜预览+录制,可替换为第三方相机库(如 camera_camera),这类库内置了滤镜录制支持。

内容的提问来源于stack exchange,提问作者tushar nariya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 11:46:37