Do image files of type XFile have less quality than images of type File in Flutter?

Viewed 2036

I'm working with the Camera plugin in Flutter and by default when it captures the image it returns it in XFile format. I've noticed that the quality of the image is really good inside the camera preview screen before capturing, it has the same quality as the native camera app on my phone. But when I capture the image and try to send the image file to another screen and display it in an Image.file() widget, the quality drops and it becomes very grainy. I was trying to find out if that's an issue related with the XFile format or the camera plugin itself.

I have tried adding a delay before capture and I have tried to use two different devices, but it doesn't solve the grainy photo problem. Below you'll find some screenshots to better demonstrate the problem, and along with it is the code I'm using to capture, save and display the image across my screens.

P.S. I have tried to research more on XFile but there isn't much on the web except the documentation and it doesn't offer a lot of helpful information, so any help is much appreciated.

Screenshots:

In Camera Preview (Live camera, quality is good):

enter image description here

In PreviewScreen (post-capture, you can see quality is getting a bit grainy):

enter image description here

In PhotoTakenScreen (very grainy, quality is very bad):

enter image description here

Code:

Inside CameraScreen (on pressing the capture button):

onPressed: () async{
var path;

if (cameraController!.value.isInitialized) {
      await cameraController!.takePicture().then((XFile file) {
        if (mounted) {
          path = file.path;
          print(path);
        }

Future.delayed(const Duration(milliseconds: 500), () {
                Navigator.pushNamed(
                  context,
                  cameraPreviewRoute,
                  arguments: PreviewScreen(
                    imageFile: File(file.path),
                  ),
                );
              });
       }

In PreviewScreen:

....
Scaffold(
body: body: Container(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            flex: 2,
            child: Image.file(
              File(widget.imageFile.path),
              fit: BoxFit.cover,
            ),
          ),
        ],
      ),
    ),
  )

There is a button in PreviewScreen widget that goes to PhotoTakenScreen, this is what it does:

onPressed: () async {
        try {
          //adding a toast here because saving _loadImage takes a few seconds before moving to the next screen
          showToast("Please wait while image is being processed..", false,
              info: true);

          File savedImage;
          await _loadImage(widget.imageFile.path).then((value) {
            savedImage = value!; 
            // for debugging
            print(savedImage.path);

            //send the image object over to phototaken
            Navigator.pushNamed(
              context,
              photoTakenScreen,
              arguments: PhotoTakenScreen(
                imagePath: savedImage.path,
              ),
            );
           }

loadImage() is a function that calls saveImageToDisk() which saves the image to the disk in a separate thread and returns the saved file. Here they are:

Future<File?> _loadImage(String imagePath) async {
try {
  File mFile;
  Directory directory = await getTemporaryDirectory();
  Map map = Map();
  map['path'] = imagePath;
  map['directory'] = directory;
  // run _saveImageToDisk in a separate thread
  mFile = await compute(saveImageToDisk, map);
  return mFile;
} catch (err) {
  rethrow;
 }
}

Future<File> saveImageToDisk(Map pathAndDirectory) async {
  try {
    String path = pathAndDirectory['path'];
    Directory directory = pathAndDirectory['directory'];
    File tempFile = File(path);
    Img.Image? image = Img.decodeImage(tempFile.readAsBytesSync());
    if (image == null) throw ('Error: Could not decode image');
    Img.Image mImage = Img.copyResizeCropSquare(image, 224);
    String imgType = path.split('.').last;
    String mPath =
        '${directory.path.toString()}/image_${DateTime.now()}.$imgType';
    mPath = mPath.replaceAll(
        new RegExp(r"\s+\b|\b\s"), ""); //remove all spaces from img path
    File dFile = File(mPath);
    if (imgType == 'jpg' || imgType == 'jpeg') {
      dFile.writeAsBytesSync(Img.encodeJpg(mImage));
    } else {
      dFile.writeAsBytesSync(Img.encodePng(mImage));
    }
    return dFile;
  } catch (e) {
    rethrow; //rethrow to alert _loadImage in PreviewScreen()
  }
}
1 Answers

Maybe check the 2nd parameter 'ResolutionPreset' of your CameraController constructor. Maybe change to ResolutionPreset.max

Related