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