How to get bounding box from object detection of TensorFlowJS? - javascript

I'm using this object detection model. And I'd like to get bounding box, class labe and score. I think this model returns bounding box and so on. however it's returns diferent results.
My code
function getMethods(o) {
return Object.getOwnPropertyNames(Object.getPrototypeOf(o))
.filter(m => 'function' === typeof o[m]);
}
const model = await tf.loadGraphModel("https://tfhub.dev/tensorflow/tfjs-model/ssd_mobilenet_v2/1/default/1", {fromTFHub: true});
console.log("Get methods");
console.log(getMethods(model));
const cat = document.getElementById('cat');
let tensor = tf.browser.fromPixels(cat);
tensor = tf.cast(tensor, 'int32');
let y_pred = await model.executeAsync({'image_tensor': tensor.expandDims(0)});
console.log('Predictions: ');
console.log(y_pred);
Results
(12) ["constructor", "findIOHandler", "load", "loadSync", "save", "predict", "normalizeInputs", "normalizeOutputs", "execute", "executeAsync", "convertTensorMapToTensorsMap", "dispose"]
Predictions
[
{
"kept": false,
"isDisposedInternal": false,
"shape": [
1,
1917,
90
],
"dtype": "float32",
"size": 172530,
"strides": [
172530,
90
],
"dataId": {
"id": 1959
},
"id": 1188,
"rankType": "3",
"scopeId": 1382
},
{
"kept": false,
"isDisposedInternal": false,
"shape": [
1,
1917,
1,
4
],
"dtype": "float32",
"size": 7668,
"strides": [
7668,
4,
4
],
"dataId": {
"id": 2019
},
"id": 1239,
"rankType": "4",
"scopeId": 1475
}
]
If I get bounding box, class labe and score, How should I change my code?

each model is different, this one is on tfhub model page: https://tfhub.dev/tensorflow/ssd_mobilenet_v2/2 - you can see what each tensor in output result is
anyhow, model returns all possible scores, classes and boxes without any filtering - which means you need to post-process those results, best using tf.image.nonMaxSuppressionAsync() so you can filter by minimum score and get only boxes you want back
then match box indexes with index of class tensor output to match box with class
you can look up reference implementation at https://github.com/tensorflow/tfjs-models/blob/master/coco-ssd/src/index.ts, key function is infer

There is a ready made code proovided by Paul Van Eck (IBM Developer) channel from YouTube (code snippet works for me, but as a model not good):
const scores = y_pred[0].dataSync();
const numBoxesFound = y_pred[0].shape[1];
const numClassesFound = y_pred[0].shape[2];
const maxScores = [];
const classes = [];
// for each bounding box returned
for (let i = 0; i < numBoxesFound; i++) {
let maxScore = -1;
let classIndex = -1;
// find the class with the highest score
for (let j = 0; j < numClassesFound; j++) {
if (scores[i * numClassesFound + j] > maxScore) {
maxScore = scores[i * numClassesFound + j];
classIndex = j;
}
}
maxScores[i] = maxScore;
classes[i] = classIndex;
}
const boxes = tf.tensor2d(y_pred[1].dataSync(), [y_pred[1].shape[1], y_pred[1].shape[3]]);
const indexTensor = tf.image.nonMaxSuppression(boxes, maxScores, maxNumBoxes, 0.5, 0.5);
const indexes = indexTensor.dataSync();
const count = indexes.length;
const objects = [];
for (let i = 0; i < count; i++) {
const bbox = [];
for (let j = 0; j < 4; j++) {
bbox[j] = rrr[1].dataSync()[indexes[i] * 4 + j];
}
const minY = bbox[0] * image_height; // <= width & height you can get it by gm nodejs module
const minX = bbox[1] * image_width;
const maxY = bbox[2] * image_height;
const maxX = bbox[3] * image_width;
objects.push({
bbox: [minX, minY, maxX, maxY],
label: labels[classes[indexes[i]]],
score: maxScores[indexes[i]]
});
}
console.log(objects);
// output:
[
{
bbox: [
25.680667877197266,
991.999267578125,
2295.833375930786,
3062.655055999756
],
label: 'background',
score: 0.900220513343811
}
]

Related

How I can make this code work faster? I have a time limit of 6.5 seconds

