HoviTron Video Pipeline
image_loading.cpp
1/* The copyright in this software is being made available under the BSD
2* License, included below. This software may be subject to other third party
3* and contributor rights, including patent rights, and no such rights are
4* granted under this license.
5*
6* Copyright (c) 2010-2018, ITU/ISO/IEC
7* All rights reserved.
8*
9* Redistribution and use in source and binary forms, with or without
10* modification, are permitted provided that the following conditions are met:
11*
12* * Redistributions of source code must retain the above copyright notice,
13* this list of conditions and the following disclaimer.
14* * Redistributions in binary form must reproduce the above copyright notice,
15* this list of conditions and the following disclaimer in the documentation
16* and/or other materials provided with the distribution.
17* * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
18* be used to endorse or promote products derived from this software without
19* specific prior written permission.
20*
21* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31* THE POSSIBILITY OF SUCH DAMAGE.
32*/
33
34/*
35Original authors:
36
37Universite Libre de Bruxelles, Brussels, Belgium:
38 Sarah Fachada, Sarah.Fernandes.Pinto.Fachada@ulb.ac.be
39 Daniele Bonatto, Daniele.Bonatto@ulb.ac.be
40 Arnaud Schenkel, arnaud.schenkel@ulb.ac.be
41
42Koninklijke Philips N.V., Eindhoven, The Netherlands:
43 Bart Kroon, bart.kroon@philips.com
44 Bart Sonneveldt, bart.sonneveldt@philips.com
45*/
46
47#include "../include/image_loading.h"
48#include "../include/Config.h"
49
50#include <opencv2/imgproc.hpp>
51#include <opencv2/imgcodecs.hpp>
52
53#include <fstream>
54#include <iostream>
55#include <stdexcept>
56#include <limits>
57
58namespace rvs
59{
60 namespace
61 {
62 using detail::ColorSpace;
63 using detail::g_color_space;
64
65 void read_raw(std::ifstream& stream, cv::Mat image)
66 {
67 CV_Assert(stream.good() && !image.empty() && image.isContinuous());
68 stream.read(reinterpret_cast<char*>(image.data), image.size().area() * image.elemSize());
69 }
70
71 cv::Mat read_color_YUV(std::string filepath, int frame, Parameters const& parameters) {
72 auto size = parameters.getPaddedSize();
73 auto bit_depth = parameters.getColorBitDepth();
74 auto type = CV_MAKETYPE(cvdepth_from_bit_depth(bit_depth), 1);
75 cv::Mat y_channel(size, type);
76 cv::Mat cb_channel(size / 2, type);
77 cv::Mat cr_channel(size / 2, type);
78
79 std::ifstream stream(filepath, std::ios::binary);
80 if (!stream.good()) {
81 std::ostringstream what;
82 what << "Failed to read raw YUV color file \"" << filepath << "\"";
83 throw std::runtime_error(what.str());
84 }
85 stream.seekg(size.area() * y_channel.elemSize() * 3 / 2 * frame);
86 read_raw(stream, y_channel);
87 read_raw(stream, cb_channel);
88 read_raw(stream, cr_channel);
89
90 cv::resize(cb_channel, cb_channel, size, 0, 0, cv::INTER_CUBIC);
91 cv::resize(cr_channel, cr_channel, size, 0, 0, cv::INTER_CUBIC);
92
93 cv::Mat image(size, CV_MAKETYPE(cvdepth_from_bit_depth(bit_depth), 3));
94 cv::Mat src[] = { y_channel, cr_channel, cb_channel };
95 cv::merge(src, 3, image);
96 return image;
97 }
98
99 cv::Mat read_depth_YUV(std::string filepath, int frame, Parameters const& parameters) {
100 auto size = parameters.getPaddedSize();
101 auto bit_depth = parameters.getDepthBitDepth();
102 cv::Mat image(size, CV_MAKETYPE(cvdepth_from_bit_depth(bit_depth), 1));
103 std::ifstream stream(filepath, std::ios_base::binary);
104 if (!stream.good()) {
105 std::ostringstream what;
106 what << "Failed to read raw YUV depth file \"" << filepath << "\"";
107 throw std::runtime_error(what.str());
108 }
109
110 switch (parameters.getDepthColorFormat()) {
111 case ColorFormat::YUV420:
112 stream.seekg(size.area() * image.elemSize() * 3 / 2 * frame);
113 break;
114 case ColorFormat::YUV400:
115 stream.seekg(size.area() * image.elemSize() * frame);
116 break;
117 default:
118 throw std::logic_error("Unknown depth map color format");
119 }
120
121 read_raw(stream, image);
122 return image;
123 }
124
125 cv::Mat read_color_RGB(std::string filepath, Parameters const& parameters) {
126 cv::Mat image = cv::imread(filepath, cv::IMREAD_UNCHANGED);
127
128 if (image.empty())
129 throw std::runtime_error("Failed to read color file");
130 if (image.size() != parameters.getPaddedSize())
131 throw std::runtime_error("Color file does not have the expected size");
132 if (image.depth() != cvdepth_from_bit_depth(parameters.getColorBitDepth()))
133 throw std::runtime_error("Color file has wrong bit depth");
134 if (image.channels() != 3)
135 throw std::runtime_error("Color file has wrong number of channels");
136
137 return image;
138 }
139
140 cv::Mat read_depth_RGB(std::string filepath, Parameters const& parameters) {
141 cv::Mat image = cv::imread(filepath, cv::IMREAD_UNCHANGED);
142
143 if (image.empty())
144 throw std::runtime_error("Failed to read depth file");
145 if (image.size() != parameters.getPaddedSize())
146 throw std::runtime_error("Depth file does not have the expected size");
147 if (image.depth() != cvdepth_from_bit_depth(parameters.getDepthBitDepth()))
148 throw std::runtime_error("Depth file has the wrong bit depth");
149 if (image.channels() != 1)
150 throw std::runtime_error("Depth file has the wrong number of channels");
151
152 return image;
153 }
154 }
155
156 int cvdepth_from_bit_depth(int bit_depth)
157 {
158 if (bit_depth >= 1 && bit_depth <= 8)
159 return CV_8U;
160 else if (bit_depth >= 9 && bit_depth <= 16)
161 return CV_16U;
162 else if (bit_depth == 32)
163 return CV_32F;
164 else throw std::invalid_argument("invalid raw image bit depth");
165 }
166
167 unsigned max_level(int bit_depth)
168 {
169 assert(bit_depth > 0 && bit_depth <= 16);
170 return (1u << bit_depth) - 1u;
171 }
172
173 cv::Mat3f read_color(std::string filepath, int frame, Parameters const& parameters)
174 {
175 // Load the image
176 cv::Mat image;
177 ColorSpace color_space;
178 if (filepath.substr(filepath.size() - 4, 4) == ".yuv") {
179 image = read_color_YUV(filepath, frame, parameters);
180 color_space = ColorSpace::YUV;
181 }
182 else if (frame == 0) {
183 image = read_color_RGB(filepath, parameters);
184 color_space = ColorSpace::RGB;
185 }
186 else {
187 throw std::runtime_error("Readig multiple frames not (yet) supported for image files");
188 }
189
190 // Crop out padded regions
191 if (parameters.getPaddedSize() != parameters.getSize()) {
192 image = image(parameters.getCropRegion()).clone();
193 }
194
195 // Normalize to [0, 1]
196 cv::Mat3f color;
197 if (image.depth() == CV_32F) {
198 color = image;
199 }
200 else {
201 image.convertTo(color, CV_32F, 1. / max_level(parameters.getColorBitDepth()));
202 }
203
204 // Color space conversion
205 if (color_space == ColorSpace::YUV && g_color_space == ColorSpace::RGB) {
206 cv::cvtColor(color, color, cv::COLOR_YCrCb2BGR);
207 }
208 else if (color_space == ColorSpace::RGB && g_color_space == ColorSpace::YUV) {
209 cv::cvtColor(color, color, cv::COLOR_BGR2YCrCb);
210 }
211
212 return color;
213 }
214
215 cv::Mat1f read_depth(std::string filepath, int frame, Parameters const& parameters)
216 {
217 // Load the image
218 cv::Mat image;
219 if (filepath.substr(filepath.size() - 4, 4) == ".yuv") {
220 image = read_depth_YUV(filepath, frame, parameters);
221 }
222 else if (frame == 0) {
223 image = read_depth_RGB(filepath, parameters);
224 }
225 else {
226 throw std::runtime_error("Readig multiple frames not (yet) supported for image files");
227 }
228
229 // Crop out padded regions
230 if (parameters.getPaddedSize() != parameters.getSize()) {
231 image = image(parameters.getCropRegion()).clone();
232 }
233
234 // Do not manipulate floating-point depth maps (e.g. OpenEXR)
235 if (image.depth() == CV_32F) {
236 return image;
237 }
238
239 // Normalize to [0, 1]
240 cv::Mat1f depth;
241 image.convertTo(depth, CV_32F, 1. / max_level(parameters.getDepthBitDepth()));
242
243 // 1000 is for 'infinitly far'
244 auto near = parameters.getDepthRange()[0];
245 auto far = parameters.getDepthRange()[1];
246 if (far >= 1000.f) {
247 depth = near / depth;
248 }
249 else {
250 depth = far * near / (near + depth * (far - near));
251 }
252
253
254 if (parameters.hasInvalidDepth()) {
255 // Level 0 is for 'invalid'
256 // Mark invalid pixels as NaN
257 auto const NaN = std::numeric_limits<float>::quiet_NaN();
258 depth.setTo(NaN, image == 0);
259 }
260
261 return depth;
262 }
263}