2016-11-17 11:13:22 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2011-2016 Blender Foundation
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __UTIL_IMAGE_IMPL_H__
|
|
|
|
#define __UTIL_IMAGE_IMPL_H__
|
|
|
|
|
2016-11-22 11:05:57 +00:00
|
|
|
#include "util_algorithm.h"
|
2016-11-17 11:13:22 +00:00
|
|
|
#include "util_debug.h"
|
2017-02-23 13:46:22 +00:00
|
|
|
#include "util_half.h"
|
2016-11-17 11:13:22 +00:00
|
|
|
#include "util_image.h"
|
|
|
|
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
const T *util_image_read(const vector<T>& pixels,
|
|
|
|
const size_t width,
|
|
|
|
const size_t height,
|
|
|
|
const size_t /*depth*/,
|
|
|
|
const size_t components,
|
|
|
|
const size_t x, const size_t y, const size_t z) {
|
|
|
|
const size_t index = ((size_t)z * (width * height) +
|
|
|
|
(size_t)y * width +
|
|
|
|
(size_t)x) * components;
|
|
|
|
return &pixels[index];
|
|
|
|
}
|
|
|
|
|
2017-02-23 13:46:22 +00:00
|
|
|
/* Cast input pixel from unknown storage to float. */
|
|
|
|
template<typename T>
|
|
|
|
inline float cast_to_float(T value);
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline float cast_to_float(float value)
|
|
|
|
{
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
template<>
|
|
|
|
inline float cast_to_float(uchar value)
|
|
|
|
{
|
|
|
|
return (float)value / 255.0f;
|
|
|
|
}
|
|
|
|
template<>
|
|
|
|
inline float cast_to_float(half value)
|
|
|
|
{
|
|
|
|
return half_to_float(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cast float value to output pixel type. */
|
|
|
|
template<typename T>
|
|
|
|
inline T cast_from_float(float value);
|
|
|
|
|
|
|
|
template<>
|
|
|
|
inline float cast_from_float(float value)
|
|
|
|
{
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
template<>
|
|
|
|
inline uchar cast_from_float(float value)
|
|
|
|
{
|
|
|
|
if(value < 0.0f) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if(value > (1.0f - 0.5f / 255.0f)) {
|
|
|
|
return 255;
|
|
|
|
}
|
|
|
|
return (uchar)((255.0f * value) + 0.5f);
|
|
|
|
}
|
|
|
|
template<>
|
|
|
|
inline half cast_from_float(float value)
|
|
|
|
{
|
|
|
|
return float_to_half(value);
|
|
|
|
}
|
|
|
|
|
2016-11-17 11:13:22 +00:00
|
|
|
template<typename T>
|
|
|
|
void util_image_downscale_sample(const vector<T>& pixels,
|
|
|
|
const size_t width,
|
|
|
|
const size_t height,
|
|
|
|
const size_t depth,
|
|
|
|
const size_t components,
|
|
|
|
const size_t kernel_size,
|
|
|
|
const float x,
|
|
|
|
const float y,
|
|
|
|
const float z,
|
|
|
|
T *result)
|
|
|
|
{
|
|
|
|
assert(components <= 4);
|
|
|
|
const size_t ix = (size_t)x,
|
|
|
|
iy = (size_t)y,
|
|
|
|
iz = (size_t)z;
|
|
|
|
/* TODO(sergey): Support something smarter than box filer. */
|
|
|
|
float accum[4] = {0};
|
|
|
|
size_t count = 0;
|
|
|
|
for(size_t dz = 0; dz < kernel_size; ++dz) {
|
|
|
|
for(size_t dy = 0; dy < kernel_size; ++dy) {
|
|
|
|
for(size_t dx = 0; dx < kernel_size; ++dx) {
|
|
|
|
const size_t nx = ix + dx,
|
|
|
|
ny = iy + dy,
|
|
|
|
nz = iz + dz;
|
|
|
|
if(nx >= width || ny >= height || nz >= depth) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const T *pixel = util_image_read(pixels,
|
|
|
|
width, height, depth,
|
|
|
|
components,
|
|
|
|
nx, ny, nz);
|
|
|
|
for(size_t k = 0; k < components; ++k) {
|
2017-02-23 13:46:22 +00:00
|
|
|
accum[k] += cast_to_float(pixel[k]);
|
2016-11-17 11:13:22 +00:00
|
|
|
}
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-23 13:46:22 +00:00
|
|
|
if(count != 0) {
|
|
|
|
const float inv_count = 1.0f / (float)count;
|
|
|
|
for(size_t k = 0; k < components; ++k) {
|
|
|
|
result[k] = cast_from_float<T>(accum[k] * inv_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for(size_t k = 0; k < components; ++k) {
|
|
|
|
result[k] = T(0.0f);
|
|
|
|
}
|
2016-11-17 11:13:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void util_image_downscale_pixels(const vector<T>& input_pixels,
|
|
|
|
const size_t input_width,
|
|
|
|
const size_t input_height,
|
|
|
|
const size_t input_depth,
|
|
|
|
const size_t components,
|
|
|
|
const float inv_scale_factor,
|
|
|
|
const size_t output_width,
|
|
|
|
const size_t output_height,
|
|
|
|
const size_t output_depth,
|
|
|
|
vector<T> *output_pixels)
|
|
|
|
{
|
|
|
|
const size_t kernel_size = (size_t)(inv_scale_factor + 0.5f);
|
|
|
|
for(size_t z = 0; z < output_depth; ++z) {
|
|
|
|
for(size_t y = 0; y < output_height; ++y) {
|
|
|
|
for(size_t x = 0; x < output_width; ++x) {
|
|
|
|
const float input_x = (float)x * inv_scale_factor,
|
|
|
|
input_y = (float)y * inv_scale_factor,
|
|
|
|
input_z = (float)z * inv_scale_factor;
|
|
|
|
const size_t output_index =
|
|
|
|
(z * output_width * output_height +
|
|
|
|
y * output_width + x) * components;
|
|
|
|
util_image_downscale_sample(input_pixels,
|
|
|
|
input_width, input_height, input_depth,
|
|
|
|
components,
|
|
|
|
kernel_size,
|
|
|
|
input_x, input_y, input_z,
|
|
|
|
&output_pixels->at(output_index));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* namespace */
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void util_image_resize_pixels(const vector<T>& input_pixels,
|
|
|
|
const size_t input_width,
|
|
|
|
const size_t input_height,
|
|
|
|
const size_t input_depth,
|
|
|
|
const size_t components,
|
|
|
|
const float scale_factor,
|
|
|
|
vector<T> *output_pixels,
|
|
|
|
size_t *output_width,
|
|
|
|
size_t *output_height,
|
|
|
|
size_t *output_depth)
|
|
|
|
{
|
|
|
|
/* Early output for case when no scaling is applied. */
|
|
|
|
if(scale_factor == 1.0f) {
|
|
|
|
*output_width = input_width;
|
|
|
|
*output_height = input_height;
|
|
|
|
*output_depth = input_depth;
|
|
|
|
*output_pixels = input_pixels;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* First of all, we calculate output image dimensions.
|
|
|
|
* We clamp them to be 1 pixel at least so we do not generate degenerate
|
|
|
|
* image.
|
|
|
|
*/
|
2016-11-22 11:11:08 +00:00
|
|
|
*output_width = max((size_t)((float)input_width * scale_factor), (size_t)1);
|
|
|
|
*output_height = max((size_t)((float)input_height * scale_factor), (size_t)1);
|
|
|
|
*output_depth = max((size_t)((float)input_depth * scale_factor), (size_t)1);
|
2016-11-17 11:13:22 +00:00
|
|
|
/* Prepare pixel storage for the result. */
|
|
|
|
const size_t num_output_pixels = ((*output_width) *
|
|
|
|
(*output_height) *
|
|
|
|
(*output_depth)) * components;
|
|
|
|
output_pixels->resize(num_output_pixels);
|
|
|
|
if(scale_factor < 1.0f) {
|
|
|
|
const float inv_scale_factor = 1.0f / scale_factor;
|
|
|
|
util_image_downscale_pixels(input_pixels,
|
|
|
|
input_width, input_height, input_depth,
|
|
|
|
components,
|
|
|
|
inv_scale_factor,
|
|
|
|
*output_width, *output_height, *output_depth,
|
|
|
|
output_pixels);
|
|
|
|
} else {
|
|
|
|
/* TODO(sergey): Needs implementation. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCL_NAMESPACE_END
|
|
|
|
|
|
|
|
#endif /* __UTIL_IMAGE_IMPL_H__ */
|