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

opticalflow works with Slice inputs instd of Image #164

Merged
merged 1 commit into from
Apr 28, 2023
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
8 changes: 3 additions & 5 deletions examples/skeletonize/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ void main() @nogc nothrow

auto skel = skeletonize2D(gray);

auto to_write = skel.asImage(ImageFormat.IF_MONO);
scope(exit) destroyFree(to_write);

imwrite(to_write, "result/skel.png");

imwrite("result/skel.png", skel.shape[1], skel.shape[0], ImageFormat.IF_MONO, BitDepth.BD_8,
skel.rcslice.ptr[0..skel.elementCount]);

auto points = skel.endsAndjunctions;
auto junctions = junctions(points, skel);

Expand Down
Binary file modified examples/tracking/hornschunck/result/2_flowColor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/tracking/hornschunck/result/3_flowWarp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 20 additions & 12 deletions examples/tracking/hornschunck/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,15 @@ void main(string[] args)
nextPath = args.length >= 3 ? args[2] : "../../data/optflow/Army/frame11.png";
}

auto current = imread(currentPath, rparams);
auto next = imread(nextPath, rparams);

auto _current = imread(currentPath, rparams);
auto _next = imread(nextPath, rparams);
scope(exit){
destroyFree(current);
destroyFree(next);
destroyFree(_current);
destroyFree(_next);
}
auto current = _current.sliced2D.as!float.rcslice;
auto next = _next.sliced2D.as!float.rcslice;

// Setup algorithm parameters.
HornSchunckProperties props = HornSchunckProperties();
Expand All @@ -102,18 +104,24 @@ void main(string[] args)

HornSchunckFlow hsFlow = mallocNew!HornSchunckFlow(props);
DensePyramidFlow densePyramid = mallocNew!DensePyramidFlow(hsFlow, pyramidLevels);

scope(exit){
destroyFree(hsFlow);
destroyFree(densePyramid);
}
auto flow = densePyramid.evaluate(current, next);
auto flow = densePyramid.evaluate(current.lightScope, next.lightScope);

auto flowColor = flow.lightScope.colorCode;
auto flowWarp = warp(current.sliced, flow.lightScope);
auto flowColor = colorCode(flow.lightScope);
auto flowWarp = warp(current.lightScope, flow.lightScope);

current.imwrite("./result/1_current.png");
current.as!ubyte.rcslice.imwrite(ImageFormat.IF_MONO,"./result/1_current.png");
flowColor.imwrite(ImageFormat.IF_RGB, "./result/2_flowColor.png");
flowWarp.imwrite(ImageFormat.IF_MONO, "./result/3_flowWarp.png");
next.imwrite("./result/4_next.png");
flowWarp.as!ubyte.rcslice.imwrite(ImageFormat.IF_MONO, "./result/3_flowWarp.png");
next.as!ubyte.rcslice.imwrite(ImageFormat.IF_MONO, "./result/4_next.png");

imshow(flowColor, "flowColor");

waitKey();

destroyFigures();
}
1 change: 0 additions & 1 deletion examples/tracking/klt/dub.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"authors": ["relja"],
"dependencies": {
"dcv:core": {"path": "../../../"},
"dcv:videoio": {"path": "../../../"},
"dcv:imageio": {"path": "../../../"},
"dcv:plot": {"path": "../../../"}
}
Expand Down
56 changes: 30 additions & 26 deletions examples/tracking/klt/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ module dcv.example.opticalflow;

import std.stdio;
import std.conv : to;
import std.algorithm : copy, map, each;
import std.algorithm : copy, map, each, min;
import std.range : lockstep, repeat;
import std.array : staticArray;
import mir.ndslice;
import mir.rc;
import mir.appender;

import dcv.core;
import dcv.imageio;
import dcv.videoio;
import dcv.imgproc.filter : filterNonMaximum;
import dcv.imgproc.color : gray2rgb;
import dcv.features.corner.harris : shiTomasiCorners;
Expand Down Expand Up @@ -50,13 +50,15 @@ Example:
enum H = 240;
enum W = 320;

// @nogc nothrow: // only pipeProcess allocates with GC

