Skip to content
This repository was archived by the owner on Jul 1, 2024. It is now read-only.

Commit 652238e

Browse files
Merge pull request #3 from legraphista/feature/buffer-to-detection
Add: support for in memory image stream to darknet detector
2 parents 33c34ad + ab8e122 commit 652238e

File tree

3 files changed

+416
-66
lines changed

3 files changed

+416
-66
lines changed

darknet.d.ts

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// <reference types="node" />
12
export declare class Darknet {
23
darknet: any;
34
meta: any;
@@ -8,28 +9,49 @@ export declare class Darknet {
89
* @param config
910
*/
1011
constructor(config: IDarknetConfig);
11-
private getArrayFromBuffer(buffer, length, type);
12-
private bufferToDetections(buffer, length);
13-
private _detectSync(net, meta, image, thresh?, hier_thresh?, nms?);
12+
private getArrayFromBuffer;
13+
private bufferToDetections;
14+
private _detectSync;
15+
private _detectAsync;
1416
/**
1517
* Synchronously detect objects in an image.
1618
* @param image the destination of the image to be detected
1719
* @param config optional configuration (threshold, etc.)
1820
*/
19-
detect(image: string, config?: IConfig): Detection[];
21+
detect(image: string | IBufferImage, config?: IConfig): Detection[];
22+
getImageFromPath(path: string): any;
23+
getImageFromPathAsync(path: String): Promise<{}>;
24+
imageToRGBBuffer(image: any): Buffer;
25+
private rgbToDarknet;
26+
RGBBufferToImage(buffer: Buffer, w: number, h: number, c: number): any;
27+
/**
28+
* Transform an RGB buffer to a darknet encoded image
29+
* @param buffer - rgb buffer
30+
* @param w - width
31+
* @param h - height
32+
* @param c - channels
33+
* @returns Promise<IMAGE>
34+
*/
35+
RGBBufferToImageAsync(buffer: Buffer, w: number, h: number, c: number): Promise<any>;
2036
/**
2137
* Asynchronously detect objects in an image.
2238
* @param image
2339
* @param config
2440
* @returns A promise
2541
*/
26-
detectAsync(image: string, config?: IConfig): Promise<Detection[]>;
42+
detectAsync(image: string | IBufferImage, config?: IConfig): Promise<Detection[]>;
2743
}
2844
export interface IConfig {
2945
thresh?: number;
3046
hier_thresh?: number;
3147
nms?: number;
3248
}
49+
export interface IBufferImage {
50+
b: Buffer;
51+
w: number;
52+
h: number;
53+
c: number;
54+
}
3355
export declare type IClasses = string[];
3456
export interface IDarknetConfig {
3557
weights: string;

darknet.js

+185-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,39 @@
11
"use strict";
2+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3+
return new (P || (P = Promise))(function (resolve, reject) {
4+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6+
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7+
step((generator = generator.apply(thisArg, _arguments || [])).next());
8+
});
9+
};
10+
var __generator = (this && this.__generator) || function (thisArg, body) {
11+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13+
function verb(n) { return function (v) { return step([n, v]); }; }
14+
function step(op) {
15+
if (f) throw new TypeError("Generator is already executing.");
16+
while (_) try {
17+
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
18+
if (y = 0, t) op = [0, t.value];
19+
switch (op[0]) {
20+
case 0: case 1: t = op; break;
21+
case 4: _.label++; return { value: op[1], done: false };
22+
case 5: _.label++; y = op[1]; op = [0]; continue;
23+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
24+
default:
25+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29+
if (t[2]) _.ops.pop();
30+
_.trys.pop(); continue;
31+
}
32+
op = body.call(thisArg, _);
33+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35+
}
36+
};
237
Object.defineProperty(exports, "__esModule", { value: true });
338
var ffi = require("ffi");
439
var ref = require("ref");
@@ -56,6 +91,7 @@ var Darknet = /** @class */ (function () {
5691
this.meta.classes = this.names.length;
5792
this.meta.names = this.names.join('\n');
5893
this.darknet = ffi.Library(library, {
94+
'float_to_image': [IMAGE, ['int', 'int', 'int', float_pointer]],
5995
'load_image_color': [IMAGE, ['string', 'int', 'int']],
6096
'network_predict_image': [float_pointer, ['pointer', IMAGE]],
6197
'get_network_boxes': [detection_pointer, ['pointer', 'int', 'int', 'float', 'float', int_pointer, 'int', int_pointer]],
@@ -104,17 +140,45 @@ var Darknet = /** @class */ (function () {
104140
hier_thresh = 0.5;
105141
if (!nms)
106142
nms = 0.45;
107-
var _image = this.darknet.load_image_color(image, 0, 0);
108-
this.darknet.network_predict_image(net, _image);
143+
this.darknet.network_predict_image(net, image);
109144
var pnum = ref.alloc('int');
110-
var dets = this.darknet.get_network_boxes(net, _image.w, _image.h, thresh, hier_thresh, ref.NULL_POINTER, 0, pnum);
145+
var dets = this.darknet.get_network_boxes(net, image.w, image.h, thresh, hier_thresh, ref.NULL_POINTER, 0, pnum);
111146
var num = pnum.deref();
112147
this.darknet.do_nms_obj(dets, num, meta.classes, nms);
113148
var detections = this.bufferToDetections(dets, num);
114-
this.darknet.free_image(_image);
115149
this.darknet.free_detections(dets, num);
116150
return detections;
117151
};
152+
Darknet.prototype._detectAsync = function (net, meta, image, thresh, hier_thresh, nms) {
153+
return __awaiter(this, void 0, void 0, function () {
154+
var pnum, dets, num, detections;
155+
var _this = this;
156+
return __generator(this, function (_a) {
157+
switch (_a.label) {
158+
case 0: return [4 /*yield*/, new Promise(function (res, rej) {
159+
return _this.darknet.network_predict_image.async(net, image, function (e) { return e ? rej(e) : res(); });
160+
})];
161+
case 1:
162+
_a.sent();
163+
pnum = ref.alloc('int');
164+
return [4 /*yield*/, new Promise(function (res, rej) {
165+
return _this.darknet.get_network_boxes.async(net, image.w, image.h, thresh, hier_thresh, ref.NULL_POINTER, 0, pnum, function (err, dets) { return err ? rej(err) : res(dets); });
166+
})];
167+
case 2:
168+
dets = _a.sent();
169+
num = pnum.deref();
170+
return [4 /*yield*/, new Promise(function (res, rej) {
171+
return _this.darknet.do_nms_obj.async(dets, num, meta.classes, nms, function (e) { return e ? rej(e) : res(); });
172+
})];
173+
case 3:
174+
_a.sent();
175+
detections = this.bufferToDetections(dets, num);
176+
this.darknet.free_detections(dets, num);
177+
return [2 /*return*/, detections];
178+
}
179+
});
180+
});
181+
};
118182
/**
119183
* Synchronously detect objects in an image.
120184
* @param image the destination of the image to be detected
@@ -123,7 +187,86 @@ var Darknet = /** @class */ (function () {
123187
Darknet.prototype.detect = function (image, config) {
124188
if (!config)
125189
config = {};
126-
return this._detectSync(this.net, this.meta, image, config.thresh, config.hier_thresh, config.nms);
190+
var darkNetLoadedImage = typeof image === 'string';
191+
var imageData = typeof image === 'string' ?
192+
this.getImageFromPath(image) :
193+
this.RGBBufferToImage(image.b, image.w, image.h, image.c);
194+
var detection = this._detectSync(this.net, this.meta, imageData, config.thresh, config.hier_thresh, config.nms);
195+
if (darkNetLoadedImage) {
196+
// memory is owned by the darknet lib
197+
this.darknet.free_image(imageData);
198+
}
199+
else {
200+
// memory is owned by JS and will GC eventually
201+
}
202+
return detection;
203+
};
204+
Darknet.prototype.getImageFromPath = function (path) {
205+
return this.darknet.load_image_color(path, 0, 0);
206+
};
207+
Darknet.prototype.getImageFromPathAsync = function (path) {
208+
return __awaiter(this, void 0, void 0, function () {
209+
var _this = this;
210+
return __generator(this, function (_a) {
211+
return [2 /*return*/, new Promise(function (res, rej) {
212+
return _this.darknet.load_image_color.async(path, 0, 0, function (e, image) { return e ? rej(e) : res(image); });
213+
})];
214+
});
215+
});
216+
};
217+
Darknet.prototype.imageToRGBBuffer = function (image) {
218+
var w = image.w;
219+
var h = image.h;
220+
var c = image.c;
221+
var imageElements = w * h * c;
222+
var imageData = new Float32Array(image.data.reinterpret(imageElements * Float32Array.BYTES_PER_ELEMENT, 0).buffer, 0, imageElements);
223+
var rgbBuffer = Buffer.allocUnsafe(imageData.length);
224+
var step = c * w;
225+
var i, k, j;
226+
for (i = 0; i < h; ++i) {
227+
for (k = 0; k < c; ++k) {
228+
for (j = 0; j < w; ++j) {
229+
rgbBuffer[i * step + j * c + k] = imageData[k * w * h + i * w + j] * 255;
230+
}
231+
}
232+
}
233+
return rgbBuffer;
234+
};
235+
Darknet.prototype.rgbToDarknet = function (buffer, w, h, c) {
236+
var imageElements = w * h * c;
237+
var floatBuff = new Float32Array(imageElements);
238+
var step = w * c;
239+
var i, k, j;
240+
for (i = 0; i < h; ++i) {
241+
for (k = 0; k < c; ++k) {
242+
for (j = 0; j < w; ++j) {
243+
floatBuff[k * w * h + i * w + j] = buffer[i * step + j * c + k] / 255;
244+
}
245+
}
246+
}
247+
return floatBuff;
248+
};
249+
Darknet.prototype.RGBBufferToImage = function (buffer, w, h, c) {
250+
var floatBuff = this.rgbToDarknet(buffer, w, h, c);
251+
return this.darknet.float_to_image(w, h, c, new Uint8Array(floatBuff.buffer, 0, floatBuff.length * Float32Array.BYTES_PER_ELEMENT));
252+
};
253+
/**
254+
* Transform an RGB buffer to a darknet encoded image
255+
* @param buffer - rgb buffer
256+
* @param w - width
257+
* @param h - height
258+
* @param c - channels
259+
* @returns Promise<IMAGE>
260+
*/
261+
Darknet.prototype.RGBBufferToImageAsync = function (buffer, w, h, c) {
262+
return __awaiter(this, void 0, void 0, function () {
263+
var floatBuff;
264+
var _this = this;
265+
return __generator(this, function (_a) {
266+
floatBuff = this.rgbToDarknet(buffer, w, h, c);
267+
return [2 /*return*/, new Promise(function (res, rej) { return _this.darknet.float_to_image.async(w, h, c, new Uint8Array(floatBuff.buffer, 0, floatBuff.length * Float32Array.BYTES_PER_ELEMENT), function (e, image) { return e ? rej(e) : res(image); }); })];
268+
});
269+
});
127270
};
128271
/**
129272
* Asynchronously detect objects in an image.
@@ -132,26 +275,43 @@ var Darknet = /** @class */ (function () {
132275
* @returns A promise
133276
*/
134277
Darknet.prototype.detectAsync = function (image, config) {
135-
var _this = this;
136-
if (!config)
137-
config = {};
138-
var thresh = (config.thresh) ? config.thresh : 0.5;
139-
var hier_thresh = (config.hier_thresh) ? config.hier_thresh : 0.5;
140-
var nms = (config.nms) ? config.nms : 0.5;
141-
return new Promise(function (resolve, reject) {
142-
_this.darknet.load_image_color.async(image, 0, 0, function (err, _image) {
143-
_this.darknet.network_predict_image.async(_this.net, _image, function () {
144-
var pnum = ref.alloc('int');
145-
_this.darknet.get_network_boxes.async(_this.net, _image.w, _image.h, thresh, hier_thresh, ref.NULL_POINTER, 0, pnum, function (err, dets) {
146-
var num = pnum.deref();
147-
_this.darknet.do_nms_obj.async(dets, num, _this.meta.classes, nms, function () {
148-
var detections = _this.bufferToDetections(dets, num);
149-
_this.darknet.free_image(_image);
150-
_this.darknet.free_detections(dets, num);
151-
resolve(detections);
152-
});
153-
});
154-
});
278+
return __awaiter(this, void 0, void 0, function () {
279+
var thresh, hier_thresh, nms, darkNetLoadedImage, imageData, _a, detection;
280+
var _this = this;
281+
return __generator(this, function (_b) {
282+
switch (_b.label) {
283+
case 0:
284+
if (!config)
285+
config = {};
286+
thresh = (config.thresh) ? config.thresh : 0.5;
287+
hier_thresh = (config.hier_thresh) ? config.hier_thresh : 0.5;
288+
nms = (config.nms) ? config.nms : 0.5;
289+
darkNetLoadedImage = typeof image === 'string';
290+
if (!(typeof image === 'string')) return [3 /*break*/, 2];
291+
return [4 /*yield*/, this.getImageFromPathAsync(image)];
292+
case 1:
293+
_a = _b.sent();
294+
return [3 /*break*/, 4];
295+
case 2: return [4 /*yield*/, this.RGBBufferToImageAsync(image.b, image.w, image.h, image.c)];
296+
case 3:
297+
_a = _b.sent();
298+
_b.label = 4;
299+
case 4:
300+
imageData = _a;
301+
return [4 /*yield*/, this._detectAsync(this.net, this.meta, imageData, thresh, hier_thresh, nms)];
302+
case 5:
303+
detection = _b.sent();
304+
if (!darkNetLoadedImage) return [3 /*break*/, 7];
305+
// memory is owned by the darknet lib
306+
return [4 /*yield*/, new Promise(function (res, rej) {
307+
return _this.darknet.free_image.async(imageData, function (e) { return e ? rej(e) : res(); });
308+
})];
309+
case 6:
310+
// memory is owned by the darknet lib
311+
_b.sent();
312+
return [3 /*break*/, 7];
313+
case 7: return [2 /*return*/, detection];
314+
}
155315
});
156316
});
157317
};

0 commit comments

Comments
 (0)