I need this code to work faster. I'm assuming that the problem could be solved by creating correct key:value pairs in "filedics". I guess that it should look something like this:
word: [{ id: 1, count: 1}, { id: 2, count: 1}];
My code is working good in terms of logic, but when it receiving a lot of strings as an input it can`t handled it.
But maybe there are some other mistakes that I don`t see.
const _reader = _readline.createInterface({
input: process.stdin,
});
const _inputLines = [];
let _curLine = 0;
_reader.on("line", (line) => {
_inputLines.push(line);
});
process.stdin.on("end", solve);
function result(reqs, files, n, m) {
const filedics = files.map(() => ({}));
for (let fi = 0; fi < files.length; ++fi) {
const file = files[fi];
const fdic = filedics[fi];
const usedwords = {};
for (let wf = 0; wf < file.length; ++wf) {
const rword = file[wf];
if (usedwords[rword]) continue;
usedwords[rword] = true;
if (fdic[rword] == null) fdic[rword] = numofword(rword, file);
}
}
let rel = 0;
const relfiles = [];
for (let ri = 0; ri < reqs.length; ++ri) {
for (let fi = 0; fi < filedics.length; ++fi) {
const fdic = filedics[fi];
for (let wi = 0; wi < reqs[ri].length; ++wi) {
const wordReq = reqs[ri][wi];
const usedwords = {};
if (usedwords[wordReq]) continue;
usedwords[wordReq] = true;
if (fdic[wordReq]) {
rel += fdic[wordReq];
}
}
if (rel) relfiles.push([rel, fi + 1]);
rel = 0;
}
const sr = relfiles.sort(([rel1], [rel2]) => rel2 - rel1);
for (let i = 0; i < 5 && i < sr.length; ++i)
process.stdout.write(sr[i][1] + " ");
process.stdout.write("\n");
relfiles.length = 0;
}
}
function numofword(word, file) {
let n = 0;
for (let i = 0; i < file.length; ++i) {
if (word === file[i]) n++;
}
return n;
}
function solve() {
const n = readInt();
const files = arrLines(n);
const m = readInt();
const reqs = arrLines(m);
let res = result(reqs, files, n, m);
}
I have for example 2 arrays with 3 subarrays in each.
INPUT
3
[
[ 'i', 'love', 'coffee' ],
[ 'coffee', 'with', 'milk', 'and', 'sugar' ],
[ 'free', 'tea', 'for', 'everyone' ]
]
3
[
[ 'i', 'like', 'black', 'coffee', 'without', 'milk' ],
[ 'everyone', 'loves', 'new', 'year' ],
[ 'mary', 'likes', 'black', 'coffee', 'without', 'milk' ]
]
Goal is to output subarrays by number that have most matches, in descending order. For example the output for this code should be
1 2
3
2 1
(1) I don't see the reason to maintain separate dictionaries for each file if always all entries are checked against all filedicts and the result is summed up. Instead one could just use one filedict for all files.
(2) numofword is extremely inefficient, as for every word in the file the whole file is scanned again, turning this into an O(n^2) algorithm. Simply traversing the file once and counting up turns that into O(n).
(3) Maps can be slightly better for lots of keys than objects.
(4) I'm not quite sure what arrLines does, but usually one can maximize the throughput by reading and processing data in chunks and processing multiple files in parallel.
const occurences = new Map();
for (const file of files) {
for(const word of file) {
occurences.set(word, (occurences.get(word) ?? 0) + 1);
}
}
for (const seq of seqs) {
let score = 0;
const checked = new Set();
for (const word of seq) {
if (checked.has(word)) continue;
checked.add(word);
score +=occurences.get(word) ?? 0;
}
// collect seqs and sort
}

Creating an array of objects with random number of properties

