change tint and saturation of live camera in flutter using camera package

108 views Asked by At

change tint and saturation of live camera in flutter using camera package I have developed flutter app with camera package that preview live camera and record video or shoot photo and saves it. I want to change color and saturation of preview and apply it to video and camera. how can I achieve that/.

nothing found on changing tint and saturation

1

There are 1 answers

1
rmtmckenzie On

This is going to have to be a two step process unless you want to re-write parts of the camera package. You might be able to get the frames from the camera and process them directly but my guess is that it isn't possible to do this in dart without having major performance issues.

Instead, the first step is going to be to change the preview in real-time to show hue + saturation changes. To do this, you can use ColorFilter widgets which wrap the preview widget. You're going to need to set these ColorFilters to perform the manipulation you'd like; I'd recommend looking at the Color Filter Generator pub package which includes the math for a bunch of different matrices including hue, saturation, brightness.

This is an example of what that would look like - you can paste into dartpad.dev to see what it looks like. But the gist is that I'm using the hueMatrix and saturationMatrix methods to generate matrices that I then pass to ImageFilter objects. If you wanted, you could even combine the function that makes the hueMatrix and saturationMatrix into one, but I'll leave that as an exercise for you to complete. In the example I'm applying the filter to text but it should work just the same for the camera preview.

import 'package:flutter/material.dart';
import 'dart:math';

// from https://github.com/hsbijarniya/colorfilter_generator
List<double> hueMatrix(double value) {
  value = value * pi;

  if (value == 0) {
    return [
      1,
      0,
      0,
      0,
      0,
      0,
      1,
      0,
      0,
      0,
      0,
      0,
      1,
      0,
      0,
      0,
      0,
      0,
      1,
      0,
    ];
  }

  var cosVal = cos(value);
  var sinVal = sin(value);
  var lumR = 0.213;
  var lumG = 0.715;
  var lumB = 0.072;

  return List<double>.from(<double>[
    (lumR + (cosVal * (1 - lumR))) + (sinVal * (-lumR)),
    (lumG + (cosVal * (-lumG))) + (sinVal * (-lumG)),
    (lumB + (cosVal * (-lumB))) + (sinVal * (1 - lumB)),
    0,
    0,
    (lumR + (cosVal * (-lumR))) + (sinVal * 0.143),
    (lumG + (cosVal * (1 - lumG))) + (sinVal * 0.14),
    (lumB + (cosVal * (-lumB))) + (sinVal * (-0.283)),
    0,
    0,
    (lumR + (cosVal * (-lumR))) + (sinVal * (-(1 - lumR))),
    (lumG + (cosVal * (-lumG))) + (sinVal * lumG),
    (lumB + (cosVal * (1 - lumB))) + (sinVal * lumB),
    0,
    0,
    0,
    0,
    0,
    1,
    0,
  ]).map((i) => i.toDouble()).toList();
}

// from https://github.com/hsbijarniya/colorfilter_generator
List<double> saturationMatrix(double value) {
  value = value * 100;

  if (value == 0) {
    return [
      1,
      0,
      0,
      0,
      0,
      0,
      1,
      0,
      0,
      0,
      0,
      0,
      1,
      0,
      0,
      0,
      0,
      0,
      1,
      0,
    ];
  }

  var x =
      ((1 + ((value > 0) ? ((3 * value) / 100) : (value / 100)))).toDouble();
  var lumR = 0.3086;
  var lumG = 0.6094;
  var lumB = 0.082;

  return List<double>.from(<double>[
    (lumR * (1 - x)) + x,
    lumG * (1 - x),
    lumB * (1 - x),
    0,
    0,
    lumR * (1 - x),
    (lumG * (1 - x)) + x,
    lumB * (1 - x),
    0,
    0,
    lumR * (1 - x),
    lumG * (1 - x),
    (lumB * (1 - x)) + x,
    0,
    0,
    0,
    0,
    0,
    1,
    0,
  ]).map((i) => i.toDouble()).toList();
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double hue = 0;
  double saturation = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.max,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            MyWidget(hue: hue, saturation: saturation),
            Text("Hue:"),
            Slider(
              value: hue / 2 + .5,
              onChanged: (val) => setState(() => hue = val * 2 - 1),
            ),
            Text("Saturation:"),
            Slider(
              value: saturation / 2 + .5,
              onChanged: (val) => setState(() => saturation = val * 2 - 1),
            )
          ],
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  final double hue;
  final double saturation;

  const MyWidget({super.key, required this.hue, required this.saturation});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return ColorFiltered(
      colorFilter: ColorFilter.matrix(hueMatrix(hue)),
      child: ColorFiltered(
        colorFilter: ColorFilter.matrix(
          saturationMatrix(saturation),
        ),
        child: Text(
          'Hello, World!',
          style: theme.textTheme.headlineMedium!
              .copyWith(color: theme.primaryColor),
        ),
      ),
    );
  }
}

This satisfies the first part of your problem; you now have a preview which is showing the video with a saturation, hue, or whatever else applied.


The second part is going to be a lot more dependent on a bunch of other factors such as which format you're recording, what you're doing with the video etc. But basically, once you call stopVideoRecording on the camera, you'll be given a XFile object which contains the data from the video. Should be similar for if you're taking a picture.

You might be able to get away with using a plugin such as tapioca for video to then edit that file to add the right tint, but I have a feeling it doesn't have enough functionality. If that is the case, you'll probably need to include some 3rd party android-specific (and iOS specific if you're using it) package and then call into them from flutter to edit the actual hue & saturation and whatever else for the video file, and then wait for that to process, and then do whatever else you were going to do with the file.

For images, you'll have a similar process except that it might be easier as there are more plugins available and one might be able to do the photo manipulation for you - image editor might do the trick as it appears to support applying a color matrix.