Commit 9021af7f authored by Tiago de Freitas Pereira's avatar Tiago de Freitas Pereira

Merge branch...

Merge branch '8-extrapolate_mask-only-considers-2d-images-and-cannot-be-applied-to-color-images' into 'master'

Resolve "extrapolate_mask only considers 2D images and cannot be applied to color images"

Closes #8

See merge request !12
parents a7b215a9 586b454f
Pipeline #11602 passed with stages
in 48 minutes and 36 seconds
......@@ -420,12 +420,34 @@ bob::extension::FunctionDoc s_extrapolateMask = bob::extension::FunctionDoc(
.add_prototype("mask, img")
.add_prototype("mask, img, random_sigma, [neighbors], [rng]")
.add_parameter("mask", "array_like (2D, bool)", "The mask which has the valid pixel set to ``True`` and the invalid pixel set to ``False``")
.add_parameter("img", "array_like (2D, bool)", "The image that will be filled; must have the same shape as ``mask``")
.add_parameter("img", "array_like (2D or 3D, bool)", "The image that will be filled; must have the same shape as ``mask``")
.add_parameter("random_sigma", "float", "The standard deviation of the random factor to multiply thevalid pixel value from the border with; must be greater than or equal to 0")
.add_parameter("neighbors", "int", "[Default: 5] The number of neighbors of valid border pixels to choose one from; set ``neighbors=0`` to disable random selection")
.add_parameter("rng", ":py:class:`bob.core.random.mt19937`", "[Default: rng initialized with the system time] The random number generator to consider")
;
template <int N>
static void extrapolate_inner(PyBlitzArrayObject* mask, PyBlitzArrayObject* img){
switch (img->type_num){
case NPY_UINT8: bob::ip::base::extrapolateMask(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint8_t, N>(img)); break;
case NPY_UINT16: bob::ip::base::extrapolateMask(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint16_t, N>(img)); break;
case NPY_FLOAT64: bob::ip::base::extrapolateMask(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<double, N>(img)); break;
default:
throw std::runtime_error((boost::format("img arrays of type %s are currently not supported") % PyBlitzArray_TypenumAsString(img->type_num)).str());
}
}
template <int N>
static void extrapolate_inner2(PyBlitzArrayObject* mask, PyBlitzArrayObject* img, PyBoostMt19937Object* rng, double sigma, int neighbors){
switch (img->type_num){
case NPY_UINT8: bob::ip::base::extrapolateMaskRandom(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint8_t, N>(img), *rng->rng, sigma, neighbors); break;
case NPY_UINT16: bob::ip::base::extrapolateMaskRandom(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint16_t, N>(img), *rng->rng, sigma, neighbors); break;
case NPY_FLOAT64: bob::ip::base::extrapolateMaskRandom(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<double, N>(img), *rng->rng, sigma, neighbors); break;
default:
throw std::runtime_error((boost::format("img arrays of type %s are currently not supported") % PyBlitzArray_TypenumAsString(img->type_num)).str());
}
}
PyObject* PyBobIpBase_extrapolateMask(PyObject*, PyObject* args, PyObject* kwargs) {
BOB_TRY
/* Parses input arguments in a single shot */
......@@ -450,30 +472,24 @@ PyObject* PyBobIpBase_extrapolateMask(PyObject*, PyObject* args, PyObject* kwarg
PyErr_Format(PyExc_TypeError, "extrapolate_mask: the mask must be 2D and of boolean type");
return 0;
}
if (img->ndim != 2) {
PyErr_Format(PyExc_TypeError, "extrapolate_mask: the img must be 2D");
if (img->ndim != 2 && img->ndim != 3) {
PyErr_Format(PyExc_TypeError, "extrapolate_mask: the img must be 2D or 3D");
return 0;
}
if (sigma < 0){
// first variant
switch (img->type_num){
case NPY_UINT8: bob::ip::base::extrapolateMask(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint8_t, 2>(img)); break;
case NPY_UINT16: bob::ip::base::extrapolateMask(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint16_t, 2>(img)); break;
case NPY_FLOAT64: bob::ip::base::extrapolateMask(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<double, 2>(img)); break;
default:
PyErr_Format(PyExc_TypeError, "extrapolate_mask: img arrays of type %s are currently not supported", PyBlitzArray_TypenumAsString(img->type_num));
return 0;
switch (img->ndim){
case 2: extrapolate_inner<2>(mask, img); break;
case 3: extrapolate_inner<3>(mask, img); break;
default: break; // already handled
}
} else {
// second variant
switch (img->type_num){
case NPY_UINT8: bob::ip::base::extrapolateMaskRandom(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint8_t, 2>(img), *rng->rng, sigma, neighbors); break;
case NPY_UINT16: bob::ip::base::extrapolateMaskRandom(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<uint16_t, 2>(img), *rng->rng, sigma, neighbors); break;
case NPY_FLOAT64: bob::ip::base::extrapolateMaskRandom(*PyBlitzArrayCxx_AsBlitz<bool, 2>(mask), *PyBlitzArrayCxx_AsBlitz<double, 2>(img), *rng->rng, sigma, neighbors); break;
default:
PyErr_Format(PyExc_TypeError, "extrapolate_mask: img arrays of type %s are currently not supported", PyBlitzArray_TypenumAsString(img->type_num));
return 0;
switch (img->ndim){
case 2: extrapolate_inner2<2>(mask, img, rng, sigma, neighbors); break;
case 3: extrapolate_inner2<3>(mask, img, rng, sigma, neighbors); break;
default: break; // already handled
}
}
......
......@@ -438,6 +438,39 @@ namespace bob { namespace ip { namespace base {
}
}
/**
* brief Color version of mask extrapolation
*/
template <typename T>
void extrapolateMask(const blitz::Array<bool,2>& mask, blitz::Array<T,3>& img){
blitz::Range a = blitz::Range::all();
for (int i = 0; i < img.extent(0); ++i){
blitz::Array<T,2> slice(img(i,a,a));
extrapolateMask(mask(a,a), slice);
}
}
template <typename T>
void _copy(blitz::Array<T,2>& img, const blitz::TinyVector<int,2>& to, const blitz::TinyVector<int,2>& from, double random_factor, boost::mt19937& rng){
T value = img(from);
if (random_factor){
value = static_cast<T>(bob::core::random::normal_distribution<double>(1., random_factor)(rng) * value);
}
img(to) = value;
}
template <typename T>
void _copy(blitz::Array<T,3>& img, const blitz::TinyVector<int,2>& to, const blitz::TinyVector<int,2>& from, double random_factor, boost::mt19937& rng){
blitz::Array<T,1> pixel(img(blitz::Range::all(), from[0], from[1]));
if (random_factor){
double factor = bob::core::random::normal_distribution<double>(1., random_factor)(rng);
for (int i = 0; i < img.extent(0); ++i)
pixel(i) = static_cast<T>(pixel(i) * factor);
}
img(blitz::Range::all(), to[0], to[1]) = pixel;
}
/**
* @brief Function which fills unmasked pixel areas of an image with pixel values from the border of the masked part of the image
......@@ -453,10 +486,11 @@ namespace bob { namespace ip { namespace base {
* high performance. A copy might be done by the user before calling
* the function if required.
*/
template <typename T>
void extrapolateMaskRandom(const blitz::Array<bool,2>& mask, blitz::Array<T,2>& img, boost::mt19937& rng, double random_factor = 0.01, int neighbors = 5){
template <typename T, int N>
void extrapolateMaskRandom(const blitz::Array<bool,2>& mask, blitz::Array<T,N>& img, boost::mt19937& rng, double random_factor = 0.01, int neighbors = 5){
// Check input and output size
bob::core::array::assertSameShape(mask, img);
blitz::TinyVector<int,2> shape(img.extent(N-2), img.extent(N-1));
bob::core::array::assertSameShape(mask, shape);
// get the masked center
int miny = mask.extent(0)-1, maxy = 0, minx = mask.extent(1)-1, maxx = 0;
......@@ -536,30 +570,31 @@ namespace bob { namespace ip { namespace base {
if (valid_y * next_dir_y + valid_x * next_dir_x >= border[next_index]){
bob::core::warn << "Could not find valid pixel in direction (" << next_dir_y << ", " << next_dir_x << ") at pixel position (" << current_pos_y << ", " << current_pos_x << "); is your mask convex?";
} else {
T value = static_cast<T>(0);
// choose one of the next pixels
std::vector<blitz::TinyVector<int,2>> locations;
if (neighbors >= 1){
std::vector<T> values;
for (int c = -neighbors; c <= neighbors; ++c){
int pos_y = valid_y + c * current_dir_y;
int pos_x = valid_x + c * current_dir_x;
if (pos_y >= 0 && pos_y < img.extent(0) && pos_x >= 0 && pos_x < img.extent(1) && filled_mask(pos_y, pos_x)){
values.push_back(img(pos_y, pos_x));
locations.push_back(blitz::TinyVector<int,2>(pos_y, pos_x));
}
}
if (!values.size()){
bob::core::warn << "Could not find valid pixel in range " << neighbors << " close to the border at pixel position (" << current_pos_y << ", " << current_pos_x << "); is your mask convex?";
} else {
// choose random value
value = values[boost::uniform_int<int>(0, values.size()-1)(rng)];
}
} else { // neighbors == 1
value = img(valid_y, valid_x);
locations.push_back(blitz::TinyVector<int,2>(valid_y, valid_x));
}
if (random_factor){
value = static_cast<T>(bob::core::random::normal_distribution<double>(1., random_factor)(rng) * value);
// choose random location
blitz::TinyVector<int,2> location;
if (!locations.size()){
bob::core::warn << "Could not find valid pixel in range " << neighbors << " close to the border at pixel position (" << current_pos_y << ", " << current_pos_x << "); is your mask convex?";
location = blitz::TinyVector<int,2>(current_pos_y, current_pos_x);
} else {
location = locations[boost::uniform_int<int>(0, locations.size()-1)(rng)];
}
img(current_pos_y, current_pos_x) = value;
// copy pixel value
_copy(img, blitz::TinyVector<int,2>(current_pos_y, current_pos_x), location, random_factor, rng);
filled_mask(current_pos_y, current_pos_x) = true;
}
} // write value
......
......@@ -92,9 +92,14 @@ def test_extrapolate_mask():
bob.ip.base.extrapolate_mask(a2_5_3, i2_5_3)
assert numpy.allclose(i2_5_3, s2_5_3)
# test color image
i = numpy.array([i2_5, i2_5, i2_5])
bob.ip.base.extrapolate_mask(a2_5_1,i)
assert numpy.allclose(i, [s2_5_1,s2_5_1,s2_5_1])
###############################################
#### random image extrapolartion with mask ####
#### random image extrapolation with mask #####
###############################################
fill_src_image = numpy.array([
......@@ -128,6 +133,11 @@ def test_extrapolate_random():
assert numpy.allclose(image, fill_ref_image)
# test color image
color_image = numpy.array([fill_src_image.astype(numpy.float64)]*3)
bob.ip.base.extrapolate_mask(fill_src_mask, color_image, random_sigma = 0.05, neighbors = 1, rng = bob.core.random.mt19937(42))
assert numpy.allclose(image, [fill_ref_image]*3)
###############################################
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment