Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LUT for cv_8u cv_8s cv_16u and cv_16s #57

Merged
merged 3 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/src/core/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,33 @@ Mat log(InputArray src, {OutputArray? dst}) {
return dst;
}

/// Performs a look-up table transform of an array. Support CV_8U, CV_8S, CV_16U, CV_16S
///
/// The function LUT fills the output array with values from the look-up table. Indices of the entries
/// are taken from the input array. That is, the function processes each element of src as follows:
///
/// $\texttt{dst} (I) \leftarrow \texttt{lut(src(I) + d)}$
///
/// where
///
/// $d = \fork{0}{if \(\texttt{src}\) has depth \(\texttt{CV_8U}\)}{128}{if \(\texttt{src}\) has depth \(\texttt{CV_8S}\)}$
///
/// [src] input array of 8-bit elements.
/// [lut] look-up table of 256 elements; in case of multi-channel input array, the table should
/// either have a single channel (in this case the same table is used for all channels) or the same
/// number of channels as in the input array.
///
/// [dst] output array of the same size and number of channels as src, and the same depth as lut.
///
/// see also: [convertScaleAbs]
///
/// https://docs.opencv.org/4.x/d2/de8/group__core__array.html#gab55b8d062b7f5587720ede032d34156f
Mat LUT(InputArray src, InputArray lut, {OutputArray? dst}) {
dst ??= Mat.empty();
cvRun(() => CFFI.LUT(src.ref, lut.ref, dst!.ref));
return dst;
}

/// Magnitude calculates the magnitude of 2D vectors.
///
/// For further details, please see:
Expand Down
76 changes: 75 additions & 1 deletion src/core/core.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "core.h"
#include "lut.hpp"
#include <iostream>
#include <vector>

Expand Down Expand Up @@ -1694,12 +1695,85 @@ CvStatus Mat_colRange(Mat m, int start, int end, Mat *rval)
*rval = {new cv::Mat(m.ptr->colRange(start, end))};
END_WRAP
}

// https://docs.opencv.org/4.x/db/da5/tutorial_how_to_scan_images.html#:~:text=Goal
CvStatus LUT(Mat src, Mat lut, Mat dst)
{
BEGIN_WRAP
cv::LUT(*src.ptr, *lut.ptr, *dst.ptr);
auto cn = src.ptr->channels(), depth = src.ptr->depth();
if (depth == CV_8U || depth == CV_8S) {
cv::LUT(*src.ptr, *lut.ptr, *dst.ptr);
} else {
int lutcn = lut.ptr->channels(), lut_depth = lut.ptr->depth();
size_t lut_total = lut.ptr->total(), expect_total = 0;
switch (depth) {
case CV_8U:
case CV_8S:
expect_total = 256;
case CV_16U:
case CV_16S:
expect_total = 65536;
break;
// TODO: can't create a mat with 4294967296 rows, maybe use vector instead
// case CV_32S:
// expect_total = 4294967296;
// break;
default:
throw cv::Exception(cv::Error::StsNotImplemented, "source Mat Type not supported", __func__, __FILE__,
__LINE__);
}

CV_Assert((lutcn == cn || lutcn == 1) && lut_total == expect_total && lut.ptr->isContinuous());
dst.ptr->create(src.ptr->dims, src.ptr->size, CV_MAKETYPE(lut.ptr->depth(), cn));

const cv::Mat *arrays[] = {src.ptr, dst.ptr, 0};
uchar *ptrs[2] = {};
cv::NAryMatIterator it(arrays, ptrs);
int len = (int)it.size;
if (depth == CV_16U && lut_depth == CV_8U)
cvd::LUT16u_8u(src.ptr->ptr<ushort>(), lut.ptr->ptr<uchar>(), dst.ptr->ptr<uchar>(), len, cn, lutcn);
else if (depth == CV_16U && lut_depth == CV_8S)
cvd::LUT16u_8s(src.ptr->ptr<ushort>(), lut.ptr->ptr<char>(), dst.ptr->ptr<char>(), len, cn, lutcn);
else if (depth == CV_16U && lut_depth == CV_16U)
cvd::LUT16u_16u(src.ptr->ptr<ushort>(), lut.ptr->ptr<ushort>(), dst.ptr->ptr<ushort>(), len, cn, lutcn);
else if (depth == CV_16U && lut_depth == CV_16S)
cvd::LUT16u_16s(src.ptr->ptr<ushort>(), lut.ptr->ptr<short>(), dst.ptr->ptr<short>(), len, cn, lutcn);
else if (depth == CV_16U && lut_depth == CV_32S)
cvd::LUT16u_32s(src.ptr->ptr<ushort>(), lut.ptr->ptr<int>(), dst.ptr->ptr<int>(), len, cn, lutcn);
else if (depth == CV_16U && lut_depth == CV_32F)
cvd::LUT16u_32f(src.ptr->ptr<ushort>(), lut.ptr->ptr<float>(), dst.ptr->ptr<float>(), len, cn, lutcn);
else if (depth == CV_16U && lut_depth == CV_64F)
cvd::LUT16u_64f(src.ptr->ptr<ushort>(), lut.ptr->ptr<double>(), dst.ptr->ptr<double>(), len, cn, lutcn);
// 16s
else if (depth == CV_16S && lut_depth == CV_8U)
cvd::LUT16s_8u(src.ptr->ptr<short>(), lut.ptr->ptr<uchar>(), dst.ptr->ptr<uchar>(), len, cn, lutcn);
else if (depth == CV_16S && lut_depth == CV_8S)
cvd::LUT16s_8s(src.ptr->ptr<short>(), lut.ptr->ptr<char>(), dst.ptr->ptr<char>(), len, cn, lutcn);
else if (depth == CV_16S && lut_depth == CV_16U)
cvd::LUT16s_16u(src.ptr->ptr<short>(), lut.ptr->ptr<ushort>(), dst.ptr->ptr<ushort>(), len, cn, lutcn);
else if (depth == CV_16S && lut_depth == CV_16S)
cvd::LUT16s_16s(src.ptr->ptr<short>(), lut.ptr->ptr<short>(), dst.ptr->ptr<short>(), len, cn, lutcn);
else if (depth == CV_16S && lut_depth == CV_32S)
cvd::LUT16s_32s(src.ptr->ptr<short>(), lut.ptr->ptr<int>(), dst.ptr->ptr<int>(), len, cn, lutcn);
else if (depth == CV_16S && lut_depth == CV_32F)
cvd::LUT16s_32f(src.ptr->ptr<short>(), lut.ptr->ptr<float>(), dst.ptr->ptr<float>(), len, cn, lutcn);
else if (depth == CV_16S && lut_depth == CV_64F)
cvd::LUT16s_64f(src.ptr->ptr<short>(), lut.ptr->ptr<double>(), dst.ptr->ptr<double>(), len, cn, lutcn);
// 32s
else if (depth == CV_32S && lut_depth == CV_32S)
cvd::LUT32s_32s(src.ptr->ptr<int>(), lut.ptr->ptr<int>(), dst.ptr->ptr<int>(), len, cn, lutcn);
else if (depth == CV_32S && lut_depth == CV_32F)
cvd::LUT32s_32f(src.ptr->ptr<int>(), lut.ptr->ptr<float>(), dst.ptr->ptr<float>(), len, cn, lutcn);
else if (depth == CV_32S && lut_depth == CV_64F)
cvd::LUT32s_64f(src.ptr->ptr<int>(), lut.ptr->ptr<double>(), dst.ptr->ptr<double>(), len, cn, lutcn);
else
throw cv::Exception(cv::Error::StsNotImplemented,
"Unsupported combination of source and destination types", __func__, __FILE__,
__LINE__);
}
END_WRAP
}

