HoviTron Video Pipeline
image_writing.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_writing.h"
48#include "../include/image_loading.h"
49#include "../include/Config.h"
50
51#include <fstream>
52#include <iostream>
53#include <stdexcept>
54
55#include <opencv2/imgproc.hpp>
56#include <opencv2/imgcodecs.hpp>
57
58namespace rvs
59{
60 namespace
61 {
62 using detail::ColorSpace;
63 using detail::g_color_space;
64
65 void write_raw(std::ofstream& stream, cv::Mat image)
66 {
67 CV_Assert(stream.good() && !image.empty() && image.isContinuous());
68 stream.write(reinterpret_cast<char const*>(image.data), image.size().area() * image.elemSize());
69 }
70
71 void write_color_YUV(std::string filepath, cv::Mat image, int frame)
72 {
73 std::ofstream stream(filepath, frame
74 ? std::ios::binary | std::ios::app
75 : std::ios::binary);
76 if (!stream.is_open())
77 throw std::runtime_error("Failed to open YUV output image");
78
79 cv::Mat dst[3];
80 cv::split(image, dst);
81
82 cv::resize(dst[1], dst[1], cv::Size(), 0.5, 0.5, cv::INTER_CUBIC);
83 cv::resize(dst[2], dst[2], cv::Size(), 0.5, 0.5, cv::INTER_CUBIC);
84
85 write_raw(stream, dst[0]);
86 write_raw(stream, dst[1]);
87 write_raw(stream, dst[2]);
88 }
89
90 void write_depth_YUV(std::string filepath, cv::Mat image, int frame, Parameters const& parameters)
91 {
92 std::ofstream stream(filepath, frame
93 ? std::ios::binary | std::ios::app
94 : std::ios::binary);
95 if (!stream.is_open())
96 throw std::runtime_error("Failed to open YUV output image");
97
98 auto bit_depth = parameters.getDepthBitDepth();
99 auto neutral = bit_depth == 32
100 ? 0.5
101 : 0.5 * (1 + max_level(bit_depth));
102
103 write_raw(stream, image);
104
105 if (parameters.getDepthColorFormat() == ColorFormat::YUV420) {
106 auto chroma = cv::Mat(image.size() / 2, image.type(), cv::Scalar::all(neutral));
107 write_raw(stream, chroma);
108 write_raw(stream, chroma);
109 }
110 }
111
112 void write_mask_YUV(std::string filepath, cv::Mat1b image, int frame)
113 {
114 std::ofstream stream(filepath, frame
115 ? std::ios::binary | std::ios::app
116 : std::ios::binary);
117 if (!stream.is_open())
118 throw std::runtime_error("Failed to open YUV output image");
119
120 auto chroma = cv::Mat1b(image.size() / 2, 128);
121
122 write_raw(stream, image);
123 write_raw(stream, chroma);
124 write_raw(stream, chroma);
125 }
126 }
127
128 void write_color(std::string filepath, cv::Mat3f color, int frame, Parameters const& parameters)
129 {
130 // Color space conversion
131 auto color_space = filepath.substr(filepath.size() - 4, 4) == ".yuv"
132 ? ColorSpace::YUV
133 : ColorSpace::RGB;
134 if (g_color_space == ColorSpace::YUV && color_space == ColorSpace::RGB) {
135 cv::cvtColor(color, color, cv::COLOR_YUV2BGR);
136 }
137 else if (g_color_space == ColorSpace::RGB && color_space == ColorSpace::YUV) {
138 cv::cvtColor(color, color, cv::COLOR_BGR2YUV);
139 }
140
141 // Quantization
142 auto bit_depth = parameters.getColorBitDepth();
143 cv::Mat image;
144 if (bit_depth == 32 || color_space != ColorSpace::YUV) {
145 image = color;
146 }
147 else {
148 color.convertTo(image, cvdepth_from_bit_depth(bit_depth), max_level(bit_depth));
149 }
150
151 // Pad image
152 if (parameters.getPaddedSize() != parameters.getSize()) {
153 auto padded = cv::Mat(parameters.getPaddedSize(), image.type(), cv::Scalar::all(0.));
154 padded(parameters.getCropRegion()) = image;
155 image = padded;
156 }
157
158 // Write the image
159 if (color_space == ColorSpace::YUV) {
160 write_color_YUV(filepath, image, frame);
161 }
162 else if (frame == 0) {
163 image *= 255.0;
164 cv::imwrite(filepath, image);
165 }
166 else {
167 throw std::runtime_error("Writing multiple frames not (yet) supported for image files");
168 }
169 }
170
171 void write_depth(std::string filepath, cv::Mat1f depth, int frame, Parameters const& parameters)
172 {
173 write_maskedDepth(filepath, depth, cv::Mat1b(), frame, parameters);
174 }
175
176 void write_maskedDepth(std::string filepath, cv::Mat1f depth, cv::Mat1b mask, int frame, Parameters const& parameters)
177 {
178 // Clone to avoid modifying the input depth
179 depth = depth.clone();
180
181 auto bit_depth = parameters.getDepthBitDepth();
182
183 cv::Mat image;
184 if (bit_depth == 32) {
185 // Do not manipulate floating-point depth maps (e.g. OpenEXR)
186 image = depth;
187 }
188 else {
189 auto near = parameters.getDepthRange()[0];
190 auto far = parameters.getDepthRange()[1];
191
192 // 1000 is for 'infinitly far'
193 if (far >= 1000.f) {
194 depth = near / depth;
195 }
196 else {
197 depth = (far * near / depth - near) / (far - near);
198 }
199
200 if (!mask.empty()) {
201 depth.setTo(0.f, mask);
202 }
203 depth.convertTo(image, cvdepth_from_bit_depth(bit_depth), max_level(bit_depth));
204 }
205
206 // Pad image
207 if (parameters.getPaddedSize() != parameters.getSize()) {
208 auto padded = cv::Mat(parameters.getPaddedSize(), image.type(), cv::Scalar::all(0.));
209 padded(parameters.getCropRegion()) = image;
210 image = padded;
211 }
212
213 // Save the image
214 if (filepath.substr(filepath.size() - 4, 4) == ".yuv") {
215 write_depth_YUV(filepath, image, frame, parameters);
216 }
217 else if (frame == 0) {
218 cv::imwrite(filepath, image);
219 }
220 else {
221 throw std::runtime_error("Writing multiple frames not (yet) supported for image files");
222 }
223 }
224
225 void write_mask(std::string filepath, cv::Mat1b mask, int frame, Parameters const& parameters)
226 {
227 mask.setTo(255, mask);
228
229 // Pad image
230 if (parameters.getPaddedSize() != parameters.getSize()) {
231 auto padded = cv::Mat(parameters.getPaddedSize(), mask.type(), cv::Scalar::all(0.));
232 padded(parameters.getCropRegion()) = mask;
233 mask = padded;
234 }
235
236 // Save the image
237 if (filepath.substr(filepath.size() - 4, 4) == ".yuv") {
238 write_mask_YUV(filepath, mask, frame);
239 }
240 else if (frame == 0) {
241 cv::imwrite(filepath, mask);
242 }
243 else {
244 throw std::runtime_error("Writing multiple frames not (yet) supported for image files");
245 }
246 }
247}