blob: 9d46895af17a2510506a287e80178b88f0c52cc5 [file] [log] [blame]
Parker Schuh44f86922017-01-03 23:59:50 -08001#include "aos/vision/image/jpeg_routines.h"
2
Parker Schuh44f86922017-01-03 23:59:50 -08003#include <sys/mman.h>
4#include <unistd.h>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07005
6#include <cerrno>
7#include <csetjmp>
8#include <cstdint>
9#include <cstdio>
10#include <cstdlib>
Parker Schuh44f86922017-01-03 23:59:50 -080011#include <cstring>
12
Parker Schuh44f86922017-01-03 23:59:50 -080013#include "third_party/libjpeg/jpeglib.h"
14
Philipp Schrader790cb542023-07-05 21:06:52 -070015#include "aos/logging/logging.h"
16
Stephan Pleinesf63bde82024-01-13 15:59:33 -080017namespace aos::vision {
Parker Schuh44f86922017-01-03 23:59:50 -080018
19namespace {
20
21void decompress_add_huff_table(j_decompress_ptr cinfo, JHUFF_TBL **htblptr,
22 const UINT8 *bits, const UINT8 *val);
23
24void standard_huff_tables(j_decompress_ptr cinfo);
25
26// Error handling form libjpeg
27struct JpegErrorManager {
28 /// "public" fields
29 struct jpeg_error_mgr pub;
30 // for return to caller
31 jmp_buf setjmp_buffer;
32};
33
34char JpegLastErrorMsg[JMSG_LENGTH_MAX];
35
36// TODO(parker): Error handling needs to be investigated bettter.
37void JpegErrorExit(j_common_ptr cinfo) {
38 JpegErrorManager myerr;
39 // cinfo->err actually points to a JpegErrorManager struct
40 ::std::memcpy(&myerr, cinfo->err, sizeof(myerr));
41 // JpegErrorManager* myerr = (JpegErrorManager*) cinfo->err;
42 // note : *(cinfo->err) is now equivalent to myerr->pub
43
44 // output_message is a method to print an error message
45 //(* (cinfo->err->output_message) ) (cinfo);
46
47 // Create the message
48 (*(cinfo->err->format_message))(cinfo, JpegLastErrorMsg);
49
50 // Jump to the setjmp point
51 longjmp(myerr.setjmp_buffer, 1);
52}
53
54// This is also adapted from libjpeg to be used on decompression tables rather
55// than compression tables as it was originally intended.
56void decompress_add_huff_table(j_decompress_ptr cinfo, JHUFF_TBL **htblptr,
57 const UINT8 *bits, const UINT8 *val) {
58 if (*htblptr == NULL) *htblptr = jpeg_alloc_huff_table((j_common_ptr)cinfo);
59
60 // Copy the number-of-symbols-of-each-code-length counts.
61 memcpy((*htblptr)->bits, bits, sizeof((*htblptr)->bits));
62
63 // Validate the counts. We do this here mainly so we can copy the right
64 // number of symbols from the val[] array, without risking marching off
65 // the end of memory. jchuff.c will do a more thorough test later.
66 int nsymbols = 0;
67 for (int len = 1; len <= 16; len++) nsymbols += bits[len];
68 if (nsymbols < 1 || nsymbols > 256) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070069 AOS_LOG(FATAL, "%s:%d: Error, bad huffman table", __FILE__, __LINE__);
Parker Schuh44f86922017-01-03 23:59:50 -080070 }
71
72 memcpy((*htblptr)->huffval, val, nsymbols * sizeof(uint8_t));
73}
74
75// standard_huff_tables is taken from libjpeg compression stuff
76// and is here used to set up the same tables in the decompression structure.
77// Set up the standard Huffman tables (cf. JPEG standard section K.3)
78// IMPORTANT: these are only valid for 8-bit data precision!
79void standard_huff_tables(j_decompress_ptr cinfo) {
80 /* 0-base on first 0, */
81 static const UINT8 bits_dc_luminance[17] = {0, 0, 1, 5, 1, 1, 1, 1, 1,
82 1, 0, 0, 0, 0, 0, 0, 0};
83 static const UINT8 val_dc_luminance[] = {0, 1, 2, 3, 4, 5,
84 6, 7, 8, 9, 10, 11};
85
86 /* 0-base on first 0 */
87 static const UINT8 bits_dc_chrominance[17] = {0, 0, 3, 1, 1, 1, 1, 1, 1,
88 1, 1, 1, 0, 0, 0, 0, 0};
89 static const UINT8 val_dc_chrominance[] = {0, 1, 2, 3, 4, 5,
90 6, 7, 8, 9, 10, 11};
91
92 /* 0-base on first 0 */
93 static const UINT8 bits_ac_luminance[17] = {0, 0, 2, 1, 3, 3, 2, 4, 3,
94 5, 5, 4, 4, 0, 0, 1, 0x7d};
95 static const UINT8 val_ac_luminance[] = {
96 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
97 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
98 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
99 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
100 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
101 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
102 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
103 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
104 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
105 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
106 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
107 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
108 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4,
109 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
110
111 /* 0-base on first 0 */
112 static const UINT8 bits_ac_chrominance[17] = {0, 0, 2, 1, 2, 4, 4, 3, 4,
113 7, 5, 4, 4, 0, 1, 2, 0x77};
114 static const UINT8 val_ac_chrominance[] = {
115 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
116 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
117 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
118 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
119 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
120 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
121 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
122 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
123 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
124 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
125 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
126 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
127 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
128 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
129
130 decompress_add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0],
131 bits_dc_luminance, val_dc_luminance);
132 decompress_add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0],
133 bits_ac_luminance, val_ac_luminance);
134 decompress_add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1],
135 bits_dc_chrominance, val_dc_chrominance);
136 decompress_add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1],
137 bits_ac_chrominance, val_ac_chrominance);
138}
139
140void local_emit_message(jpeg_common_struct * /*cinfo*/, int /*msg_level*/) {
141 return;
142}
143
144} // namespace
145
146ImageFormat GetFmt(DataRef data) {
147 ImageFormat fmt;
148 struct jpeg_decompress_struct cinfo;
149 struct jpeg_error_mgr jerr;
150
151 cinfo.err = jpeg_std_error(&jerr);
152 jerr.emit_message = local_emit_message;
153
154 cinfo.out_color_space = JCS_RGB;
155 jpeg_create_decompress(&cinfo);
156
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700157 jpeg_mem_src(
158 &cinfo,
159 reinterpret_cast<unsigned char *>(const_cast<char *>(data.data())),
160 data.size());
Parker Schuh44f86922017-01-03 23:59:50 -0800161
162 jpeg_read_header(&cinfo, TRUE);
163 fmt.w = cinfo.image_width;
164 fmt.h = cinfo.image_height;
165 jpeg_destroy_decompress(&cinfo);
166 return fmt;
167}
168
169// Returns true if successful false if an error was encountered.
170bool ProcessJpeg(DataRef data, PixelRef *out) {
171 /*
172 TODO(parker): sort of error handling.
173 struct jpeg_decompress_struct cinfo;
174 struct jpeg_error_mgr jerr;
175
176 cinfo.err = jpeg_std_error( &jerr );
177 jerr.emit_message = local_emit_message;
178
179 cinfo.out_color_space = JCS_RGB;
180 jpeg_create_decompress( &cinfo );
181 */
182
183 static bool lost_camera_connect = false;
184 struct jpeg_decompress_struct cinfo;
185
186 // We set up the normal JPEG error routines, then override error_exit.
187 JpegErrorManager jerr;
188 cinfo.err = jpeg_std_error(&jerr.pub);
189 jerr.pub.emit_message = local_emit_message;
190 jerr.pub.error_exit = JpegErrorExit;
191 // Establish the setjmp return context for my_error_exit to use.
192 if (setjmp(jerr.setjmp_buffer)) {
193 // If we get here, the JPEG code has signaled an error.
194 if (!lost_camera_connect) {
195 printf(
196 "Lost camera connection in process_jpeg.\nLooking for reconnect "
197 "...\n");
198 fflush(stdout);
199 lost_camera_connect = true;
200 }
201 jpeg_destroy_decompress(&cinfo);
202 return false;
203 }
204
205 cinfo.out_color_space = JCS_RGB;
206 jpeg_create_decompress(&cinfo);
207
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700208 jpeg_mem_src(
209 &cinfo,
210 reinterpret_cast<unsigned char *>(const_cast<char *>(data.data())),
211 data.size());
Parker Schuh44f86922017-01-03 23:59:50 -0800212
213 jpeg_read_header(&cinfo, TRUE);
214 standard_huff_tables(&cinfo);
215
216 /*printf( "JPEG File Information: \n" );
217 printf( "Image width and height: %d pixels and %d pixels.\n",
218 cinfo.image_width, cinfo.image_height );
219 printf( "Color components per pixel: %d.\n", cinfo.num_components );
220 printf( "Color space: %d.\n", cinfo.jpeg_color_space );
221 printf("JpegDecompressed\n");*/
222
223 jpeg_start_decompress(&cinfo);
224
225 int offset = 0;
226 int step = cinfo.num_components * cinfo.image_width;
227 unsigned char *buffers[cinfo.image_height];
228 for (size_t i = 0; i < cinfo.image_height; ++i) {
Parker Schuhd7db83d2017-02-08 20:49:15 -0800229 buffers[i] = &reinterpret_cast<unsigned char *>(out)[offset];
Parker Schuh44f86922017-01-03 23:59:50 -0800230 offset += step;
231 }
232
233 while (cinfo.output_scanline < cinfo.image_height) {
234 jpeg_read_scanlines(&cinfo, &buffers[cinfo.output_scanline],
235 cinfo.image_height - cinfo.output_scanline);
236 }
237
238 jpeg_finish_decompress(&cinfo);
239 jpeg_destroy_decompress(&cinfo);
240
241 if (lost_camera_connect) {
242 printf("Camera connection restablished.\n");
243 fflush(stdout);
244 lost_camera_connect = false;
245 }
246
247 return true;
248}
249
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800250} // namespace aos::vision