I am trying to create an array of objects that will be populated by random properties.
I want the objects all to look like this
{ systemStar: "something random", planets: ["random1", "random2", etc]}
Currently I have the following code
const letThereBeLight = () => {
let universe = []
const starNumber = 5;
let randomNumberOfPlanets = Math.floor(Math.random() * 6);
for (let i = 0; i < starNumber; i++) {
universe.push({
systemStar: starList[Math.floor(Math.random() * lengthOfStarList)],
systemPlanets: [planetList[Math.floor(Math.random() * lengthOfPlanetList)]]
})
}
console.log(universe)
}
This does a great job in creating the objects I want with the following format, however the systemPlanets is only one, instead of a random number. I have tried to do a double for loop but the syntax was eluding me.
How can I get an array of random strings inside of my for loop?
Added variables for clarity
let starList = [
"Red-Giant",
"Red-Supergiant",
"Blue-Giant",
"White-Dwarf",
"Yellow-Dwarf",
"Red-Dwarf",
"Brown-Dwarf",
];
let planetList = [
"Rocky",
"Temperate",
"Ocean",
"Frozen",
"Lava",
"Gas"
];
Try using Array.from({length:5}) to create an array of 5 elements.
and replace 5 with a random number by using Math.random() * 5 Math.floor() method to round it down to near number, add add one to at least create one planet. then map them to values in source array `planetList.
let starList = [
"Red-Giant",
"Red-Supergiant",
"Blue-Giant",
"White-Dwarf",
"Yellow-Dwarf",
"Red-Dwarf",
"Brown-Dwarf",
];
let planetList = [
"Rocky",
"Temperate",
"Ocean",
"Frozen",
"Lava",
"Gas"
];
let lengthOfStarList = starList.length;
let lengthOfPlanetList = planetList.length;
let universe = []
const starNumber = 5;
for (let i = 0; i < starNumber; i++) {
universe.push({
systemStar: starList[Math.floor(Math.random() * lengthOfStarList)],
systemPlanets: Array.from({length:Math.floor(Math.random() * 5)+1}).map(x=>planetList[Math.floor(Math.random() * lengthOfPlanetList)])
})
}
console.log(universe)
What you need is create another loop of random number that is less than number of available planets, and create an array of planets in that loop:
let starList = [
"Red-Giant",
"Red-Supergiant",
"Blue-Giant",
"White-Dwarf",
"Yellow-Dwarf",
"Red-Dwarf",
"Brown-Dwarf",
];
let planetList = [
"Rocky",
"Temperate",
"Ocean",
"Frozen",
"Lava",
"Gas"
];
const letThereBeLight = () => {
let universe = []
const starNumber = 5;
let randomNumberOfPlanets = Math.floor(Math.random() * 6);
for (let i = 0; i < starNumber; i++) {
const planets = [];
// generate list of planets with at least 1 planet
for(let p = 0, max = ~~(Math.random() * planetList.length - 1) + 1; p < max; p++)
planets[planets.length] = planetList[~~(Math.random() * planetList.length)];
universe.push({
systemStar: starList[Math.floor(Math.random() * starList.length)],
systemPlanets: planets
})
}
console.log(universe)
}
letThereBeLight();

Rearrange / transpose array items by keys diagonally in JavaScript

I have an array of strings, for example:
var arr=["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"];
To refer to any of these values, there are corresponding keys from 0 to 8, i.e. the arr[3] corresponds to "wolf". The amount of items of the actual array may vary and have more than 100 items in it. In this example there are 9 [0,1,2,3,4,5,6,7,8].
What I would like to accomplish is to rearrange the items by their keys diagonally, i.e. from:
[0,1,2,
3,4,5,
6,7,8]
into:
[0,2,5,
1,4,7,
3,6,8]
i.e. into [0,2,5,1,4,7,3,6,8], and thus also the sequence of the corresponding values from the original:
var arr=["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"];
resulting into the rearranged values:
var arr2=["dog", "bear", "hare", "cat", "lynx", "wolf", "owl", "sheep", "hen"];
The use of this solution would be implemented in more complex visualization of string items (strings each consisting of binary digits that correspond to UTF-8 encoded values of another data) in square shape, arranging them diagonally specifically from the left top corner. Thank you in advance!
It took some time for me to get the math right, but I was able to make a function which returns an Array of indexes in the correct order:
function getDiagonalArrayIndexes(length) {
const sqrt = Math.floor(Math.sqrt(length));
const formula = (x, y) => (y + x) * (y + x + 1) / 2 + x;
return Array.from({ length: sqrt*sqrt }, (_, i) => {
let x = i % sqrt, y = Math.floor(i / sqrt);
if (x + y < sqrt) {
return formula(x, y);
} else {
return length - 1 - formula(sqrt - 1 - x, sqrt - 1 - y);
}
})
// In case length's square root is not an integer
.concat(new Array(length - sqrt * sqrt).fill(null));
}
printSquare( getDiagonalArrayIndexes(9) );
printSquare( getDiagonalArrayIndexes(16) );
printSquare( getDiagonalArrayIndexes(25) ); /* Just for the demo */ function printSquare(n){const o=Math.sqrt(n.length),t=[];for(var e=0,a=0;e<n.length;e++)e>=o&&e%o==0&&a++,t[a]=t[a]||[],t[a].push(n[e]);console.log("[\n"+t.map(n=>n.map(n=>(" "+n).slice(-3)).join(",")).join(",\n")+"\n]")}document.body.innerHTML="<style>\n .as-console-wrapper { max-height: 100% !important; top: 0; }\n</style>";
You can then reuse it and map the indexes using your data:
function reorderDiagonally(arr) {
return getDiagonalArrayIndexes(arr.length)
.map(i => i !== null ? arr[i] : '');
}
var arr = ["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"];
console.log(JSON.stringify( reorderDiagonally(arr) )); /* Just for the demo */ function getDiagonalArrayIndexes(r){const t=Math.floor(Math.sqrt(r)),n=(r,t)=>(t+r)*(t+r+1)/2+r,l=Array.from({length:t*t},(l,a)=>{let e=a%t,o=Math.floor(a/t);return e+o<t?n(e,o):r-1-n(t-1-e,t-1-o)});return l.concat(new Array(r-l.length).fill(null))}
path method, gives valid diagonal path for given [row, col]
diagonals, aggregate paths for starting on first column and last row.
Simple map to shuffle based on the diagonal paths generated.
PS: Not tested the cases where array length is not perfect square.
const path = (row, col, len, res) => {
while (row > -1 && col < len) {
res.push([row, col]);
row--;
col++;
}
return res;
};
const diagonals = (len) => {
const res = [];
for (let i = 0; i < len; i++) {
path(i, 0, len, res);
}
for (let j = 1; j < len; j++) {
path(len - 1, j, len, res);
}
return res;
};
// const input = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const input = ["dog", "cat", "bear", "wolf", "lynx", "hare", "sheep", "owl", "hen"]
const len = Math.floor(Math.sqrt(input.length));
const output = [...input]
diagonals(len).map(([row, col], i) => output[row * len + col] = input[i]);
console.log(output.join(', '));

How can I remove duplicate results and merge objects that have a similar property on a mySQL query?

I'm using the mysql library with node.js to create this query:
SELECT vols.id_vol, vols.id_place, vols.id_vol_type, vols.id_user_creator, vols.name, vols.desc, vols.date_creation, vols.date_begin, vols.date_end, vols.duration, ' +
' vols.active, vols.start_time, vols.end_time, vols.insurance, vols.deleted, users.id_user, users.login, users.verified, users.photo_url, comments.message, comments.id_user, place.id_place, place.name AS placeName, place.lat, place.long' +
' FROM vols INNER JOIN users ON vols.id_user_creator = users.id_user INNER JOIN place ON vols.id_place = place.id_place LEFT JOIN comments ON comments.id_vol = vols.id_vol WHERE vols.deleted = 0
That returns this:
"success": true,
"vols": [
{
"vol": {
"id_vol": 1,
"id_place": 1,
"id_vol_type": 2,
"id_user_creator": 1,
},
"users": {
"id_user": 1,
"login": "",
"verified": 0,
},
"comments": {
"message": "Muito fixe",
"id_user": 3
},
{
"vol": {
"id_vol": 1,
"id_place": 1,
"id_vol_type": 2,
"id_user_creator": 1,
},
"users": {
"id_user": 1,
"login": "",
"verified": 0,
},
"comments": {
"message": "ola",
"id_user": 3
}
It's currently repeating "vol" for each comment that exists with that specific vol ID. It is possible to stop showing duplicates and merge the comments on a single array?
It is not really possible, I mean you could do something with the group_concat function, but it would be messy.
What I usually do in similar cases is:
Query out all the relevant vols
Extract all the userIDs, placeIDs, commentIDs you want to look at.(While running through all the results to extract this data, it is a good idea to make an object for each result, and put them in index an index array(object) for each indexable parameter"user,place,comment")
Make 3 separate queries, one for each (users, places, comments), where you only select the relevant elements(use the WHERE id IN (1,5,7,8,10) filter)
run through the results of each of those queries, and insert them in the vol objects, using the index arrays(objects)
In javascript that would be something like(Sorry don't know node.js, so I don't have an excact solution
var results = db.queryResults('SELECT vols.id_vol, vols.id_place, vols.id_vol_type, vols.id_user_creator, vols.name, vols.desc, vols.date_creation, vols.date_begin, vols.date_end, vols.duration, vols.active, vols.start_time, vols.end_time, vols.insurance, vols.deleted FROM vols WHERE vols.deleted = 0');
var lookupByUserID = {};
var lookupByPlaceID = {};
var lookupByVolID = {};
var userIN = [];
var placeIN = [];
var volIN = [];
for (var i = 0; i < results.length; i++)
{
var result = results[i];
if (!lookupByUserID[result.id_user_creator])
lookupByUserID[result.id_user_creator] = [];
lookupByUserID[result.id_user_creator].push(result);
if(!lookupByPlaceID[result.id_place])
lookupByPlaceID[result.id_place] = [];
lookupByPlaceID[result.id_place].push(result);
lookupByVolID[result.id_vol] = result;
userIN.push(result.id_user_creator);
placeIN.push(result.id_place);
volIN.push(result.id_vol);
results.comments = [];
}
var userResults = db.queryResults('SELECT users.id_user, users.login, users.verified, users.photo_url FROM users WHERE id_user IN ('+userIN.join(',')+')');
var placeResults = db.queryResults('SELECT place.id_place, place.name AS placeName, place.lat, place.long FROM place WHERE id_place IN ('+placeIN.join(',')+')');
var commentResults = db.queryResults('SELECT comments.id_vol, comments.message, comments.id_user FROM comments WHERE id_vol IN ('+volIN.join(',')+')');
for (var i = 0; i < userResults.length; i++)
{
var user = userResults[i];
for (var j = 0; j < lookupByUserID[user.id_user].length; j++)
lookupByUserID[user.id_user][j].created_by_user = user;
}
for (var i = 0; i < placeResults.length; i++)
{
var place = placeResults[i];
for (var j = 0; j < lookupByPlaceID[place.id_place].length; j++)
lookupByPlaceID[place.id_place][j].place = place;
}
for (var i = 0; i < commentResults.length; i++)
{
var comment = commentResults[i];
lookupByVolID[comment.id_vol].comments.push(comment);
}
Now the object "results" should contain all the information

Javascript/jQuery Update Date

I wrote the following code which works correctly, I'm creating a new array that takes each two members from numbers and divide them. Sample:
var numbers = [{ "name": "testvalue", "data": [10] },
{ "name": "testtotal", "data": [2] },
{ "name": "prodvalue", "data": [10] },
{ "name": "prodtotal", "data": [2] }];
var mydata = $.grep(numbers, function(e) {
return new RegExp('^test*').test(e.name)
});
console.log(mydata) // prints [{"name": "testvalue", "data": [10]}, {"name": "testtotal", "data": [2]}]
result = [{
"name": "testresult",
"data": [mydata[0]["data"] / mydata[1]["data"]]
}];
console.log(result) // [{"name": "testresult", "data": [5]}]
My question is about efficiency, is what I'm doing efficient or is there a better way to do things? (I'm new to JS/jQuery that's why I'm asking).
I don't care about the content of numbers, All I want is divide every two elements, every element name ends with total or value
Sample real data as requested. specifically the data is coming from graphite/d3.json
var data =
[ {"target": "xs12t11.Busy", "datapoints": [34,54,65,76,87] },
{"target": "xs12t11.Total", "datapoints": [34,54,2,12,33]} ];
I'm still not entirely sure I understand your requirements. I've provided two examples that iterate over data and translate it into a result.
Given the following input with the potential for an odd number of rows:
var data =
[ {"target": "xs12t11.Busy", "datapoints": [34,54,65,76,87] },
{"target": "xs12t11.Total", "datapoints": [34,54,2,12,33] },
{"target": "xs12t12.Busy", "datapoints": [34,54,2,12,33]} ];
Example 1 Sums up the series in each row and gives a result with a single average per pair of rows:
function sum(inArr) {
var retVal = 0;
for(var i = 0; i < inArr.length; ++i) retVal += inArr[i];
return retVal;
}
var result = [];
// Increment by 2 to grab records in pairs
for(var i = 0; i < data.length; i += 2) {
var series1 = data[i];
var series2 = data[i+1];
// Series1 always exists, but series2 may not
if(!series2) break;
result.push({
name: series1.target.split('.')[0],
data: sum(series1.datapoints)/sum(series2.datapoints)
});
}
console.log(JSON.stringify(result));
Output
[{"name":"xs12t11","data":2.3407407407407406}]
Example 2 Returns results for each pair of rows with a series of averages:
function avgSeries(series1, series2) {
var retVal = [];
var len = Math.min(series1.length, series2.length);
for(var i = 0; i < len; i++) retVal.push(series1[i]/series2[i]);
return retVal;
}
var result = [];
// Increment by 2 to grab records in pairs
for(var i = 0; i < data.length; i += 2) {
var series1 = data[i];
var series2 = data[i+1];
// Series1 always exists, but series2 may not
if(!series2) break;
result.push({
name: series1.target.split('.')[0],
data: avgSeries(series1.datapoints, series2.datapoints)
});
}
console.log(JSON.stringify(result));
Output
[{"name":"xs12t11","data":[1,1,32.5,6.333333333333333,2.6363636363636362]}]
If neither of these are inline with what you are looking to do, let me know.

Categories