Commit 711462c6 authored by André Anjos's avatar André Anjos 💬
Browse files

Implemented cross tests; All passing

parent 58b1c875
......@@ -45,9 +45,10 @@ setup(
),
Extension("xbob.ip.draw._library",
[
"xbob/ip/draw/cross.cpp",
"xbob/ip/draw/line.cpp",
"xbob/ip/draw/point.cpp",
"xbob/ip/draw/try_point.cpp",
"xbob/ip/draw/point.cpp",
"xbob/ip/draw/main.cpp",
],
packages = packages,
......
/**
* @author André Anjos <andre.anjos@idiap.ch>
* @date Mon 7 Apr 14:50:46 2014 CEST
*
* @brief Binds point drawing operator to Python
*
* Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland
*/
#include <xbob.blitz/cppapi.h>
#include <xbob.blitz/cleanup.h>
#include <boost/tuple/tuple.hpp>
#include <bob/ip/drawing.h>
#include "utils.h"
template <typename T>
static PyObject* inner_cross (PyBlitzArrayObject* image,
Py_ssize_t y, Py_ssize_t x, Py_ssize_t radius, PyObject* color) {
switch (image->ndim) {
case 2:
{
T c;
int ok = get_color1(color, c);
if (!ok) return 0;
try {
bob::ip::draw_cross(*PyBlitzArrayCxx_AsBlitz<T,2>(image),
y, x, radius, c);
Py_RETURN_NONE;
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::draw_cross");
}
}
break;
case 3:
{
T r, g, b;
int ok = get_color3(color, r, g, b);
if (!ok) return 0;
try {
bob::ip::draw_cross(*PyBlitzArrayCxx_AsBlitz<T,3>(image),
y, x, radius, boost::tuple<T,T,T>(r, g, b));
Py_RETURN_NONE;
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::draw_cross");
}
}
break;
default:
PyErr_Format(PyExc_TypeError, "drawing operation does not support images with %" PY_FORMAT_SIZE_T "d dimensions (1 or 3 only)", image->ndim);
}
return 0;
}
PyObject* PyBobIpDraw_Cross (PyObject*, PyObject* args, PyObject* kwds) {
static const char* const_kwlist[] = {"image", "p", "radius", "color", 0};
static char** kwlist = const_cast<char**>(const_kwlist);
PyBlitzArrayObject* image = 0;
Py_ssize_t y = 0;
Py_ssize_t x = 0;
Py_ssize_t r = 0;
PyObject* color = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&(nn)nO", kwlist,
&PyBlitzArray_OutputConverter, &image, &y, &x, &r, &color)) return 0;
//protects acquired resources through this scope
auto image_ = make_safe(image);
switch(image->type_num) {
case NPY_UINT8:
return inner_cross<uint8_t>(image, y, x, r, color);
case NPY_UINT16:
return inner_cross<uint16_t>(image, y, x, r, color);
case NPY_FLOAT64:
return inner_cross<double>(image, y, x, r, color);
default:
PyErr_Format(PyExc_TypeError, "drawing operation does not support images with data type `%s' (choose from uint8|uint16|float64)", PyBlitzArray_TypenumAsString(image->type_num));
}
return 0;
}
template <typename T>
static PyObject* inner_plus (PyBlitzArrayObject* image,
Py_ssize_t y, Py_ssize_t x, Py_ssize_t radius, PyObject* color) {
switch (image->ndim) {
case 2:
{
T c;
int ok = get_color1(color, c);
if (!ok) return 0;
try {
bob::ip::draw_cross_plus(*PyBlitzArrayCxx_AsBlitz<T,2>(image),
y, x, radius, c);
Py_RETURN_NONE;
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::draw_cross_plus");
}
}
break;
case 3:
{
T r, g, b;
int ok = get_color3(color, r, g, b);
if (!ok) return 0;
try {
bob::ip::draw_cross_plus(*PyBlitzArrayCxx_AsBlitz<T,3>(image),
y, x, radius, boost::tuple<T,T,T>(r, g, b));
Py_RETURN_NONE;
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::draw_cross_plus");
}
}
break;
default:
PyErr_Format(PyExc_TypeError, "drawing operation does not support images with %" PY_FORMAT_SIZE_T "d dimensions (1 or 3 only)", image->ndim);
}
return 0;
}
PyObject* PyBobIpDraw_Plus (PyObject*, PyObject* args, PyObject* kwds) {
static const char* const_kwlist[] = {"image", "p", "radius", "color", 0};
static char** kwlist = const_cast<char**>(const_kwlist);
PyBlitzArrayObject* image = 0;
Py_ssize_t y = 0;
Py_ssize_t x = 0;
Py_ssize_t r = 0;
PyObject* color = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&(nn)nO", kwlist,
&PyBlitzArray_OutputConverter, &image, &y, &x, &r, &color)) return 0;
//protects acquired resources through this scope
auto image_ = make_safe(image);
switch(image->type_num) {
case NPY_UINT8:
return inner_plus<uint8_t>(image, y, x, r, color);
case NPY_UINT16:
return inner_plus<uint16_t>(image, y, x, r, color);
case NPY_FLOAT64:
return inner_plus<double>(image, y, x, r, color);
default:
PyErr_Format(PyExc_TypeError, "drawing operation does not support images with data type `%s' (choose from uint8|uint16|float64)", PyBlitzArray_TypenumAsString(image->type_num));
}
return 0;
}
......@@ -36,7 +36,7 @@ static xbob::extension::FunctionDoc s_point = xbob::extension::FunctionDoc(
.add_prototype("image, p, color")
.add_parameter("image", "array (uint8|uint16|float64, 3D)", "Input array containing an image with the shape ``(height, width)`` (for gray-scale images) or ``(3, height, width)`` (for color images)")
.add_parameter("p", "tuple", "a point, on the format ``(y, x)``, defining the location on the image that the pixel is going to be drawn.")
.add_parameter("color", "scalar|tuple", "Color of the pixel. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn.")
.add_parameter("color", "scalar|tuple", "Color of the pixel. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn. The type of scalars representing colors should match the pixel type in ``image``.")
;
extern PyObject* PyBobIpDraw_TryPoint(PyObject*, PyObject* args, PyObject* kwds);
......@@ -54,7 +54,7 @@ static xbob::extension::FunctionDoc s_try_point = xbob::extension::FunctionDoc(
.add_prototype("image, p, color")
.add_parameter("image", "array (uint8|uint16|float64, 3D)", "Input array containing an image with the shape ``(height, width)`` (for gray-scale images) or ``(3, height, width)`` (for color images)")
.add_parameter("p", "tuple", "a point, on the format ``(y, x)``, defining the location on the image that the pixel is going to be drawn.")
.add_parameter("color", "scalar|tuple", "Color of the pixel. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn.")
.add_parameter("color", "scalar|tuple", "Color of the pixel. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn. The type of scalars representing colors should match the pixel type in ``image``.")
;
extern PyObject* PyBobIpDraw_Line(PyObject*, PyObject* args, PyObject* kwds);
......@@ -86,12 +86,70 @@ static xbob::extension::FunctionDoc s_line = xbob::extension::FunctionDoc(
.add_prototype("image, p1, p2, color")
.add_parameter("image", "array (uint8|uint16|float64, 3D)", "Input array containing an image with the shape ``(height, width)`` (for gray-scale images) or ``(3, height, width)`` (for color images)")
.add_parameter("p1, p2", "tuple", "Points, on the format ``(y, x)``, defining the start and end of the line. Portions of the line outside the image range will be ignored.")
.add_parameter("color", "scalar|tuple", "Color of the pixel. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn.")
.add_parameter("color", "scalar|tuple", "Color of the pixel. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn. The type of scalars representing colors should match the pixel type in ``image``.")
;
extern PyObject* PyBobIpDraw_Cross(PyObject*, PyObject* args, PyObject* kwds);
static xbob::extension::FunctionDoc s_cross = xbob::extension::FunctionDoc(
"cross",
"Draws a cross in the given (gray-scale or color) image.",
"This method can draw a cross-like set of lines resembling an ``x``, in "
"either gray-scale (2D) or color images. The cross is centered on a given "
"point ``p`` and will have the ``radius`` defined. Images have to be "
":py:class:`numpy.ndarray`'s with either ``uint8``, ``uint16`` or "
"``float64`` data type. Trying to access outside the image range will "
"raise a :py:class:`RuntimeError`.\n"
"\n"
"In case you pass a 2D array representing a gray-scale image, this function "
"expects you pass a single scalar as a value for the input parameter "
"``color``. In the case you pass a 3D array (color image), then the color "
"parameter should be set to a tuple contanining 3 scalars representing the "
"levels for each of the color components (red, green and blue)\n"
"\n"
"Color images are expected to be represented using the first dimension for "
"the color planes: ``(3, height, width)``. Images are modified in place.\n"
)
.add_prototype("image, p, radius, color")
.add_parameter("image", "array (uint8|uint16|float64, 3D)", "Input array containing an image with the shape ``(height, width)`` (for gray-scale images) or ``(3, height, width)`` (for color images)")
.add_parameter("p", "tuple", "a point, on the format ``(y, x)``, defining the location on the image that the pixel is going to be drawn.")
.add_parameter("radius", "int", "the value of the radius for the cross to be drawn, in pixels")
.add_parameter("color", "scalar|tuple", "Color of the cross sign. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn. The type of scalars representing colors should match the pixel type in ``image``.")
;
extern PyObject* PyBobIpDraw_Plus(PyObject*, PyObject* args, PyObject* kwds);
static xbob::extension::FunctionDoc s_plus = xbob::extension::FunctionDoc(
"plus",
"Draws a plus sign in the given (gray-scale or color) image.",
"This method can draw a cross-like set of lines resembling an ``+``, in "
"either gray-scale (2D) or color images. The cross is centered on a given "
"point ``p`` and will have the ``radius`` defined. Images have to be "
":py:class:`numpy.ndarray`'s with either ``uint8``, ``uint16`` or "
"``float64`` data type. Trying to access outside the image range will "
"raise a :py:class:`RuntimeError`.\n"
"\n"
"In case you pass a 2D array representing a gray-scale image, this function "
"expects you pass a single scalar as a value for the input parameter "
"``color``. In the case you pass a 3D array (color image), then the color "
"parameter should be set to a tuple contanining 3 scalars representing the "
"levels for each of the color components (red, green and blue)\n"
"\n"
"Color images are expected to be represented using the first dimension for "
"the color planes: ``(3, height, width)``. Images are modified in place.\n"
)
.add_prototype("image, p, radius, color")
.add_parameter("image", "array (uint8|uint16|float64, 3D)", "Input array containing an image with the shape ``(height, width)`` (for gray-scale images) or ``(3, height, width)`` (for color images)")
.add_parameter("p", "tuple", "a point, on the format ``(y, x)``, defining the location on the image that the pixel is going to be drawn.")
.add_parameter("radius", "int", "the value of the radius for the cross to be drawn, in pixels")
.add_parameter("color", "scalar|tuple", "Color of the cross sign. In case the input image is gray-scale (2D), this should be a single scalar. If the input image is colored (3D), then it should be a sequence containing 3 scalars, representing the levels of red, green and blue (in this order) of the pixel to be drawn. The type of scalars representing colors should match the pixel type in ``image``.")
;
/**
def("draw_cross", &draw_cross, (arg("image"), arg("y"), arg("x"), arg("radius"), arg("color")), "Draws a cross with a given radius and color at the image. Uses the draw_line() primitive above. The cross will look like an 'x' and not like a '+'. To get a '+' sign, use the draw_cross_plus() variant. This method supports both grayscale (2D) or color RGB (3D) images. Depending on your image type, select an appropriate color value: a single gray value for 2D grayscale images or a 3-tuple containing the RGB color to set during drawing.");
def("draw_cross_plus", &draw_cross_plus, (arg("image"), arg("y"), arg("x"), arg("radius"), arg("color")), "Draws a cross with a given radius and color at the image. Uses the draw_line() primitive above. The cross will look like an '+' and not like a 'x'. To get a 'x' sign, use the draw_cross() variant. This method supports both grayscale (2D) or color RGB (3D) images. Depending on your image type, select an appropriate color value: a single gray value for 2D grayscale images or a 3-tuple containing the RGB color to set during drawing.");
def("draw_box", &draw_box, (arg("image"), arg("y"), arg("x"), arg("height"), arg("width"), arg("color")), "Draws a box at the image using the draw_line() primitive. This method supports both grayscale (2D) or color RGB (3D) images. Depending on your image type, select an appropriate color value: a single gray value for 2D grayscale images or a 3-tuple containing the RGB color to set during drawing.");
**/
......@@ -114,6 +172,18 @@ static PyMethodDef module_methods[] = {
METH_VARARGS|METH_KEYWORDS,
s_line.doc()
},
{
s_cross.name(),
(PyCFunction)PyBobIpDraw_Cross,
METH_VARARGS|METH_KEYWORDS,
s_cross.doc()
},
{
s_plus.name(),
(PyCFunction)PyBobIpDraw_Plus,
METH_VARARGS|METH_KEYWORDS,
s_plus.doc()
},
{0} /* Sentinel */
};
......
......@@ -11,7 +11,7 @@
import numpy
import nose.tools
from . import point, try_point, line#, box
from . import point, try_point, line, cross, plus#, box
def test_gray_point():
......@@ -20,15 +20,15 @@ def test_gray_point():
image.fill(0)
# Draws a white point on the middle
point(image, (50, 50), 255)
nose.tools.eq_(image[50, 50], 255)
point(image, (50, 40), 255)
nose.tools.eq_(image[50, 40], 255)
def test_does_not_raise_gray():
image = numpy.ndarray((100, 100), 'uint8')
image.fill(0)
imcopy = image.copy()
try_point(imcopy, (100, 100), 255)
try_point(imcopy, (30, 100), 255)
assert numpy.array_equal(image, imcopy) # no change is made
@nose.tools.raises(RuntimeError)
......@@ -49,8 +49,8 @@ def test_color_point():
image.fill(0)
# Draws a white point on the middle
point(image, (50, 50), white)
assert numpy.array_equal(image[:,50, 50],a1)
point(image, (50, 30), white)
assert numpy.array_equal(image[:,50, 30],a1)
def test_does_not_raise_color():
......@@ -64,7 +64,7 @@ def test_does_not_raise_color():
def test_raises_color():
image = numpy.ndarray((3, 100, 100), 'uint8')
point(image, (100, 100), (255, 255, 255))
point(image, (100, 20), (255, 255, 255))
def test_line():
......@@ -94,6 +94,31 @@ def test_line():
for k in range(50,70):
nose.tools.eq_(image[50,k], 65)
def test_cross_gray():
image = numpy.ndarray((100, 100), 'uint8')
image.fill(0)
cross(image, (50, 40), 5, 255)
nose.tools.eq_(image[50, 40], 255)
def test_plus_color():
image = numpy.ndarray((3, 100, 100), 'uint8')
image.fill(0)
radius = 3
center = (40, 50)
color = (255, 255, 255)
plus(image, center, radius, color)
# should have matching color along the vertical line
for y in range(center[0]-radius, center[0]+radius+1):
assert numpy.array_equal(image[:, y, center[1]], color)
# should have a matching color along the horizontal line
for x in range(center[1]-radius, center[1]+radius+1):
assert numpy.array_equal(image[:, y, center[1]], color)
def test_box():
# draws a box on the image, only test gray as color uses the same
......
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