CvStatus KMeans(Mat data, int k, Mat bestLabels, TermCriteria criteria, int attempts, int flags, Mat centers,
double *rval)
{
Expand Down
92 changes: 92 additions & 0 deletions src/core/lut.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
Created by Rainyl.
Licensed: Apache 2.0 license. Copyright (c) 2024 Rainyl.
*/

#include <opencv2/opencv.hpp>

namespace cvd
{
#define LUT_BODY \
if (lutcn == 1) { \
for (int i = 0; i < len * cn; i++) \
dst[i] = lut[src[i]]; \
} else { \
for (int i = 0; i < len * cn; i += cn) \
for (int k = 0; k < cn; k++) \
dst[i + k] = lut[src[i + k] * cn + k]; \
}

// 16u
static void LUT16u_8u(const ushort *src, const uchar *lut, uchar *dst, int len, int cn, int lutcn)
{
LUT_BODY
}
static void LUT16u_8s(const ushort *src, const char *lut, char *dst, int len, int cn, int lutcn)
{
LUT_BODY
}
static void LUT16u_16u(const ushort *src, const ushort *lut, ushort *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

static void LUT16u_16s(const ushort *src, const short *lut, short *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

static void LUT16u_32s(const ushort *src, const int *lut, int *dst, int len, int cn, int lutcn) { LUT_BODY }

static void LUT16u_32f(const ushort *src, const float *lut, float *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

static void LUT16u_64f(const ushort *src, const double *lut, double *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

// 16s
static void LUT16s_8u(const short *src, const uchar *lut, uchar *dst, int len, int cn, int lutcn)
{
LUT_BODY
}
static void LUT16s_8s(const short *src, const char *lut, char *dst, int len, int cn, int lutcn)
{
LUT_BODY
}
static void LUT16s_16u(const short *src, const ushort *lut, ushort *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

static void LUT16s_16s(const short *src, const short *lut, short *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

static void LUT16s_32s(const short *src, const int *lut, int *dst, int len, int cn, int lutcn) { LUT_BODY }

static void LUT16s_32f(const short *src, const float *lut, float *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

static void LUT16s_64f(const short *src, const double *lut, double *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

// 32s
static void LUT32s_32s(const int *src, const int *lut, int *dst, int len, int cn, int lutcn) { LUT_BODY }

static void LUT32s_32f(const int *src, const float *lut, float *dst, int len, int cn, int lutcn) { LUT_BODY }

static void LUT32s_64f(const int *src, const double *lut, double *dst, int len, int cn, int lutcn)
{
LUT_BODY
}

} // namespace cvd
41 changes: 41 additions & 0 deletions test/core/core_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,47 @@ void main() async {
expect(dst.isEmpty, equals(false));
});

test('cv.LUT', () {
void testOneLUT(cv.Mat src, cv.Mat lut) {
expect(lut.channels == src.channels || lut.channels == 1, true);
expect(lut.isContinus, true);
final sw = Stopwatch();
sw.start();
final dst = cv.LUT(src, lut);
sw.stop();
// print('${src.type} -> ${lut.type}(${src.rows}x${src.cols}): ${sw.elapsedMilliseconds}ms');
expect(dst.isEmpty, false);
expect(src.shape, dst.shape);
}

final depthSrc = [cv.MatType.CV_8U, cv.MatType.CV_8S, cv.MatType.CV_16U, cv.MatType.CV_16S];
final depthLut = [
cv.MatType.CV_8U,
cv.MatType.CV_8S,
cv.MatType.CV_16U,
cv.MatType.CV_16S,
cv.MatType.CV_32S,
cv.MatType.CV_32F,
cv.MatType.CV_64F,
];
for (int channel in [1, 2, 3, 4]) {
for (var depth in depthSrc) {
final srcType = cv.MatType.makeType(depth, channel);
final src = cv.Mat.zeros(3, 3, srcType);
final lutSize = switch (depth) {
cv.MatType.CV_8U || cv.MatType.CV_8S => 256,
cv.MatType.CV_16U || cv.MatType.CV_16S => 65536,
_ => throw Exception("Unsupported type"),
};
for (var lutDepth in depthLut) {
final lutType = cv.MatType.makeType(lutDepth, channel);
final lut = cv.Mat.fromScalar(1, lutSize, lutType, cv.Scalar(255, 241, 21, 0));
testOneLUT(src, lut);
}
}
}
});

test('cv.magnitude', () {
final src1 = cv.Mat.randu(4, 4, cv.MatType.CV_32FC1);
final src2 = cv.Mat.randu(4, 4, cv.MatType.CV_32FC1);
Expand Down