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