1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4 
5    This file is part of the SANE package.
6 
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 */
20 
21 #ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H
22 #define BACKEND_GENESYS_IMAGE_PIPELINE_H
23 
24 #include "image.h"
25 #include "image_pixel.h"
26 #include "image_buffer.h"
27 
28 #include <algorithm>
29 #include <functional>
30 #include <memory>
31 
32 namespace genesys {
33 
34 class ImagePipelineNode
35 {
36 public:
37     virtual ~ImagePipelineNode();
38 
39     virtual std::size_t get_width() const = 0;
40     virtual std::size_t get_height() const = 0;
41     virtual PixelFormat get_format() const = 0;
42 
get_row_bytes() const43     std::size_t get_row_bytes() const
44     {
45         return get_pixel_row_bytes(get_format(), get_width());
46     }
47 
48     virtual bool eof() const = 0;
49 
50     // returns true if the row was filled successfully, false otherwise (e.g. if not enough data
51     // was available.
52     virtual bool get_next_row_data(std::uint8_t* out_data) = 0;
53 };
54 
55 // A pipeline node that produces data from a callable
56 class ImagePipelineNodeCallableSource : public ImagePipelineNode
57 {
58 public:
59     using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>;
60 
ImagePipelineNodeCallableSource(std::size_t width, std::size_t height, PixelFormat format, ProducerCallback producer)61     ImagePipelineNodeCallableSource(std::size_t width, std::size_t height, PixelFormat format,
62                                     ProducerCallback producer) :
63         producer_{producer},
64         width_{width},
65         height_{height},
66         format_{format}
67     {}
68 
69     std::size_t get_width() const override { return width_; }
70     std::size_t get_height() const override { return height_; }
71     PixelFormat get_format() const override { return format_; }
72 
73     bool eof() const override { return eof_; }
74 
75     bool get_next_row_data(std::uint8_t* out_data) override;
76 
77 private:
78     ProducerCallback producer_;
79     std::size_t width_ = 0;
80     std::size_t height_ = 0;
81     PixelFormat format_ = PixelFormat::UNKNOWN;
82     bool eof_ = false;
83 };
84 
85 // A pipeline node that produces data from a callable requesting fixed-size chunks.
86 class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNode
87 {
88 public:
89     using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>;
90 
91     ImagePipelineNodeBufferedCallableSource(std::size_t width, std::size_t height,
92                                             PixelFormat format, std::size_t input_batch_size,
93                                             ProducerCallback producer);
94 
95     std::size_t get_width() const override { return width_; }
96     std::size_t get_height() const override { return height_; }
97     PixelFormat get_format() const override { return format_; }
98 
99     bool eof() const override { return eof_; }
100 
101     bool get_next_row_data(std::uint8_t* out_data) override;
102 
remaining_bytes() const103     std::size_t remaining_bytes() const { return buffer_.remaining_size(); }
set_remaining_bytes(std::size_t bytes)104     void set_remaining_bytes(std::size_t bytes) { buffer_.set_remaining_size(bytes); }
set_last_read_multiple(std::size_t bytes)105     void set_last_read_multiple(std::size_t bytes) { buffer_.set_last_read_multiple(bytes); }
106 
107 private:
108     ProducerCallback producer_;
109     std::size_t width_ = 0;
110     std::size_t height_ = 0;
111     PixelFormat format_ = PixelFormat::UNKNOWN;
112 
113     bool eof_ = false;
114     std::size_t curr_row_ = 0;
115 
116     ImageBuffer buffer_;
117 };
118 
119 // A pipeline node that produces data from the given array.
120 class ImagePipelineNodeArraySource : public ImagePipelineNode
121 {
122 public:
123     ImagePipelineNodeArraySource(std::size_t width, std::size_t height, PixelFormat format,
124                                  std::vector<std::uint8_t> data);
125 
126     std::size_t get_width() const override { return width_; }
127     std::size_t get_height() const override { return height_; }
128     PixelFormat get_format() const override { return format_; }
129 
130     bool eof() const override { return eof_; }
131 
132     bool get_next_row_data(std::uint8_t* out_data) override;
133 
134 private:
135     std::size_t width_ = 0;
136     std::size_t height_ = 0;
137     PixelFormat format_ = PixelFormat::UNKNOWN;
138 
139     bool eof_ = false;
140 
141     std::vector<std::uint8_t> data_;
142     std::size_t next_row_ = 0;
143 };
144 
145 
146 /// A pipeline node that produces data from the given image
147 class ImagePipelineNodeImageSource : public ImagePipelineNode
148 {
149 public:
150     ImagePipelineNodeImageSource(const Image& source);
151 
152     std::size_t get_width() const override { return source_.get_width(); }
153     std::size_t get_height() const override { return source_.get_height(); }
154     PixelFormat get_format() const override { return source_.get_format(); }
155 
156     bool eof() const override { return next_row_ >= get_height(); }
157 
158     bool get_next_row_data(std::uint8_t* out_data) override;
159 
160 private:
161     const Image& source_;
162     std::size_t next_row_ = 0;
163 };
164 
165 // A pipeline node that converts between pixel formats
166 class ImagePipelineNodeFormatConvert : public ImagePipelineNode
167 {
168 public:
ImagePipelineNodeFormatConvert(ImagePipelineNode& source, PixelFormat dst_format)169     ImagePipelineNodeFormatConvert(ImagePipelineNode& source, PixelFormat dst_format) :
170         source_(source),
171         dst_format_{dst_format}
172     {}
173 
174     ~ImagePipelineNodeFormatConvert() override = default;
175 
176     std::size_t get_width() const override { return source_.get_width(); }
177     std::size_t get_height() const override { return source_.get_height(); }
178     PixelFormat get_format() const override { return dst_format_; }
179 
180     bool eof() const override { return source_.eof(); }
181 
182     bool get_next_row_data(std::uint8_t* out_data) override;
183 
184 private:
185     ImagePipelineNode& source_;
186     PixelFormat dst_format_;
187     std::vector<std::uint8_t> buffer_;
188 };
189 
190 // A pipeline node that handles data that comes out of segmented sensors. Note that the width of
191 // the output data does not necessarily match the input data width, because in many cases almost
192 // all width of the image needs to be read in order to desegment it.
193 class ImagePipelineNodeDesegment : public ImagePipelineNode
194 {
195 public:
196     ImagePipelineNodeDesegment(ImagePipelineNode& source,
197                                std::size_t output_width,
198                                const std::vector<unsigned>& segment_order,
199                                std::size_t segment_pixels,
200                                std::size_t interleaved_lines,
201                                std::size_t pixels_per_chunk);
202 
203     ImagePipelineNodeDesegment(ImagePipelineNode& source,
204                                std::size_t output_width,
205                                std::size_t segment_count,
206                                std::size_t segment_pixels,
207                                std::size_t interleaved_lines,
208                                std::size_t pixels_per_chunk);
209 
210     ~ImagePipelineNodeDesegment() override = default;
211 
212     std::size_t get_width() const override { return output_width_; }
213     std::size_t get_height() const override { return source_.get_height() / interleaved_lines_; }
214     PixelFormat get_format() const override { return source_.get_format(); }
215 
216     bool eof() const override { return source_.eof(); }
217 
218     bool get_next_row_data(std::uint8_t* out_data) override;
219 
220 private:
221     ImagePipelineNode& source_;
222     std::size_t output_width_;
223     std::vector<unsigned> segment_order_;
224     std::size_t segment_pixels_ = 0;
225     std::size_t interleaved_lines_ = 0;
226     std::size_t pixels_per_chunk_ = 0;
227 
228     RowBuffer buffer_;
229 };
230 
231 // A pipeline node that deinterleaves data on multiple lines
232 class ImagePipelineNodeDeinterleaveLines : public ImagePipelineNodeDesegment
233 {
234 public:
235     ImagePipelineNodeDeinterleaveLines(ImagePipelineNode& source,
236                                        std::size_t interleaved_lines,
237                                        std::size_t pixels_per_chunk);
238 };
239 
240 // A pipeline that swaps bytes in 16-bit components and does nothing otherwise.
241 class ImagePipelineNodeSwap16BitEndian : public ImagePipelineNode
242 {
243 public:
244     ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source);
245 
246     std::size_t get_width() const override { return source_.get_width(); }
247     std::size_t get_height() const override { return source_.get_height(); }
248     PixelFormat get_format() const override { return source_.get_format(); }
249 
250     bool eof() const override { return source_.eof(); }
251 
252     bool get_next_row_data(std::uint8_t* out_data) override;
253 
254 private:
255     ImagePipelineNode& source_;
256     bool needs_swapping_ = false;
257 };
258 
259 class ImagePipelineNodeInvert : public ImagePipelineNode
260 {
261 public:
262     ImagePipelineNodeInvert(ImagePipelineNode& source);
263 
264     std::size_t get_width() const override { return source_.get_width(); }
265     std::size_t get_height() const override { return source_.get_height(); }
266     PixelFormat get_format() const override { return source_.get_format(); }
267 
268     bool eof() const override { return source_.eof(); }
269 
270     bool get_next_row_data(std::uint8_t* out_data) override;
271 
272 private:
273     ImagePipelineNode& source_;
274 };
275 
276 // A pipeline node that merges 3 mono lines into a color channel
277 class ImagePipelineNodeMergeMonoLinesToColor : public ImagePipelineNode
278 {
279 public:
280     ImagePipelineNodeMergeMonoLinesToColor(ImagePipelineNode& source,
281                                            ColorOrder color_order);
282 
283     std::size_t get_width() const override { return source_.get_width(); }
284     std::size_t get_height() const override { return source_.get_height() / 3; }
285     PixelFormat get_format() const override { return output_format_; }
286 
287     bool eof() const override { return source_.eof(); }
288 
289     bool get_next_row_data(std::uint8_t* out_data) override;
290 
291 private:
292     static PixelFormat get_output_format(PixelFormat input_format, ColorOrder order);
293 
294     ImagePipelineNode& source_;
295     PixelFormat output_format_ = PixelFormat::UNKNOWN;
296 
297     RowBuffer buffer_;
298 };
299 
300 // A pipeline node that splits a color channel into 3 mono lines
301 class ImagePipelineNodeSplitMonoLines : public ImagePipelineNode
302 {
303 public:
304     ImagePipelineNodeSplitMonoLines(ImagePipelineNode& source);
305 
306     std::size_t get_width() const override { return source_.get_width(); }
307     std::size_t get_height() const override { return source_.get_height() * 3; }
308     PixelFormat get_format() const override { return output_format_; }
309 
310     bool eof() const override { return source_.eof(); }
311 
312     bool get_next_row_data(std::uint8_t* out_data) override;
313 
314 private:
315     static PixelFormat get_output_format(PixelFormat input_format);
316 
317     ImagePipelineNode& source_;
318     PixelFormat output_format_ = PixelFormat::UNKNOWN;
319 
320     std::vector<std::uint8_t> buffer_;
321     unsigned next_channel_ = 0;
322 };
323 
324 
325 // A pipeline node that merges 3 mono lines into a gray channel
326 class ImagePipelineNodeMergeColorToGray : public ImagePipelineNode
327 {
328 public:
329     ImagePipelineNodeMergeColorToGray(ImagePipelineNode& source);
330 
331     std::size_t get_width() const override { return source_.get_width(); }
332     std::size_t get_height() const override { return source_.get_height(); }
333     PixelFormat get_format() const override { return output_format_; }
334 
335     bool eof() const override { return source_.eof(); }
336 
337     bool get_next_row_data(std::uint8_t* out_data) override;
338 
339 private:
340     static PixelFormat get_output_format(PixelFormat input_format);
341 
342     ImagePipelineNode& source_;
343     PixelFormat output_format_ = PixelFormat::UNKNOWN;
344     float ch0_mult_ = 0;
345     float ch1_mult_ = 0;
346     float ch2_mult_ = 0;
347 
348     std::vector<std::uint8_t> temp_buffer_;
349 };
350 
351 // A pipeline node that shifts colors across lines by the given offsets
352 class ImagePipelineNodeComponentShiftLines : public ImagePipelineNode
353 {
354 public:
355     ImagePipelineNodeComponentShiftLines(ImagePipelineNode& source,
356                                          unsigned shift_r, unsigned shift_g, unsigned shift_b);
357 
358     std::size_t get_width() const override { return source_.get_width(); }
359     std::size_t get_height() const override { return height_; }
360     PixelFormat get_format() const override { return source_.get_format(); }
361 
362     bool eof() const override { return source_.eof(); }
363 
364     bool get_next_row_data(std::uint8_t* out_data) override;
365 
366 private:
367     ImagePipelineNode& source_;
368     std::size_t extra_height_ = 0;
369     std::size_t height_ = 0;
370 
371     std::array<unsigned, 3> channel_shifts_;
372 
373     RowBuffer buffer_;
374 };
375 
376 // A pipeline node that shifts pixels across lines by the given offsets (performs vertical
377 // unstaggering)
378 class ImagePipelineNodePixelShiftLines : public ImagePipelineNode
379 {
380 public:
381     ImagePipelineNodePixelShiftLines(ImagePipelineNode& source,
382                                      const std::vector<std::size_t>& shifts);
383 
384     std::size_t get_width() const override { return source_.get_width(); }
385     std::size_t get_height() const override { return height_; }
386     PixelFormat get_format() const override { return source_.get_format(); }
387 
388     bool eof() const override { return source_.eof(); }
389 
390     bool get_next_row_data(std::uint8_t* out_data) override;
391 
392 private:
393     ImagePipelineNode& source_;
394     std::size_t extra_height_ = 0;
395     std::size_t height_ = 0;
396 
397     std::vector<std::size_t> pixel_shifts_;
398 
399     RowBuffer buffer_;
400 };
401 
402 // A pipeline node that shifts pixels across columns by the given offsets. Each row is divided
403 // into pixel groups of shifts.size() pixels. For each output group starting at position xgroup,
404 // the i-th pixel will be set to the input pixel at position xgroup + shifts[i].
405 class ImagePipelineNodePixelShiftColumns : public ImagePipelineNode
406 {
407 public:
408     ImagePipelineNodePixelShiftColumns(ImagePipelineNode& source,
409                                        const std::vector<std::size_t>& shifts);
410 
411     std::size_t get_width() const override { return width_; }
412     std::size_t get_height() const override { return source_.get_height(); }
413     PixelFormat get_format() const override { return source_.get_format(); }
414 
415     bool eof() const override { return source_.eof(); }
416 
417     bool get_next_row_data(std::uint8_t* out_data) override;
418 
419 private:
420     ImagePipelineNode& source_;
421     std::size_t width_ = 0;
422     std::size_t extra_width_ = 0;
423 
424     std::vector<std::size_t> pixel_shifts_;
425 
426     std::vector<std::uint8_t> temp_buffer_;
427 };
428 
429 // exposed for tests
430 std::size_t compute_pixel_shift_extra_width(std::size_t source_width,
431                                             const std::vector<std::size_t>& shifts);
432 
433 // A pipeline node that extracts a sub-image from the image. Padding and cropping is done as needed.
434 // The class can't pad to the left of the image currently, as only positive offsets are accepted.
435 class ImagePipelineNodeExtract : public ImagePipelineNode
436 {
437 public:
438     ImagePipelineNodeExtract(ImagePipelineNode& source,
439                              std::size_t offset_x, std::size_t offset_y,
440                              std::size_t width, std::size_t height);
441 
442     ~ImagePipelineNodeExtract() override;
443 
444     std::size_t get_width() const override { return width_; }
445     std::size_t get_height() const override { return height_; }
446     PixelFormat get_format() const override { return source_.get_format(); }
447 
448     bool eof() const override { return source_.eof(); }
449 
450     bool get_next_row_data(std::uint8_t* out_data) override;
451 
452 private:
453     ImagePipelineNode& source_;
454     std::size_t offset_x_ = 0;
455     std::size_t offset_y_ = 0;
456     std::size_t width_ = 0;
457     std::size_t height_ = 0;
458 
459     std::size_t current_line_ = 0;
460     std::vector<std::uint8_t> cached_line_;
461 };
462 
463 // A pipeline node that scales rows to the specified width by using a point filter
464 class ImagePipelineNodeScaleRows : public ImagePipelineNode
465 {
466 public:
467     ImagePipelineNodeScaleRows(ImagePipelineNode& source, std::size_t width);
468 
469     std::size_t get_width() const override { return width_; }
470     std::size_t get_height() const override { return source_.get_height(); }
471     PixelFormat get_format() const override { return source_.get_format(); }
472 
473     bool eof() const override { return source_.eof(); }
474 
475     bool get_next_row_data(std::uint8_t* out_data) override;
476 
477 private:
478     ImagePipelineNode& source_;
479     std::size_t width_ = 0;
480 
481     std::vector<std::uint8_t> cached_line_;
482 };
483 
484 // A pipeline node that mimics the calibration behavior on Genesys chips
485 class ImagePipelineNodeCalibrate : public ImagePipelineNode
486 {
487 public:
488 
489     ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector<std::uint16_t>& bottom,
490                                const std::vector<std::uint16_t>& top, std::size_t x_start);
491 
492     std::size_t get_width() const override { return source_.get_width(); }
493     std::size_t get_height() const override { return source_.get_height(); }
494     PixelFormat get_format() const override { return source_.get_format(); }
495 
496     bool eof() const override { return source_.eof(); }
497 
498     bool get_next_row_data(std::uint8_t* out_data) override;
499 
500 private:
501     ImagePipelineNode& source_;
502 
503     std::vector<float> offset_;
504     std::vector<float> multiplier_;
505 };
506 
507 class ImagePipelineNodeDebug : public ImagePipelineNode
508 {
509 public:
510     ImagePipelineNodeDebug(ImagePipelineNode& source, const std::string& path);
511     ~ImagePipelineNodeDebug() override;
512 
513     std::size_t get_width() const override { return source_.get_width(); }
514     std::size_t get_height() const override { return source_.get_height(); }
515     PixelFormat get_format() const override { return source_.get_format(); }
516 
517     bool eof() const override { return source_.eof(); }
518 
519     bool get_next_row_data(std::uint8_t* out_data) override;
520 
521 private:
522     ImagePipelineNode& source_;
523     std::string path_;
524     RowBuffer buffer_;
525 };
526 
527 class ImagePipelineStack
528 {
529 public:
ImagePipelineStack()530     ImagePipelineStack() {}
ImagePipelineStack(ImagePipelineStack&& other)531     ImagePipelineStack(ImagePipelineStack&& other)
532     {
533         clear();
534         nodes_ = std::move(other.nodes_);
535     }
536 
operator =(ImagePipelineStack&& other)537     ImagePipelineStack& operator=(ImagePipelineStack&& other)
538     {
539         clear();
540         nodes_ = std::move(other.nodes_);
541         return *this;
542     }
543 
~ImagePipelineStack()544     ~ImagePipelineStack() { clear(); }
545 
546     std::size_t get_input_width() const;
547     std::size_t get_input_height() const;
548     PixelFormat get_input_format() const;
549     std::size_t get_input_row_bytes() const;
550 
551     std::size_t get_output_width() const;
552     std::size_t get_output_height() const;
553     PixelFormat get_output_format() const;
554     std::size_t get_output_row_bytes() const;
555 
front()556     ImagePipelineNode& front() { return *(nodes_.front().get()); }
557 
eof() const558     bool eof() const { return nodes_.back()->eof(); }
559 
560     void clear();
561 
562     template<class Node, class... Args>
push_first_node(Args&&.... args)563     Node& push_first_node(Args&&... args)
564     {
565         if (!nodes_.empty()) {
566             throw SaneException("Trying to append first node when there are existing nodes");
567         }
568         nodes_.emplace_back(std::unique_ptr<Node>(new Node(std::forward<Args>(args)...)));
569         return static_cast<Node&>(*nodes_.back());
570     }
571 
572     template<class Node, class... Args>
push_node(Args&&.... args)573     Node& push_node(Args&&... args)
574     {
575         ensure_node_exists();
576         nodes_.emplace_back(std::unique_ptr<Node>(new Node(*nodes_.back(),
577                                                            std::forward<Args>(args)...)));
578         return static_cast<Node&>(*nodes_.back());
579     }
580 
get_next_row_data(std::uint8_t* out_data)581     bool get_next_row_data(std::uint8_t* out_data)
582     {
583         return nodes_.back()->get_next_row_data(out_data);
584     }
585 
586     std::vector<std::uint8_t> get_all_data();
587 
588     Image get_image();
589 
590 private:
591     void ensure_node_exists() const;
592 
593     std::vector<std::unique_ptr<ImagePipelineNode>> nodes_;
594 };
595 
596 } // namespace genesys
597 
598 #endif // ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H
599