[mob] Two varients of CroppedFaceImageView for testing out which is more performant
This commit is contained in:
parent
f173bc4038
commit
7617817798
|
@ -267,7 +267,7 @@ class _FaceWidgetState extends State<FaceWidget> {
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
child: CroppedFaceImageView(
|
child: CroppedFaceImgImageView(
|
||||||
enteFile: widget.file,
|
enteFile: widget.file,
|
||||||
face: widget.face,
|
face: widget.face,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import "dart:io" show File;
|
import "dart:io" show File;
|
||||||
|
import "dart:typed_data";
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import "package:computer/computer.dart";
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import "package:flutter/widgets.dart";
|
import "package:flutter/widgets.dart";
|
||||||
import "package:flutter_image_compress/flutter_image_compress.dart";
|
|
||||||
import "package:image/image.dart" as img;
|
|
||||||
import "package:logging/logging.dart";
|
import "package:logging/logging.dart";
|
||||||
import "package:photos/face/model/face.dart";
|
import "package:photos/face/model/face.dart";
|
||||||
import "package:photos/models/file/file.dart";
|
import "package:photos/models/file/file.dart";
|
||||||
import "package:photos/ui/viewer/file/thumbnail_widget.dart";
|
import "package:photos/ui/viewer/file/thumbnail_widget.dart";
|
||||||
|
import "package:photos/utils/face/face_util.dart";
|
||||||
import "package:photos/utils/file_util.dart";
|
import "package:photos/utils/file_util.dart";
|
||||||
import "package:photos/utils/image_util.dart";
|
import "package:photos/utils/image_util.dart";
|
||||||
|
|
||||||
|
@ -27,23 +26,22 @@ class CroppedFaceInfo {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class CroppedFaceImageView extends StatefulWidget {
|
class CroppedFaceImgImageView extends StatefulWidget {
|
||||||
final EnteFile enteFile;
|
final EnteFile enteFile;
|
||||||
final Face face;
|
final Face face;
|
||||||
|
|
||||||
const CroppedFaceImageView({
|
const CroppedFaceImgImageView({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.enteFile,
|
required this.enteFile,
|
||||||
required this.face,
|
required this.face,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CroppedFaceImageViewState createState() => CroppedFaceImageViewState();
|
CroppedFaceImgImageViewState createState() => CroppedFaceImgImageViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class CroppedFaceImageViewState extends State<CroppedFaceImageView> {
|
class CroppedFaceImgImageViewState extends State<CroppedFaceImgImageView> {
|
||||||
ui.Image? _image;
|
ui.Image? _image;
|
||||||
final _computer = Computer.shared();
|
|
||||||
final _logger = Logger("CroppedFaceImageView");
|
final _logger = Logger("CroppedFaceImageView");
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -89,39 +87,9 @@ class CroppedFaceImageViewState extends State<CroppedFaceImageView> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.Image? image = await _computer
|
final image = await generateImgFaceThumbnails(ioFile.path, [faceBox]);
|
||||||
.compute(decodeImage, param: {"filePath": ioFile.path});
|
|
||||||
|
|
||||||
if (image == null) {
|
return convertImageToFlutterUi(image.first);
|
||||||
_logger.info(
|
|
||||||
"Failed to decode image ${widget.enteFile.title}. Compressing to jpg and decoding",
|
|
||||||
);
|
|
||||||
final compressedJPGImage =
|
|
||||||
await FlutterImageCompress.compressWithFile(ioFile.path);
|
|
||||||
image = await _computer.compute(
|
|
||||||
decodeJPGImage,
|
|
||||||
param: {"image": compressedJPGImage},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (image == null) {
|
|
||||||
throw Exception("Failed to decode image");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
|
||||||
final croppedImage = img.copyCrop(
|
|
||||||
image,
|
|
||||||
x: (image.width * faceBox.xMin).round(),
|
|
||||||
y: (image.height * faceBox.yMin).round(),
|
|
||||||
width: (image.width * faceBox.width).round(),
|
|
||||||
height: (image.height * faceBox.height).round(),
|
|
||||||
antialias: false,
|
|
||||||
);
|
|
||||||
_logger.info(
|
|
||||||
"Image crop took ${stopwatch.elapsedMilliseconds}ms ----------------",
|
|
||||||
);
|
|
||||||
stopwatch.stop();
|
|
||||||
return convertImageToFlutterUi(croppedImage);
|
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
_logger.severe("Error getting image", e, s);
|
_logger.severe("Error getting image", e, s);
|
||||||
return null;
|
return null;
|
||||||
|
@ -129,10 +97,72 @@ class CroppedFaceImageViewState extends State<CroppedFaceImageView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<img.Image?> decodeImage(Map args) async {
|
class CroppedFaceJpgImageView extends StatefulWidget {
|
||||||
return await img.decodeImageFile(args["filePath"]);
|
final EnteFile enteFile;
|
||||||
|
final Face face;
|
||||||
|
|
||||||
|
const CroppedFaceJpgImageView({
|
||||||
|
Key? key,
|
||||||
|
required this.enteFile,
|
||||||
|
required this.face,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
CroppedFaceJpgImageViewState createState() => CroppedFaceJpgImageViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
img.Image? decodeJPGImage(Map args) {
|
class CroppedFaceJpgImageViewState extends State<CroppedFaceJpgImageView> {
|
||||||
return img.decodeJpg(args["image"])!;
|
Uint8List? _image;
|
||||||
|
final _logger = Logger("CroppedFaceImageView");
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadImage() async {
|
||||||
|
final image = await getImage();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_image = image;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _image != null
|
||||||
|
? LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return Image.memory(
|
||||||
|
_image!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: ThumbnailWidget(widget.enteFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> getImage() async {
|
||||||
|
try {
|
||||||
|
final faceBox = widget.face.detection.box;
|
||||||
|
|
||||||
|
final File? ioFile = await getFile(widget.enteFile);
|
||||||
|
if (ioFile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final image = await generateJpgFaceThumbnails(ioFile.path, [faceBox]);
|
||||||
|
|
||||||
|
return image.first;
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe("Error getting image", e, s);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ class PersonFaceWidget extends StatelessWidget {
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
CroppedFaceImageView(enteFile: file, face: face),
|
CroppedFaceImgImageView(enteFile: file, face: face),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue