Skip to content

Commit

Permalink
add Vec version of imencode imdecode, add haveImageReader haveImageWr…
Browse files Browse the repository at this point in the history
…iter imcount
  • Loading branch information
rainyl committed Dec 5, 2024
1 parent 4983c38 commit 5b65f4b
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 6 deletions.
99 changes: 93 additions & 6 deletions packages/dartcv/lib/src/imgcodecs/imgcodecs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@ import '../g/constants.g.dart';
import '../g/imgcodecs.g.dart' as cvg;
import '../native_lib.dart' show cimgcodecs;

/// Returns true if the specified image can be decoded by OpenCV.
///
/// https://docs.opencv.org/4.10.0/d4/da8/group__imgcodecs.html#ga65d3569d8845d1210e1aeab8c199031c
bool haveImageReader(String filename) {
final cname = filename.toNativeUtf8().cast<ffi.Char>();
final rval = cimgcodecs.cv_haveImageReader(cname);
calloc.free(cname);
return rval;
}

/// Returns true if an image with the specified filename can be encoded by OpenCV.
///
/// https://docs.opencv.org/4.10.0/d4/da8/group__imgcodecs.html#gac4fa9c4c32b58c55059752c0490d3f20
bool haveImageWriter(String filename) {
final cname = filename.toNativeUtf8().cast<ffi.Char>();
final rval = cimgcodecs.cv_haveImageWriter(cname);
calloc.free(cname);
return rval;
}

/// Returns the number of images inside the give file.
///
/// The function imcount will return the number of pages in a multi-page image, or 1 for single-page images
///
/// https://docs.opencv.org/4.10.0/d4/da8/group__imgcodecs.html#ga02237b2aad2d4ae41c9489a83781f202
int imcount(String filename, {int flags = IMREAD_ANYCOLOR}) {
final cname = filename.toNativeUtf8().cast<ffi.Char>();
final rval = cimgcodecs.cv_imcount(cname, flags);
calloc.free(cname);
return rval;
}

/// read an image from a file into a Mat.
/// The flags param is one of the IMReadFlag flags.
/// If the image cannot be read (because of missing file, improper permissions,
Expand All @@ -32,6 +64,7 @@ Mat imread(String filename, {int flags = IMREAD_COLOR}) {
return dst;
}

/// async version of [imread]
Future<Mat> imreadAsync(String filename, {int flags = IMREAD_COLOR}) async {
final dst = Mat.empty();
final cname = filename.toNativeUtf8().cast<ffi.Char>();
Expand Down Expand Up @@ -61,6 +94,7 @@ bool imwrite(String filename, InputArray img, {VecI32? params}) {
return rval;
}

/// async version of [imwrite]
Future<bool> imwriteAsync(String filename, InputArray img, {VecI32? params}) async {
final fname = filename.toNativeUtf8().cast<ffi.Char>();
final p = calloc<ffi.Bool>();
Expand All @@ -82,7 +116,7 @@ Future<bool> imwriteAsync(String filename, InputArray img, {VecI32? params}) asy
);
}

/// IMEncode encodes an image Mat into a memory buffer.
/// imencode encodes an image Mat into a memory buffer.
/// This function compresses the image and stores it in the returned memory buffer,
/// using the image format passed in in the form of a file extension string.
///
Expand All @@ -92,6 +126,18 @@ Future<bool> imwriteAsync(String filename, InputArray img, {VecI32? params}) asy
String ext,
InputArray img, {
VecI32? params,
}) {
final (success, vec) = imencodeVec(ext, img, params: params);
final u8List = vec.toU8List(); // will copy data
vec.dispose();
return (success, u8List);
}

/// Same as [imencode] but returns [VecUChar]
(bool, VecUChar) imencodeVec(
String ext,
InputArray img, {
VecI32? params,
}) {
final buffer = calloc<cvg.VecUChar>();
final pSuccess = calloc<ffi.Bool>();
Expand All @@ -105,11 +151,9 @@ Future<bool> imwriteAsync(String filename, InputArray img, {VecI32? params}) asy
calloc.free(pSuccess);

final vec = VecUChar.fromPointer(buffer);
final u8List = vec.toU8List(); // will copy data
vec.dispose();
return (success, u8List);
return (success, vec);
}

/// async version of [imencode]
Future<(bool, Uint8List)> imencodeAsync(
String ext,
InputArray img, {
Expand Down Expand Up @@ -142,23 +186,66 @@ Future<(bool, Uint8List)> imencodeAsync(
);
}

/// Same as [imencodeAsync] but returns [VecUChar]
Future<(bool, VecUChar)> imencodeVecAsync(
String ext,
InputArray img, {
VecI32? params,
}) async {
final buffer = calloc<cvg.VecUChar>();
final pSuccess = calloc<ffi.Bool>();
final cExt = ext.toNativeUtf8().cast<ffi.Char>();

void completeFunc(Completer<(bool, VecUChar)> c) {
final success = pSuccess.value;
calloc.free(cExt);
calloc.free(pSuccess);

final vec = VecUChar.fromPointer(buffer);
return c.complete((success, vec));
}

if (params == null) {
return cvRunAsync0(
(callback) => cimgcodecs.cv_imencode(cExt, img.ref, pSuccess, buffer, callback),
completeFunc,
);
}
return cvRunAsync0(
(callback) => cimgcodecs.cv_imencode_1(cExt, img.ref, params.ref, pSuccess, buffer, callback),
completeFunc,
);
}

/// imdecode reads an image from a buffer in memory.
/// The function imdecode reads an image from the specified buffer in memory.
/// If the buffer is too short or contains invalid data, the function
/// returns an empty matrix.
/// @param buf Input array or vector of bytes.
/// @param flags The same flags as in cv::imread, see cv::ImreadModes.
///
/// For further details, please see:
/// https://docs.opencv.org/master/d4/da8/group__imgcodecs.html#ga26a67788faa58ade337f8d28ba0eb19e
Mat imdecode(Uint8List buf, int flags, {Mat? dst}) {
final vec = VecUChar.fromList(buf);
return imdecodeVec(vec, flags, dst: dst);
}

/// Same as [imdecode] but accepts [VecUChar]
Mat imdecodeVec(VecUChar buf, int flags, {Mat? dst}) {
dst ??= Mat.empty();
cvRun(() => cimgcodecs.cv_imdecode(vec.ref, flags, dst!.ptr, ffi.nullptr));
cvRun(() => cimgcodecs.cv_imdecode(buf.ref, flags, dst!.ptr, ffi.nullptr));
return dst;
}

/// async version of [imdecode]
Future<Mat> imdecodeAsync(Uint8List buf, int flags, {Mat? dst}) async {
final vec = VecUChar.fromList(buf);
return imdecodeVecAsync(vec, flags, dst: dst);
}

/// Same as [imdecodeAsync] but accepts [VecUChar]
Future<Mat> imdecodeVecAsync(VecUChar vec, int flags, {Mat? dst}) async {
dst ??= Mat.empty();
return cvRunAsync0(
(callback) => cimgcodecs.cv_imdecode(vec.ref, flags, dst!.ptr, callback),
Expand Down
Binary file added packages/dartcv/test/images/avif_test.avif
Binary file not shown.
53 changes: 53 additions & 0 deletions packages/dartcv/test/imgcodecs_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ import 'package:dartcv4/dartcv.dart' as cv;
import 'package:test/test.dart';

void main() async {
test('cv.haveImageReader, cv.haveImageWriter', () {
expect(cv.haveImageReader("test/images/circles.jpg"), true);
expect(cv.haveImageReader("test/images/avif_test.avif"), false);
expect(cv.haveImageWriter("test/images/circles.jpg"), true);
});

test('cv.imcount', () {
expect(cv.imcount("test/images/circles.jpg"), 1);
});

test("cv2.imread, cv2.imwrite", () async {
{
final cvImage = cv.imread("test/images/circles.jpg", flags: cv.IMREAD_COLOR);
Expand Down Expand Up @@ -45,6 +55,30 @@ void main() async {
expect(dst.isEmpty, false);
expect((dst.rows, dst.cols, dst.channels), (cvImage.rows, cvImage.cols, cvImage.channels));
});

test("cv2.imencodeVec, cv2.imdecodeVec", () async {
final cvImage = cv.imread("test/images/circles.jpg", flags: cv.IMREAD_COLOR);
expect((cvImage.width, cvImage.height), (512, 512));
final (success, buf) = cv.imencodeVec(".png", cvImage);
expect(success, true);
expect(buf.length, greaterThan(0));
await File("test/images_out/test_imencode.png").writeAsBytes(buf.data);
final params = [cv.IMWRITE_PNG_COMPRESSION, 9].i32;
final (success1, buf1) = cv.imencodeVec(".png", cvImage, params: params);
expect(success1, true);
expect(buf1.length, greaterThan(0));

final cvimgDecode = cv.imdecodeVec(buf, cv.IMREAD_COLOR);
expect(cvimgDecode.height, equals(cvImage.height));
expect(cvimgDecode.width, equals(cvImage.width));
expect(cvimgDecode.channels, equals(cvImage.channels));

final dst = cv.Mat.empty();
cv.imdecodeVec(buf, cv.IMREAD_COLOR, dst: dst);
expect(dst.isEmpty, false);
expect((dst.rows, dst.cols, dst.channels), (cvImage.rows, cvImage.cols, cvImage.channels));
});

test("cv2.imencodeAsync, cv2.imdecodeAsync", () async {
final cvImage = await cv.imreadAsync("test/images/circles.jpg", flags: cv.IMREAD_COLOR);
expect((cvImage.width, cvImage.height), (512, 512));
Expand All @@ -63,4 +97,23 @@ void main() async {
expect(cvimgDecode.width, equals(cvImage.width));
expect(cvimgDecode.channels, equals(cvImage.channels));
});

test("cv2.imencodeVecAsync, cv2.imdecodeVecAsync", () async {
final cvImage = await cv.imreadAsync("test/images/circles.jpg", flags: cv.IMREAD_COLOR);
expect((cvImage.width, cvImage.height), (512, 512));
final (success, buf) = await cv.imencodeVecAsync(".png", cvImage);
expect(success, true);
expect(buf.length, greaterThan(0));

await File("test/images_out/test_imencode.png").writeAsBytes(buf.data);
final params = [cv.IMWRITE_PNG_COMPRESSION, 9].i32;
final (success1, buf1) = await cv.imencodeVecAsync(".png", cvImage, params: params);
expect(success1, true);
expect(buf1.length, greaterThan(0));

final cvimgDecode = await cv.imdecodeVecAsync(buf, cv.IMREAD_COLOR);
expect(cvimgDecode.height, equals(cvImage.height));
expect(cvimgDecode.width, equals(cvImage.width));
expect(cvimgDecode.channels, equals(cvImage.channels));
});
}

0 comments on commit 5b65f4b

Please sign in to comment.