int main(string[] args)
{
auto pipes = pipeProcess(["ffmpeg", "-i", "../../data/centaur_1.mpg", "-f", "image2pipe",
"-vcodec", "rawvideo", "-pix_fmt", "rgb24", "-"],
Redirect.stdout);

Image prevFrame, thisFrame; // image frames, for tracking
Slice!(RCI!float, 2) prevFrame, thisFrame; // image frames, for tracking

auto cornerW = 15.0f; // size of the tracking kernel
auto cornerCount = 20; // numer of corners tracked
Expand Down Expand Up @@ -85,42 +87,38 @@ int main(string[] args)

// read first frame and use it to detect initial corners for tracking
const ubyte[] _dt = pipes.stdout.rawRead(buffSlice.ptr[0..H*W*3]);
prevFrame = buffSlice.asImage(ImageFormat.IF_RGB);


// take the r channel and form an image
auto _prevFrame = prevFrame.sliced[0 .. $, 0 .. $, 0].asImage(ImageFormat.IF_MONO);
destroyFree(prevFrame); prevFrame = null;
prevFrame = buffSlice[0..$, 0..$, 0].as!float.rcslice;

auto h = _prevFrame.height;
auto w = _prevFrame.width;
auto h = prevFrame.shape[0];
auto w = prevFrame.shape[1];
auto frame = 0; // frame counter

while (1)
{
auto thisSlice = uninitRCslice!ubyte(h, w, 3);
const ubyte[] dt = pipes.stdout.rawRead(thisSlice.ptr[0..h*w*3]);

writeln("Tracking frame no. " ~ frame.to!string ~ "...");
printf("Tracking frame no. %d...\n", frame);

// take the y channel, and form an image of it.
thisFrame = thisSlice[0 .. $, 0 .. $, 0].asImage(ImageFormat.IF_MONO);
thisFrame = thisSlice[0 .. $, 0 .. $, 0].as!float.rcslice;

// if corner count has dropped below 50% of original count, try to detect new points.
if (corners.length < (cornerCount / 2))
{
writeln("Search features again...");
int err;
auto c = shiTomasiCorners(_prevFrame.sliced.reshape([h, w], err).as!float.rcslice.lightScope, cast(uint)cornerW)
.filterNonMaximum.lightScope.extractCorners(cornerCount);
printf("Search features again...\n");

assert(err == 0);
auto c = shiTomasiCorners(prevFrame.lightScope, cast(uint)cornerW)
.filterNonMaximum.lightScope.extractCorners(cornerCount);

foreach (v; c)
corners.put([cast(float)v[0], cast(float)v[1]].staticArray);
}

// evaluate the optical flow
auto flow = spFlow.evaluate(_prevFrame, thisFrame, corners.data, reg);
auto flow = spFlow.evaluate(prevFrame.lightScope, thisFrame.lightScope, corners.data, reg);

// discard faulty tracked corners
float[2][] fback = mallocSlice!(float[2])(corners.length);
Expand All @@ -137,21 +135,28 @@ int main(string[] args)
}
else
{
writeln("Removing corner no. ", id, " with score: ", e);
printf("Removing corner no. %zu with score: %f\n", id, e);
}
}

// Displace previous corner coordinates with newly estimated flow vectors
foreach (ref c, f; lockstep(corners.data, flow))
// nogc lockStep workaround
auto A = corners.data.sliced;
alias B = flow;

alias ItZ = ZipIterator!(typeof(A._iterator), typeof(B._iterator));
auto zipp = ItZ(A._iterator, B._iterator);
auto mlen = min(A.length, B.length);

foreach(_; 0..mlen)
{
c[0] = c[0] + f[1];
c[1] = c[1] + f[0];
(*zipp).a[0] = (*zipp).a[0] + (*zipp).b[1];
(*zipp).a[1] = (*zipp).a[1] + (*zipp).b[0];
++zipp;
}

// draw tracked corners and write the image
int err;
auto f2c = thisFrame.sliced.reshape([h, w], err).gray2rgb;
assert(err == 0);
auto f2c = thisFrame.lightScope.gray2rgb;

// plot tracked points on screen.
figureKLT.draw(f2c, ImageFormat.IF_RGB);
Expand All @@ -165,8 +170,7 @@ int main(string[] args)
break;

// take this frame as next one's previous
destroyFree(_prevFrame);
_prevFrame = thisFrame;
prevFrame = thisFrame;

if (!figureKLT.visible)
break;
Expand Down
29 changes: 10 additions & 19 deletions source/dcv/core/image.d
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public:
assert(depth != BitDepth.BD_UNASSIGNED && format != ImageFormat.IF_UNASSIGNED);
if (data !is null)
{
assert(data.length == width * height * imageFormatChannelCount[cast(size_t)format] * (cast(size_t)depth / 8));
//assert(data.length == width * height * imageFormatChannelCount[cast(size_t)format] * (cast(size_t)depth / 8));
}
}
do
Expand Down Expand Up @@ -277,21 +277,16 @@ public:
}
*/

auto sliced()
Slice!(ubyte*, 3) sliced()
{
assert(channels > 1);
return data.sliced(height, width, channels);
}

auto rcsliced() inout
Slice!(ubyte*, 2) sliced2D()
{
import mir.ndslice.allocation : rcslice;
import mir.rc;

import core.lifetime: move;

Slice!(RCI!ubyte, 3LU, Contiguous) ret = uninitRCslice!ubyte(height, width, channels);
ret[] = data;
return ret.move;
assert(channels == 1);
return data.sliced(height, width);
}
}

Expand Down Expand Up @@ -347,14 +342,10 @@ Image asImage(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slic
}

