Skip to content

Commit b50e782

Browse files
jpegenc
1 parent 13f17d8 commit b50e782

File tree

5 files changed

+231
-8
lines changed

5 files changed

+231
-8
lines changed

src/eloquent_esp32cam/camera/camera.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,20 @@ namespace Eloquent {
189189
return disk.writeBinary(filename, frame->buf, frame->len);
190190
}
191191

192+
/**
193+
* Sometimes you may need to swap RGB565 bytes
194+
*/
195+
void swapBytes() {
196+
if (!hasFrame())
197+
return;
198+
199+
for (size_t i = 0; i < frame->len; i += 2) {
200+
const uint8_t tmp = frame->buf[i];
201+
frame->buf[i] = frame->buf[i + 1];
202+
frame->buf[i + 1] = tmp;
203+
}
204+
}
205+
192206
protected:
193207
};
194208
}

src/eloquent_esp32cam/jpeg/JPEGDECWrapper.h

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ namespace Eloquent {
103103
return exception.set("No frame available");
104104

105105
if (!jpeg.openRAM(camera.frame->buf, camera.frame->len, __handleMCU__))
106-
return exception.set("Can't init decoder");
106+
return exception.set(decodeErrorCode(jpeg.getLastError()));
107107

108108
const uint16_t inputWidth = camera.frame->width / divisor();
109109
const uint16_t inputHeight = camera.frame->height / divisor();
@@ -124,13 +124,10 @@ namespace Eloquent {
124124
return exception.set("Can't allocate decode buffer");
125125

126126
benchmark.benchmark([this]() {
127-
if (!jpeg.decode(0, 0, _options)) {
128-
exception.set("Decode failed");
129-
}
130-
else {
131-
exception.clear();
132-
}
133-
127+
exception.set(
128+
!jpeg.decode(0, 0, _options) ?
129+
decodeErrorCode(jpeg.getLastError()) : ""
130+
);
134131
jpeg.close();
135132
});
136133

@@ -160,6 +157,20 @@ namespace Eloquent {
160157
return count() * bpp();
161158
}
162159

160+
/**
161+
* Get decoded image width
162+
*/
163+
inline uint16_t width() {
164+
return outputWidth;
165+
}
166+
167+
/**
168+
* Get decoded image height
169+
*/
170+
inline uint16_t height() {
171+
return outputHeight;
172+
}
173+
163174
/**
164175
* Get bytes per pixel (grayscale = 1, rgb = 2)
165176
*/
@@ -174,6 +185,13 @@ namespace Eloquent {
174185
return (uint16_t*) pixels;
175186
}
176187

188+
/**
189+
* Get pixels as uint8_t* buffer
190+
*/
191+
uint8_t* u8() {
192+
return pixels;
193+
}
194+
177195
/**
178196
* Swap byte order
179197
*/
@@ -250,6 +268,24 @@ namespace Eloquent {
250268
_allocatedSize = size;
251269
}
252270
}
271+
272+
/**
273+
*
274+
*/
275+
String decodeErrorCode(int code) {
276+
switch (code) {
277+
case JPEG_INVALID_PARAMETER:
278+
return "Invalid parameter";
279+
case JPEG_DECODE_ERROR:
280+
return "Decode error";
281+
case JPEG_UNSUPPORTED_FEATURE:
282+
return "Unsupported feature";
283+
case JPEG_INVALID_FILE:
284+
return "Invalid file";
285+
default:
286+
return "Unknown error";
287+
}
288+
}
253289
};
254290
}
255291
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#ifndef __JPEGENC__
2+
#error "You must include JPEGENC.h *before* eloquent_esp32cam"
3+
#else
4+
5+
#ifndef ELOQUENTESP32CAM_JPEGENC
6+
#define ELOQUENTESP32CAM_JPEGENC
7+
8+
#include "../mem.h"
9+
#include "../camera/camera.h"
10+
#include "../extra/exception.h"
11+
#include "../extra/time/benchmark.h"
12+
13+
using eloq::camera;
14+
using Eloquent::Error::Exception;
15+
using Eloquent::Extra::Time::Benchmark;
16+
17+
namespace Eloquent {
18+
namespace Esp32cam {
19+
namespace JPEG {
20+
/**
21+
* Eloquent interface to JPEGENC library
22+
*/
23+
class JPEGENCWrapper {
24+
public:
25+
uint8_t *bytes;
26+
JPEGENC jpeg;
27+
Exception exception;
28+
Benchmark benchmark;
29+
30+
JPEGENCWrapper() :
31+
exception("JPEGENC"),
32+
_length(0),
33+
_numBytes(0),
34+
_pixelType(JPEGE_PIXEL_RGB565),
35+
_quality(JPEGE_Q_HIGH),
36+
_subsample(JPEGE_SUBSAMPLE_444) {
37+
38+
}
39+
40+
/**
41+
*
42+
*/
43+
void fromRGB565() {
44+
_pixelType = JPEGE_PIXEL_RGB565;
45+
}
46+
47+
/**
48+
*
49+
*/
50+
void fromGrayscale() {
51+
_pixelType = JPEGE_PIXEL_GRAYSCALE;
52+
}
53+
54+
/**
55+
*
56+
*/
57+
void toBestQuality() {
58+
_quality = JPEGE_Q_BEST;
59+
}
60+
61+
/**
62+
*
63+
*/
64+
void toGoodQuality() {
65+
_quality = JPEGE_Q_HIGH;
66+
}
67+
68+
/**
69+
*
70+
*/
71+
void toMediumQuality() {
72+
_quality = JPEGE_Q_MED;
73+
}
74+
75+
/**
76+
*
77+
*/
78+
void toLowQuality() {
79+
_quality = JPEGE_Q_LOW;
80+
}
81+
82+
/**
83+
*
84+
*/
85+
void subsample() {
86+
_subsample = JPEGE_SUBSAMPLE_420;
87+
}
88+
89+
/**
90+
* Allocate buffer for decoding
91+
*/
92+
Exception& allocate(size_t numBytes) {
93+
bytes = eloq::realloc<uint8_t>(bytes, numBytes);
94+
_numBytes = numBytes;
95+
96+
return bytes == NULL ? exception.set("Can't allocate memory") : exception.clear();
97+
}
98+
99+
/**
100+
* Encode raw pixels
101+
*/
102+
Exception& encode(uint8_t *pixels, uint16_t width, uint16_t height) {
103+
benchmark.benchmark([this, pixels, width, height]() {
104+
JPEGENCODE enc;
105+
106+
if (JPEGE_SUCCESS != jpeg.open(bytes, _numBytes)) {
107+
exception.set("Cannot open JPEG");
108+
return;
109+
}
110+
111+
if (JPEGE_SUCCESS != jpeg.encodeBegin(&enc, width, height, _pixelType, _subsample, _quality)) {
112+
exception.set("Cannot setup encoding");
113+
return;
114+
}
115+
116+
if (JPEGE_SUCCESS != jpeg.addFrame(&enc, pixels, width * bpp())) {
117+
exception.set("Cannot add frame");
118+
return;
119+
}
120+
121+
_length = jpeg.close();
122+
});
123+
124+
return _length > 0 ? exception.clear() : exception.set("Empty output");
125+
}
126+
127+
/**
128+
* Encode current frame
129+
*/
130+
Exception& encode() {
131+
if (!camera.hasFrame())
132+
return exception.set("Can't encode empty frame");
133+
134+
return this->encode(camera.frame->buf, camera.frame->width, camera.frame->height);
135+
}
136+
137+
/**
138+
* Get JPEG size
139+
*/
140+
inline size_t size() {
141+
return _length;
142+
}
143+
144+
/**
145+
* Bytes per pixel
146+
*/
147+
inline uint8_t bpp() {
148+
return _pixelType == JPEGE_PIXEL_GRAYSCALE ? 1 : 2;
149+
}
150+
151+
protected:
152+
size_t _length;
153+
size_t _numBytes;
154+
uint8_t _pixelType;
155+
uint8_t _quality;
156+
uint8_t _subsample;
157+
};
158+
}
159+
}
160+
}
161+
162+
namespace eloq {
163+
static Eloquent::Esp32cam::JPEG::JPEGENCWrapper jpegenc;
164+
}
165+
#endif
166+
167+
#endif

src/eloquent_esp32cam/jpegenc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
#include "./jpeg/JPEGENCWrapper.h"

src/eloquent_esp32cam/mem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ namespace eloq {
3838
*/
3939
template<typename T>
4040
T* realloc(T* existing, size_t size) {
41+
if (existing == NULL)
42+
return alloc<T>(size);
43+
4144
if (psramFound())
4245
return zero(static_cast<T*>(ps_realloc(existing, size)), size);
4346

0 commit comments

Comments
 (0)