Updated code

This commit is contained in:
Bharath Sudharsan 2022-07-23 04:16:33 +01:00
parent 1bacaba9d1
commit ec305108a6
3 changed files with 224 additions and 4 deletions

View File

@ -1,4 +1,4 @@
# TinyML-CAM - Image Recognition System that Runs at 80 FPS in 1 Kb of RAM
## TinyML-CAM - Image Recognition System that Runs at 80 FPS in 1 Kb of RAM
### Image Recognition Demo - ESP32
ESP32 classifying Raspberry Pi Pico, Portenta H7, Wio Terminal from image frames
@ -17,6 +17,6 @@ Following can be observed from the video:
### Code
- [ipynb]-TinyML-CAM-full-code-with-markdown.ipynb
- [h]-HOG-plus-RandomForest-classifier.h
- [ino]-arduino-ESP32-code.ino - upload to
- [[ipynb]-TinyML-CAM-full-code-with-markdown.ipynb](https://github.com/bharathsudharsan/TinyML-CAM/blob/main/%5Bipynb%5D-TinyML-CAM-full-code-with-markdown.ipynb)
- [[h]-HOG-plus-RandomForest-classifier.h](https://github.com/bharathsudharsan/TinyML-CAM/blob/main/%5Bh%5D-HOG-plus-RandomForest-classifier.h)
- [[ino]-arduino-ESP32-code.ino](https://github.com/bharathsudharsan/TinyML-CAM/blob/main/%5Bino%5D-arduino-ESP32-code.ino) - upload to

220
[h]-HogPipeline.h Normal file
View File

@ -0,0 +1,220 @@
#ifndef UUID5853197456
#define UUID5853197456
#ifndef UUID5853199664
#define UUID5853199664
/**
* HOG(block_size=8, bins=9, cell_size=3)
*/
class HOG {
public:
/**
* Transform input image
*/
template<typename T, typename U>
bool transform(T *input, U *output) {
uint16_t f = 0;
uint16_t block = 0;
float hog[135] = {0};
// compute gradients
for (uint16_t blockY = 0; blockY < 3; blockY++) {
const uint16_t blockOffsetY = blockY * 320;
for (uint16_t blockX = 0; blockX < 5; blockX++) {
const uint16_t blockOffsetX = blockX * 8;
float hist[9] = {0};
for (uint16_t _y = 1; _y < 7; _y += 1) {
const uint16_t rowOffset = blockOffsetY + _y * 40 + blockOffsetX;
const uint16_t rowOffsetBefore = rowOffset - 40;
const uint16_t rowOffsetAfter = rowOffset + 40;
for (uint16_t _x = 1; _x < 7; _x += 1) {
const uint16_t offset = rowOffset + _x;
const uint16_t offsetBefore = rowOffsetBefore + _x;
const uint16_t offsetAfter = rowOffsetAfter + _x;
const float gy = input[offsetAfter] - input[offsetBefore];
const float gx = input[offset + 1] - input[offset - 1];
const float g = sqrt(gy * gy + gx * gx);
uint8_t angle = abs(this->arctan(gy, gx) * 180 / 3.141592653589793f / 20);
if (angle >= 8) angle = 8;
hist[angle] += g;
}
}
for (uint16_t i = 0; i < 9; i++)
hog[f++] = hist[i];
block += 1;
// end of cell, normalize
if ((block % 3) == 0) {
const uint16_t offset = (block - 3) * 9;
float maxGradient = 0.0001;
for (uint16_t i = 0; i < 27; i++) {
const float h = hog[offset + i];
if (h > maxGradient)
maxGradient = h;
}
for (uint16_t i = 0; i < 27; i++) {
hog[offset + i] /= maxGradient;
}
maxGradient = 0.0001;
}
}
}
// copy over
for (uint16_t i = 0; i < 135; i++)
output[i] = hog[i];
return true;
}
protected:
/**
* optional atan2 approximation for faster calculation
*/
float arctan(float y, float x) {
float r = 0;
if (abs(y) < 0.00000001)
return 0;
else if (abs(x) < 0.00000001)
return 3.14159274 * (y > 0 ? 1 : -1);
else {
float a = min(abs(x), abs(y)) / max(abs(x), abs(y));
float s = a * a;
r = ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a;
if (abs(y) > abs(x))
r = 1.57079637 - r;
}
if (x < 0)
r = 3.14159274 - r;
if (y < 0)
r = -r;
return r;
}
};
#endif
/**
* ImagePipeline: HogPipeline
* ---------
* - Resize(from=((160, 120)), to=(40, 30), pixformat=gray)
* > HOG(block_size=8, bins=9, cell_size=3)
*/
class HogPipeline {
public:
static const size_t NUM_INPUTS = 1200;
static const size_t NUM_OUTPUTS = 135;
static const size_t WORKING_SIZE = 135;
float features[135];
/**
* Extract features from input image
*/
template<typename T>
bool transform(T *input) {
time_t start = micros();
ok = true;
preprocess(input);
ok = ok && hog.transform(input, features);
latency = micros() - start;
return ok;
}
/**
* Debug output feature vector
*/
template<typename PrinterInterface>
void debugTo(PrinterInterface &printer, uint8_t precision=5) {
printer.print(features[0], precision);
for (uint16_t i = 1; i < 135; i++) {
printer.print(", ");
printer.print(features[i], precision);
}
printer.print('\n');
}
/**
* Get latency in micros
*/
uint32_t latencyInMicros() {
return latency;
}
/**
* Get latency in millis
*/
uint16_t latencyInMillis() {
return latency / 1000;
}
protected:
bool ok;
time_t latency;
HOG hog;
template<typename T>
void preprocess(T *input) {
// grayscale rescaling
const float dy = 4.0f;
const float dx = 4.0f;
for (uint16_t y = 0; y < 30; y++) {
const size_t sourceOffset = round(y * dy) * 160;
const size_t destOffset = y * 40;
for (uint16_t x = 0; x < 40; x++)
input[destOffset + x] = input[sourceOffset + ((uint16_t) (x * dx))];
}
}
};
static HogPipeline hog;
#endif