From 9b743d7afe7adf3e4ceaed36c5a37ed242c6514e Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Fri, 6 Sep 2024 13:51:01 +0200 Subject: [PATCH] [mob][photos] Stop using ByteData directly --- .../face_detection_service.dart | 6 +- .../face_ml/face_recognition_service.dart | 16 +- .../clip/clip_image_encoder.dart | 6 +- .../semantic_search_service.dart | 4 +- mobile/lib/utils/image_ml_util.dart | 222 +++++++++--------- mobile/lib/utils/ml_util.dart | 6 +- 6 files changed, 127 insertions(+), 133 deletions(-) diff --git a/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart b/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart index 7622b011d8..38d085bf5c 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_detection/face_detection_service.dart @@ -1,5 +1,5 @@ import "dart:async"; -import 'dart:typed_data' show ByteData, Float32List; +import 'dart:typed_data' show Uint8List, Float32List; import 'dart:ui' as ui show Image; import 'package:logging/logging.dart'; @@ -44,7 +44,7 @@ class FaceDetectionService extends MlModel { /// Detects faces in the given image data. static Future> predict( ui.Image image, - ByteData imageByteData, + Uint8List rawRgbaBytes, int sessionAddress, ) async { assert( @@ -59,7 +59,7 @@ class FaceDetectionService extends MlModel { final (inputImageList, newSize) = await preprocessImageToFloat32ChannelsFirst( image, - imageByteData, + rawRgbaBytes, normalization: 1, requiredWidth: kInputWidth, requiredHeight: kInputHeight, diff --git a/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart b/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart index 22e3019517..91eda670f8 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart @@ -1,5 +1,5 @@ import "dart:async" show unawaited; -import "dart:typed_data" show ByteData, Float32List; +import "dart:typed_data" show Uint8List, Float32List; import "dart:ui" show Image; import "package:logging/logging.dart"; @@ -70,7 +70,7 @@ class FaceRecognitionService { static Future> runFacesPipeline( int enteFileID, Image image, - ByteData imageByteData, + Uint8List rawRgbaBytes, int faceDetectionAddress, int faceEmbeddingAddress, ) async { @@ -82,7 +82,7 @@ class FaceRecognitionService { await _detectFacesSync( enteFileID, image, - imageByteData, + rawRgbaBytes, faceDetectionAddress, faceResults, ); @@ -100,7 +100,7 @@ class FaceRecognitionService { // Align the faces final Float32List faceAlignmentResult = await _alignFacesSync( image, - imageByteData, + rawRgbaBytes, faceDetectionResult, faceResults, ); @@ -130,7 +130,7 @@ class FaceRecognitionService { static Future> _detectFacesSync( int fileID, Image image, - ByteData imageByteData, + Uint8List rawRgbaBytes, int interpreterAddress, List faceResults, ) async { @@ -139,7 +139,7 @@ class FaceRecognitionService { final List faces = await FaceDetectionService.predict( image, - imageByteData, + rawRgbaBytes, interpreterAddress, ); @@ -166,7 +166,7 @@ class FaceRecognitionService { /// Returns a list of the aligned faces as image data. static Future _alignFacesSync( Image image, - ByteData imageByteData, + Uint8List rawRgbaBytes, List faces, List faceResults, ) async { @@ -174,7 +174,7 @@ class FaceRecognitionService { final (alignedFaces, alignmentResults, _, blurValues, _) = await preprocessToMobileFaceNetFloat32List( image, - imageByteData, + rawRgbaBytes, faces, ); diff --git a/mobile/lib/services/machine_learning/semantic_search/clip/clip_image_encoder.dart b/mobile/lib/services/machine_learning/semantic_search/clip/clip_image_encoder.dart index c25ef45526..f51cf25516 100644 --- a/mobile/lib/services/machine_learning/semantic_search/clip/clip_image_encoder.dart +++ b/mobile/lib/services/machine_learning/semantic_search/clip/clip_image_encoder.dart @@ -1,4 +1,4 @@ -import "dart:typed_data"; +import "dart:typed_data" show Uint8List, Float32List; import "dart:ui" show Image; import "package:logging/logging.dart"; @@ -29,12 +29,12 @@ class ClipImageEncoder extends MlModel { static Future> predict( Image image, - ByteData imageByteData, + Uint8List rawRgbaBytes, int sessionAddress, [ int? enteFileID, ]) async { final startTime = DateTime.now(); - final inputList = await preprocessImageClip(image, imageByteData); + final inputList = await preprocessImageClip(image, rawRgbaBytes); final preprocessingTime = DateTime.now(); final preprocessingMs = preprocessingTime.difference(startTime).inMilliseconds; diff --git a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart index 1135ca3b66..65d70b5c96 100644 --- a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -258,12 +258,12 @@ class SemanticSearchService { static Future runClipImage( int enteFileID, Image image, - ByteData imageByteData, + Uint8List rawRgbaBytes, int clipImageAddress, ) async { final embedding = await ClipImageEncoder.predict( image, - imageByteData, + rawRgbaBytes, clipImageAddress, enteFileID, ); diff --git a/mobile/lib/utils/image_ml_util.dart b/mobile/lib/utils/image_ml_util.dart index d1410b65fc..30bf5efe75 100644 --- a/mobile/lib/utils/image_ml_util.dart +++ b/mobile/lib/utils/image_ml_util.dart @@ -1,8 +1,7 @@ import "dart:async"; -import "dart:developer" show log; import "dart:io" show File, Platform; import "dart:math" show max, min; -import "dart:typed_data" show Float32List, Uint8List, ByteData; +import "dart:typed_data" show Float32List, Uint8List; import "dart:ui"; import 'package:flutter/painting.dart' as paint show decodeImageFromList; @@ -21,12 +20,18 @@ import 'package:photos/services/machine_learning/face_ml/face_filtering/blur_det final _logger = Logger("ImageMlUtil"); -Future<(Image, ByteData)> decodeImageFromPath(String imagePath) async { +/// These are 8 bit unsigned integers in range 0-255 for each RGB channel +typedef RGB = (int, int, int); + +const maxKernelSize = 5; +const maxKernelRadius = maxKernelSize ~/ 2; + +Future<(Image, Uint8List)> decodeImageFromPath(String imagePath) async { try { final imageData = await File(imagePath).readAsBytes(); final image = await decodeImageFromData(imageData); - final ByteData imageByteData = await getByteDataFromImage(image); - return (image, imageByteData); + final rawRgbaBytes = await _getRawRgbaBytes(image); + return (image, rawRgbaBytes); } catch (e, s) { final format = imagePath.split('.').last; if (Platform.isAndroid) { @@ -37,8 +42,8 @@ Future<(Image, ByteData)> decodeImageFromPath(String imagePath) async { _logger.info('Conversion successful, decoding JPG'); final imageData = await File(jpgPath).readAsBytes(); final image = await decodeImageFromData(imageData); - final ByteData imageByteData = await getByteDataFromImage(image); - return (image, imageByteData); + final rawRgbaBytes = await _getRawRgbaBytes(image); + return (image, rawRgbaBytes); } _logger.info('Unable to convert $format to JPG'); } @@ -78,25 +83,31 @@ Future decodeImageFromData(Uint8List imageData) async { // final Image image = await completer.future; // stream.removeListener(listener); // return image; +} - // // Decoding using the ImageProvider from material.Image. This is not faster than the above, and also the code below is not finished! - // final materialImage = material.Image.memory(imageData); - // final ImageProvider uiImage = await materialImage.image; +Future _getRawRgbaBytes(Image image) async { + return await _getByteDataFromImage(image, format: ImageByteFormat.rawRgba); +} + +/// Encodes an [Image] object to a [Uint8List], in the png format. +/// Can be used with `Image.memory()`. +Future _encodeImageToPng(Image image) async { + return await _getByteDataFromImage(image, format: ImageByteFormat.png); } /// Returns the [ByteData] object of the image, in rawRgba format. /// /// Throws an exception if the image could not be converted to ByteData. -Future getByteDataFromImage( +Future _getByteDataFromImage( Image image, { - ImageByteFormat format = ImageByteFormat.rawRgba, + required ImageByteFormat format, }) async { - final ByteData? byteDataRgba = await image.toByteData(format: format); - if (byteDataRgba == null) { - log('[ImageMlUtils] Could not convert image to ByteData'); - throw Exception('Could not convert image to ByteData'); + final byteData = await image.toByteData(format: format); + if (byteData == null) { + _logger.severe('Failed to get byte data in $format from image'); + throw Exception('Failed to get byte data in $format from image'); } - return byteDataRgba; + return byteData.buffer.asUint8List(); } /// Generates a face thumbnail from [imageData] and [faceBoxes]. @@ -161,11 +172,11 @@ Future> generateFaceThumbnailsUsingCanvas( Future<(Float32List, Dimensions)> preprocessImageToFloat32ChannelsFirst( Image image, - ByteData imgByteData, { + Uint8List rawRgbaBytes, { required int normalization, required int requiredWidth, required int requiredHeight, - Color Function(num, num, Image, ByteData) getPixel = _getPixelBilinear, + RGB Function(num, num, Image, Uint8List) getPixel = _getPixelBilinear, maintainAspectRatio = true, }) async { final normFunction = normalization == 2 @@ -193,20 +204,20 @@ Future<(Float32List, Dimensions)> preprocessImageToFloat32ChannelsFirst( final int channelOffsetBlue = 2 * requiredHeight * requiredWidth; for (var h = 0; h < requiredHeight; h++) { for (var w = 0; w < requiredWidth; w++) { - late Color pixel; + late RGB pixel; if (w >= scaledWidth || h >= scaledHeight) { - pixel = const Color.fromRGBO(114, 114, 114, 1.0); + pixel = const (114, 114, 114); } else { pixel = getPixel( w / scaleW, h / scaleH, image, - imgByteData, + rawRgbaBytes, ); } - buffer[pixelIndex] = normFunction(pixel.red); - buffer[pixelIndex + channelOffsetGreen] = normFunction(pixel.green); - buffer[pixelIndex + channelOffsetBlue] = normFunction(pixel.blue); + buffer[pixelIndex] = normFunction(pixel.$1); + buffer[pixelIndex + channelOffsetGreen] = normFunction(pixel.$2); + buffer[pixelIndex + channelOffsetBlue] = normFunction(pixel.$3); pixelIndex++; } } @@ -216,7 +227,7 @@ Future<(Float32List, Dimensions)> preprocessImageToFloat32ChannelsFirst( Future preprocessImageClip( Image image, - ByteData imgByteData, + Uint8List rawRgbaBytes, ) async { const int requiredWidth = 256; const int requiredHeight = 256; @@ -234,15 +245,15 @@ Future preprocessImageClip( const int blueOff = 2 * requiredHeight * requiredWidth; for (var h = 0 + heightOffset; h < scaledHeight - heightOffset; h++) { for (var w = 0 + widthOffset; w < scaledWidth - widthOffset; w++) { - final Color pixel = _getPixelBilinear( + final RGB pixel = _getPixelBilinear( w / scale, h / scale, image, - imgByteData, + rawRgbaBytes, ); - buffer[pixelIndex] = pixel.red / 255; - buffer[pixelIndex + greenOff] = pixel.green / 255; - buffer[pixelIndex + blueOff] = pixel.blue / 255; + buffer[pixelIndex] = pixel.$1 / 255; + buffer[pixelIndex + greenOff] = pixel.$2 / 255; + buffer[pixelIndex + blueOff] = pixel.$3 / 255; pixelIndex++; } } @@ -253,7 +264,7 @@ Future preprocessImageClip( Future<(Float32List, List, List, List, Size)> preprocessToMobileFaceNetFloat32List( Image image, - ByteData imageByteData, + Uint8List rawRgbaBytes, List relativeFaces, { int width = 112, int height = 112, @@ -290,7 +301,7 @@ Future<(Float32List, List, List, List, Size)> _warpAffineFloat32List( image, - imageByteData, + rawRgbaBytes, alignmentResult.affineMatrix, alignedImagesFloat32List, alignedImageIndex, @@ -320,29 +331,30 @@ Future<(Float32List, List, List, List, Size)> } /// Reads the pixel color at the specified coordinates. -Color _readPixelColor( - Image image, - ByteData byteData, +RGB _readPixelColor( int x, int y, + Image image, + Uint8List rgbaBytes, ) { - if (x < 0 || x >= image.width || y < 0 || y >= image.height) { - // throw ArgumentError('Invalid pixel coordinates.'); - if (y != -1) { - log('[WARNING] `readPixelColor`: Invalid pixel coordinates, out of bounds'); - } - return const Color.fromARGB(0, 0, 0, 0); + if (y < -maxKernelRadius || + y >= image.height + maxKernelRadius || + x < -maxKernelRadius || + x >= image.width + maxKernelRadius) { + _logger.severe( + '`readPixelColor`: Invalid pixel coordinates, out of bounds. x: $x, y: $y', + ); + return const (114, 114, 114); } - assert(byteData.lengthInBytes == 4 * image.width * image.height); + + assert(rgbaBytes.lengthInBytes == 4 * image.width * image.height); final int byteOffset = 4 * (image.width * y + x); - return Color(_rgbaToArgb(byteData.getUint32(byteOffset))); -} - -int _rgbaToArgb(int rgbaColor) { - final int a = rgbaColor & 0xFF; - final int rgb = rgbaColor >> 8; - return rgb + (a << 24); + return ( + rgbaBytes[byteOffset], // red + rgbaBytes[byteOffset + 1], // green + rgbaBytes[byteOffset + 2] // blue + ); } List> _createGrayscaleIntMatrixFromNormalized2List( @@ -398,20 +410,6 @@ double _normalizePixelNoRange(num pixelValue) { return pixelValue.toDouble(); } -/// Encodes an [Image] object to a [Uint8List], by default in the png format. -/// -/// Note that the result can be used with `Image.memory()` only if the [format] is png. -Future _encodeImageToUint8List( - Image image, { - ImageByteFormat format = ImageByteFormat.png, -}) async { - final ByteData byteDataPng = - await getByteDataFromImage(image, format: format); - final encodedImage = byteDataPng.buffer.asUint8List(); - - return encodedImage; -} - Future _cropImage( Image image, { required double x, @@ -447,7 +445,7 @@ Future _cropImage( void _warpAffineFloat32List( Image inputImage, - ByteData imgByteDataRgba, + Uint8List rawRgbaBytes, List> affineMatrix, Float32List outputList, int startIndex, { @@ -501,16 +499,16 @@ void _warpAffineFloat32List( final num xOrigin = (xTrans - b00) * a00Prime + (yTrans - b10) * a01Prime; final num yOrigin = (xTrans - b00) * a10Prime + (yTrans - b10) * a11Prime; - final Color pixel = - _getPixelBicubic(xOrigin, yOrigin, inputImage, imgByteDataRgba); + final RGB pixel = + _getPixelBicubic(xOrigin, yOrigin, inputImage, rawRgbaBytes); // Set the new pixel outputList[startIndex + 3 * (yTrans * width + xTrans)] = - _normalizePixelRange2(pixel.red); + _normalizePixelRange2(pixel.$1); outputList[startIndex + 3 * (yTrans * width + xTrans) + 1] = - _normalizePixelRange2(pixel.green); + _normalizePixelRange2(pixel.$2); outputList[startIndex + 3 * (yTrans * width + xTrans) + 2] = - _normalizePixelRange2(pixel.blue); + _normalizePixelRange2(pixel.$3); } } } @@ -529,13 +527,10 @@ Future _cropAndEncodeCanvas( width: width, height: height, ); - return await _encodeImageToUint8List( - croppedImage, - format: ImageByteFormat.png, - ); + return await _encodeImageToPng(croppedImage); } -Color _getPixelBilinear(num fx, num fy, Image image, ByteData byteDataRgba) { +RGB _getPixelBilinear(num fx, num fy, Image image, Uint8List rawRgbaBytes) { // Clamp to image boundaries fx = fx.clamp(0, image.width - 1); fy = fy.clamp(0, image.height - 1); @@ -551,10 +546,10 @@ Color _getPixelBilinear(num fx, num fy, Image image, ByteData byteDataRgba) { final dy1 = 1.0 - dy; // Get the original pixels - final Color pixel1 = _readPixelColor(image, byteDataRgba, x0, y0); - final Color pixel2 = _readPixelColor(image, byteDataRgba, x1, y0); - final Color pixel3 = _readPixelColor(image, byteDataRgba, x0, y1); - final Color pixel4 = _readPixelColor(image, byteDataRgba, x1, y1); + final RGB pixel1 = _readPixelColor(x0, y0, image, rawRgbaBytes); + final RGB pixel2 = _readPixelColor(x1, y0, image, rawRgbaBytes); + final RGB pixel3 = _readPixelColor(x0, y1, image, rawRgbaBytes); + final RGB pixel4 = _readPixelColor(x1, y1, image, rawRgbaBytes); int bilinear( num val1, @@ -566,16 +561,15 @@ Color _getPixelBilinear(num fx, num fy, Image image, ByteData byteDataRgba) { .round(); // Calculate the weighted sum of pixels - final int r = bilinear(pixel1.red, pixel2.red, pixel3.red, pixel4.red); - final int g = - bilinear(pixel1.green, pixel2.green, pixel3.green, pixel4.green); - final int b = bilinear(pixel1.blue, pixel2.blue, pixel3.blue, pixel4.blue); + final int r = bilinear(pixel1.$1, pixel2.$1, pixel3.$1, pixel4.$1); + final int g = bilinear(pixel1.$2, pixel2.$2, pixel3.$2, pixel4.$2); + final int b = bilinear(pixel1.$3, pixel2.$3, pixel3.$3, pixel4.$3); - return Color.fromRGBO(r, g, b, 1.0); + return (r, g, b); } /// Get the pixel value using Bicubic Interpolation. Code taken mainly from https://github.com/brendan-duncan/image/blob/6e407612752ffdb90b28cd5863c7f65856349348/lib/src/image/image.dart#L697 -Color _getPixelBicubic(num fx, num fy, Image image, ByteData byteDataRgba) { +RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) { fx = fx.clamp(0, image.width - 1); fy = fy.clamp(0, image.height - 1); @@ -596,66 +590,66 @@ Color _getPixelBicubic(num fx, num fy, Image image, ByteData byteDataRgba) { dx * dx * (2 * ipp - 5 * icp + 4 * inp - iap) + dx * dx * dx * (-ipp + 3 * icp - 3 * inp + iap)); - final icc = _readPixelColor(image, byteDataRgba, x, y); + final icc = _readPixelColor(x, y, image, rawRgbaBytes); final ipp = - px < 0 || py < 0 ? icc : _readPixelColor(image, byteDataRgba, px, py); - final icp = px < 0 ? icc : _readPixelColor(image, byteDataRgba, x, py); + px < 0 || py < 0 ? icc : _readPixelColor(px, py, image, rawRgbaBytes); + final icp = px < 0 ? icc : _readPixelColor(x, py, image, rawRgbaBytes); final inp = py < 0 || nx >= image.width ? icc - : _readPixelColor(image, byteDataRgba, nx, py); + : _readPixelColor(nx, py, image, rawRgbaBytes); final iap = ax >= image.width || py < 0 ? icc - : _readPixelColor(image, byteDataRgba, ax, py); + : _readPixelColor(ax, py, image, rawRgbaBytes); - final ip0 = cubic(dx, ipp.red, icp.red, inp.red, iap.red); - final ip1 = cubic(dx, ipp.green, icp.green, inp.green, iap.green); - final ip2 = cubic(dx, ipp.blue, icp.blue, inp.blue, iap.blue); + final ip0 = cubic(dx, ipp.$1, icp.$1, inp.$1, iap.$1); + final ip1 = cubic(dx, ipp.$2, icp.$2, inp.$2, iap.$2); + final ip2 = cubic(dx, ipp.$3, icp.$3, inp.$3, iap.$3); // final ip3 = cubic(dx, ipp.a, icp.a, inp.a, iap.a); - final ipc = px < 0 ? icc : _readPixelColor(image, byteDataRgba, px, y); + final ipc = px < 0 ? icc : _readPixelColor(px, y, image, rawRgbaBytes); final inc = - nx >= image.width ? icc : _readPixelColor(image, byteDataRgba, nx, y); + nx >= image.width ? icc : _readPixelColor(nx, y, image, rawRgbaBytes); final iac = - ax >= image.width ? icc : _readPixelColor(image, byteDataRgba, ax, y); + ax >= image.width ? icc : _readPixelColor(ax, y, image, rawRgbaBytes); - final ic0 = cubic(dx, ipc.red, icc.red, inc.red, iac.red); - final ic1 = cubic(dx, ipc.green, icc.green, inc.green, iac.green); - final ic2 = cubic(dx, ipc.blue, icc.blue, inc.blue, iac.blue); + final ic0 = cubic(dx, ipc.$1, icc.$1, inc.$1, iac.$1); + final ic1 = cubic(dx, ipc.$2, icc.$2, inc.$2, iac.$2); + final ic2 = cubic(dx, ipc.$3, icc.$3, inc.$3, iac.$3); // final ic3 = cubic(dx, ipc.a, icc.a, inc.a, iac.a); final ipn = px < 0 || ny >= image.height ? icc - : _readPixelColor(image, byteDataRgba, px, ny); + : _readPixelColor(px, ny, image, rawRgbaBytes); final icn = - ny >= image.height ? icc : _readPixelColor(image, byteDataRgba, x, ny); + ny >= image.height ? icc : _readPixelColor(x, ny, image, rawRgbaBytes); final inn = nx >= image.width || ny >= image.height ? icc - : _readPixelColor(image, byteDataRgba, nx, ny); + : _readPixelColor(nx, ny, image, rawRgbaBytes); final ian = ax >= image.width || ny >= image.height ? icc - : _readPixelColor(image, byteDataRgba, ax, ny); + : _readPixelColor(ax, ny, image, rawRgbaBytes); - final in0 = cubic(dx, ipn.red, icn.red, inn.red, ian.red); - final in1 = cubic(dx, ipn.green, icn.green, inn.green, ian.green); - final in2 = cubic(dx, ipn.blue, icn.blue, inn.blue, ian.blue); + final in0 = cubic(dx, ipn.$1, icn.$1, inn.$1, ian.$1); + final in1 = cubic(dx, ipn.$2, icn.$2, inn.$2, ian.$2); + final in2 = cubic(dx, ipn.$3, icn.$3, inn.$3, ian.$3); // final in3 = cubic(dx, ipn.a, icn.a, inn.a, ian.a); final ipa = px < 0 || ay >= image.height ? icc - : _readPixelColor(image, byteDataRgba, px, ay); + : _readPixelColor(px, ay, image, rawRgbaBytes); final ica = - ay >= image.height ? icc : _readPixelColor(image, byteDataRgba, x, ay); + ay >= image.height ? icc : _readPixelColor(x, ay, image, rawRgbaBytes); final ina = nx >= image.width || ay >= image.height ? icc - : _readPixelColor(image, byteDataRgba, nx, ay); + : _readPixelColor(nx, ay, image, rawRgbaBytes); final iaa = ax >= image.width || ay >= image.height ? icc - : _readPixelColor(image, byteDataRgba, ax, ay); + : _readPixelColor(ax, ay, image, rawRgbaBytes); - final ia0 = cubic(dx, ipa.red, ica.red, ina.red, iaa.red); - final ia1 = cubic(dx, ipa.green, ica.green, ina.green, iaa.green); - final ia2 = cubic(dx, ipa.blue, ica.blue, ina.blue, iaa.blue); + final ia0 = cubic(dx, ipa.$1, ica.$1, ina.$1, iaa.$1); + final ia1 = cubic(dx, ipa.$2, ica.$2, ina.$2, iaa.$2); + final ia2 = cubic(dx, ipa.$3, ica.$3, ina.$3, iaa.$3); // final ia3 = cubic(dx, ipa.a, ica.a, ina.a, iaa.a); final c0 = cubic(dy, ip0, ic0, in0, ia0).clamp(0, 255).toInt(); @@ -663,5 +657,5 @@ Color _getPixelBicubic(num fx, num fy, Image image, ByteData byteDataRgba) { final c2 = cubic(dy, ip2, ic2, in2, ia2).clamp(0, 255).toInt(); // final c3 = cubic(dy, ip3, ic3, in3, ia3); - return Color.fromRGBO(c0, c1, c2, 1.0); + return (c0, c1, c2); // (red, green, blue) } diff --git a/mobile/lib/utils/ml_util.dart b/mobile/lib/utils/ml_util.dart index 3ae73b3510..65275b7df4 100644 --- a/mobile/lib/utils/ml_util.dart +++ b/mobile/lib/utils/ml_util.dart @@ -396,7 +396,7 @@ Future analyzeImageStatic(Map args) async { final startTime = DateTime.now(); // Decode the image once to use for both face detection and alignment - final (image, imageByteData) = await decodeImageFromPath(imagePath); + final (image, rawRgbaBytes) = await decodeImageFromPath(imagePath); final decodedImageSize = Dimensions(height: image.height, width: image.width); final result = MLResult.fromEnteFileID(enteFileID); @@ -408,7 +408,7 @@ Future analyzeImageStatic(Map args) async { final resultFaces = await FaceRecognitionService.runFacesPipeline( enteFileID, image, - imageByteData, + rawRgbaBytes, faceDetectionAddress, faceEmbeddingAddress, ); @@ -426,7 +426,7 @@ Future analyzeImageStatic(Map args) async { final clipResult = await SemanticSearchService.runClipImage( enteFileID, image, - imageByteData, + rawRgbaBytes, clipImageAddress, ); result.clip = clipResult;