auto ret = mallocNew!Image(slice.shape[1], slice.shape[0], format, depth);
//static if(is(T==ubyte)){
// ret.data[] = slice[];
//}else{
foreach (i; 0..slice.elementCount) {
T ta = slice.accessFlat(i);
ret.data[i..i+T.sizeof] = (cast(ubyte*)&slice.accessFlat(i))[0..T.sizeof];
}
//}

foreach (i; 0..slice.elementCount) {
ret.data[i..i+T.sizeof] = (cast(ubyte*)&slice.accessFlat(i))[0..T.sizeof];
}

return ret;
}
Expand Down
14 changes: 14 additions & 0 deletions source/dcv/core/utils.d
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ package(dcv) @nogc pure nothrow
}
}

import dplug.core;

static ThreadPool pool;

static this() @nogc nothrow {
if(pool is null)
pool = mallocNew!ThreadPool;
}

static ~this() @nogc nothrow {
if(pool !is null)
destroyFree(pool);
}

/**
Clip value by it's value range.
Params:
Expand Down
24 changes: 8 additions & 16 deletions source/dcv/features/corner/harris.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ License: $(LINK3 http://www.boost.org/LICENSE_1_0.txt, Boost Software License -
*/
module dcv.features.corner.harris;

import std.parallelism : parallel, taskPool, TaskPool;
import std.experimental.allocator.gc_allocator;

import mir.math.common : fastmath;
Expand All @@ -19,12 +18,13 @@ import mir.rc;
import mir.algorithm.iteration : each;
import mir.ndslice.allocation;

import dcv.core.utils : emptyRCSlice;
import dcv.core.utils : emptyRCSlice, pool;
import dcv.imgproc.filter : calcPartialDerivatives;

import dplug.core;

@nogc nothrow:

/**
Calculate per-pixel corner impuls response using Harris corner detector.

Expand All @@ -34,7 +34,6 @@ Params:
k = Sensitivity parameter defined in the algorithm.
gauss = Gauss sigma value used as window weighting parameter.
prealloc = Optional pre-allocated buffer for return response image.
pool = TaskPool instance used parallelise the algorithm.

Returns:
Response matrix the same size of the input image, where each pixel represents
Expand All @@ -58,16 +57,14 @@ in
}
do
{
ThreadPool pool = mallocNew!ThreadPool;
scope(exit) destroyFree(pool);

if (prealloc.shape != image.shape)
{
prealloc = uninitRCslice!T(image.shape);//uninitializedSlice!T(image.shape);
}
HarrisDetector detector;
detector.k = k;
return calcCorners(image, winSize, gauss, prealloc, detector, pool);
return calcCorners(image, winSize, gauss, prealloc, detector);
}

/**
Expand All @@ -78,7 +75,6 @@ Params:
winSize = Window (square) size used in corner detection.
gauss = Gauss sigma value used as window weighting parameter.
prealloc = Optional pre-allocated buffer for return response image.
pool = TaskPool instance used parallelise the algorithm.

Returns:
Response matrix the same size of the input image, where each pixel represents
Expand All @@ -100,16 +96,13 @@ in
}
do
{
ThreadPool pool = mallocNew!ThreadPool;
scope(exit) destroyFree(pool);

if (prealloc.empty)
{
prealloc = uninitRCslice!T(image.shape); //uninitializedSlice!T(image.shape);
}

ShiTomasiDetector detector;
return calcCorners(image, winSize, gauss, prealloc, detector, pool);
return calcCorners(image, winSize, gauss, prealloc, detector);
}

unittest
Expand Down Expand Up @@ -209,7 +202,7 @@ struct ShiTomasiDetector

Slice!(RCI!OutputType, 2LU, SliceKind.contiguous) calcCorners(Detector, InputType, SliceKind inputKind, OutputType)
(Slice!(InputType*, 2LU, inputKind) image, uint winSize, float gaussSigma,
Slice!(RCI!OutputType, 2LU, SliceKind.contiguous) prealloc, Detector detector, ThreadPool pool)
Slice!(RCI!OutputType, 2LU, SliceKind.contiguous) prealloc, Detector detector)
{
import mir.ndslice.topology : zip, iota;

Expand All @@ -218,14 +211,13 @@ Slice!(RCI!OutputType, 2LU, SliceKind.contiguous) calcCorners(Detector, InputTyp
Slice!(RCI!InputType, 2, SliceKind.contiguous) fx, fy;
calcPartialDerivatives(image, fx, fy);

auto windowPack = zip(prealloc, fx, fy).windows(winSize, winSize);
auto windowPack = zip(prealloc, fx.lightScope, fy.lightScope).windows(winSize, winSize);

auto iterable = windowPack;
void worker(int i, int threadIndex) nothrow @nogc {
auto windowRow = iterable[i];
auto windowRow = windowPack[i];
windowRow.each!(win => calcCornersImpl(win, detector));
}
pool.parallelFor(cast(int)iterable.length, &worker);
pool.parallelFor(cast(int)windowPack.length, &worker);

return prealloc;
}
Loading