-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinterface.js
More file actions
194 lines (118 loc) · 7.84 KB
/
interface.js
File metadata and controls
194 lines (118 loc) · 7.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// █▀▀ ▀▄▀ ▀█▀ █▀▀ █▀█ █▄░█ ▄▀█ █░░ █▀▀ █▀█ █▀▀ █▀▄ █ ▀█▀ █▀
// ██▄ █░█ ░█░ ██▄ █▀▄ █░▀█ █▀█ █▄▄ █▄▄ █▀▄ ██▄ █▄▀ █ ░█░ ▄█
// █▀█ █▀▀ █▀█ █░█ █ █▀█ █▀▀ █▀▄▀█ █▀▀ █▄░█ ▀█▀ █▀
// █▀▄ ██▄ ▀▀█ █▄█ █ █▀▄ ██▄ █░▀░█ ██▄ █░▀█ ░█░ ▄█
tf = require('@tensorflow/tfjs-node'); // Load in the TensorFlow package
fs = require('fs'); // File loading system. Here to manually load in images and test the model.
jpeg = require('jpeg-js'); // Package that converts a .jpg image into raw data, for the model. Copyright (c) 2014, Eugene Ware. All rights reserved.
http = require("http"); // HTTP Module - Handles HTTP requests.
let model // Global variable for the model.
// █▀█ █░█ █▄░█ █▀▄▀█ █▀█ █▀▄ █▀▀ █░░
// █▀▄ █▄█ █░▀█ █░▀░█ █▄█ █▄▀ ██▄ █▄▄
// Possible Return Values:
// -1 = Nothing found
// -2 = Model is busy
// 0-59 = Detected Object
let isBusy = false; // Debounce, to not send another request while the module is busy.
async function runModel(rawImageData) {
if (isBusy) return -2; // If the model is busy, then return that value.
isBusy = true; // Mark the model as busy
console.log("Extracting data from received image.");
// Split off the data from jpeg-js into a more readable format for tensorFlow
const { width, height, data } = rawImageData;
let tfImg = tf.tensor3d(data, [height, width, 3], 'float32');
// data = actual pixels in the image
// height, width = size of the image
// 3 = number of channels (RGB) - directed by our specific model
// float32 = dataType to convert the image to - directed by our specific model
tfImg = tfImg.expandDims(0); // Add a "batch dimension" to the array-- the model can take more than one image in a batch,
// but in our case we give it just one image, so we add that to the data for the model (by default it's 1)
tfImg = tf.image.resizeBilinear(tfImg, [640, 640]); // Algorithm that resizes the image to 640x640 (what our specific model expects);
tfImg = tfImg.toFloat() // Convert the values of the pixels (integers up until now) to floats
tfImg = tfImg.div(255.0); // Convert the normal RGB values (0-255) to floats between 0 and 1 (what the modele expects)
console.log("Executing model...");
const outputs = await model.executeAsync(tfImg); // Execute the model
console.log("Model finished execution!");
const classIds = outputs[2]; // Second option in the result array is the actual amount of objects.
const classId = classIds.dataSync()[0]; // Model seems to be sorting the results automatically.
// hence, the most likely detected object is at index 0.
isBusy = false; // Mark model as no longer being busy!
return classId; // Return the integer representative of the detected object.
// 0-59, with -1 meaning that the model found nothing.
}
const printRawData = false;
async function main() {
// █░░ █▀█ ▄▀█ █▀▄ █▀▄▀█ █▀█ █▀▄ █▀▀ █░░
// █▄▄ █▄█ █▀█ █▄▀ █░▀░█ █▄█ █▄▀ ██▄ █▄▄
// Wait for the model to mark itself as 'ready' before giving it commands.
await tf.ready();
// Create an AI Model instance by giving TensorFlow the model.json file + .bin weigh data
model = await tf.loadGraphModel('http://localhost:4000/model.json');
console.log('API: model.json file loaded in succesfully.');
// █▀ █▀▀ █▀█ █░█ █▀▀ █▀█ █ █▄░█ ▀█▀ █▀▀ █▀█ █▀▀ ▄▀█ █▀▀ █▀▀
// ▄█ ██▄ █▀▄ ▀▄▀ ██▄ █▀▄ █ █░▀█ ░█░ ██▄ █▀▄ █▀░ █▀█ █▄▄ ██▄
const server = http.createServer(function (req, res) {
if (req.method === "POST" && req.url === "/detect") { // Check if the request to the server is specifically a POST request (that can send data alongside it) to detect something with the model.
// Check if the request contains an image or not.
const type = req.headers["content-type"];
if (type !== "image/jpeg") {
res.statusCode = 400;
return res.end("Expected image/jpeg for the model to process.");
}
let dataChunks = []; // Define a blank array of data chunks, where we will store the bytes of the image as they come.
// Runs while we're still receiving data.
req.on("data", function(chunk) {
dataChunks.push(chunk);
});
// Runs if there's a connection error to the client
req.on("error", function (errorMessage) {
console.error("Request/Connection error: ", errorMessage);
});
// Runs once all of the data has been sent.
req.on("end", async function() {
const image = Buffer.concat(dataChunks);
dataChunks = []; // Reset dataChunks after concating them in one single image.
try {
// Attempt to decode the image sent over the HTTP request.
const rawImageData = jpeg.decode(image, {formatAsRGBA: false});
console.log(rawImageData);
// Save the last searched image as a file.
let dataToEncode = {
data: rawImageData,
width: 640,
height: 480,
};
let encodedImage = jpeg.encode(dataToEncode, 50);
fs.writeFileSync('last-image.jpg', encodedImage.data);
// Run the model and store the result.
let modelResult = await runModel(rawImageData);
// Print that out on the server as well, as a form of debug/logging.
console.log('most likely object: ');
console.log(modelResult);
// Print all possible objects, not just the most likely one, and their weights.
if (printRawData) {
console.log("============= RAW DATA: ");
const numDet = outputs[3].dataSync()[0]; // N
const scores = outputs[1].dataSync().slice(0, numDet);
const ids = outputs[2].dataSync().slice(0, numDet);
console.log(numDet);
console.log(scores);
console.log(ids);
}
res.statusCode = 200;
return res.end(String(modelResult));
} catch (errorMessage) {
console.error(errorMessage);
res.statusCode = 400;
return res.end("Invalid or corrupted JPEG image.");
}
});
} else {
res.statusCode = 404;
return res.end("Command not found");
}
});
console.log("API: Server listening on port 3000.")
server.listen(3000);
}
main();