Commit 275e7480 authored by André Anjos's avatar André Anjos 💬
Browse files

Point and TryPoint now working and tested

parent 1ee675a3
......@@ -7,7 +7,7 @@ from setuptools import setup, find_packages, dist
dist.Distribution(dict(setup_requires=['xbob.blitz']))
from xbob.blitz.extension import Extension
packages = ['bob-ip >= 1.2.2']
packages = ['bob-ip >= 1.2.2', 'boost']
version = '2.0.0a0'
setup(
......@@ -45,6 +45,8 @@ setup(
),
Extension("xbob.ip.draw._library",
[
"xbob/ip/draw/point.cpp",
"xbob/ip/draw/try_point.cpp",
"xbob/ip/draw/main.cpp",
],
packages = packages,
......
......@@ -12,7 +12,71 @@
#include <xbob.blitz/cleanup.h>
#include <xbob.extension/documentation.h>
extern PyObject* PyBobIpDraw_Point(PyObject*, PyObject* args, PyObject* kwds);
static xbob::extension::FunctionDoc s_point = xbob::extension::FunctionDoc(
"point",
"Draws a point in the given (gray-scale or color) image.",
"This method can draw a point in either gray-scale (2D) or color images. "
"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, y, x, 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("y, x", "int", "Index (0-based) pointing to 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.")
;
extern PyObject* PyBobIpDraw_TryPoint(PyObject*, PyObject* args, PyObject* kwds);
static xbob::extension::FunctionDoc s_try_point = xbob::extension::FunctionDoc(
"try_point",
"Tries to draw a point in the given (gray-scale or color) image.",
"This method tries to draw a point in either gray-scale (2D) or color "
"images. If the point is out of bounds, it is simply ignored and not "
"drawn. The input of this method is identical to the input of "
":py:func:`point`, in this module. See its help message for details."
)
.add_prototype("image, y, x, 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("y, x", "int", "Index (0-based) pointing to 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.")
;
/**
def("draw_line", &draw_line, (arg("image"), arg("y1"), arg("x1"), arg("y2"), arg("x2"), arg("color")), "Draws a line between two points p1(x1,y1) and p2(x2,y2). This function is based on the Bresenham's line algorithm and is highly optimized to be able to draw lines very quickly. There is no floating point arithmetic nor multiplications and divisions involved. Only addition, subtraction and bit shifting are used.\n\nThe line may go out of the image bounds in which case such points (lying outside the image boundary are ignored).\n\nReferences: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm. 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", &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.");
**/
static PyMethodDef module_methods[] = {
{
s_point.name(),
(PyCFunction)PyBobIpDraw_Point,
METH_VARARGS|METH_KEYWORDS,
s_point.doc()
},
{
s_try_point.name(),
(PyCFunction)PyBobIpDraw_TryPoint,
METH_VARARGS|METH_KEYWORDS,
s_try_point.doc()
},
{0} /* Sentinel */
};
......
/**
* @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>
template <typename T>
static PyObject* inner_point (PyBlitzArrayObject* image,
int y, int x, PyObject* color) {
switch (image->ndim) {
case 2:
{
if (!PyArray_CheckScalar(color) && !PyNumber_Check(color)) {
PyErr_Format(PyExc_TypeError, "drawing on a 2D image (gray-scale) requires a color which is a scalar, not `%s'", Py_TYPE(color)->tp_name);
return 0;
}
T c = PyBlitzArrayCxx_AsCScalar<T>(color);
if (PyErr_Occurred()) return 0;
try {
bob::ip::draw_point(*PyBlitzArrayCxx_AsBlitz<T,2>(image), y, x, c);
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
return 0;
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::draw_point");
return 0;
}
}
break;
case 3:
{
if (!PySequence_Check(color) || (PySequence_Fast_GET_SIZE(color) != 3)) {
PyErr_Format(PyExc_TypeError, "drawing on a 3D image (colored) requires a color which is a sequence (tuple, list, etc) with 3 components");
return 0;
}
boost::tuple<T,T,T> c(
PyBlitzArrayCxx_AsCScalar<T>(PyTuple_GET_ITEM(color, 0)),
PyBlitzArrayCxx_AsCScalar<T>(PyTuple_GET_ITEM(color, 1)),
PyBlitzArrayCxx_AsCScalar<T>(PyTuple_GET_ITEM(color, 2))
);
if (PyErr_Occurred()) return 0;
try {
bob::ip::draw_point(*PyBlitzArrayCxx_AsBlitz<T,3>(image), y, x, c);
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
return 0;
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::draw_point");
return 0;
}
}
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;
}
Py_RETURN_NONE;
}
PyObject* PyBobIpDraw_Point (PyObject*, PyObject* args, PyObject* kwds) {
static const char* const_kwlist[] = {"image", "y", "x", "color", 0};
static char** kwlist = const_cast<char**>(const_kwlist);
PyBlitzArrayObject* image = 0;
Py_ssize_t x = 0;
Py_ssize_t y = 0;
PyObject* color = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&nnO", kwlist,
&PyBlitzArray_OutputConverter, &image, &x, &y, &color)) return 0;
//protects acquired resources through this scope
auto image_ = make_safe(image);
switch(image->type_num) {
case NPY_UINT8:
return inner_point<uint8_t>(image, y, x, color);
case NPY_UINT16:
return inner_point<uint16_t>(image, y, x, color);
case NPY_FLOAT64:
return inner_point<double>(image, y, x, 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;
}
......@@ -11,7 +11,7 @@
import numpy
import nose.tools
from . import point, line, box, try_point
from . import point, try_point#, line, box
def test_gray_point():
......@@ -31,11 +31,11 @@ def test_does_not_raise_gray():
try_point(imcopy, 100, 100, 255)
assert numpy.array_equal(image, imcopy) # no change is made
@nose.tools.raises(IndexError)
@nose.tools.raises(RuntimeError)
def test_raises_gray():
image = numpy.ndarray((100, 100), 'uint8')
point(imcopy, 100, 100, 255)
point(image, 100, 100, 255)
def test_color_point():
......@@ -57,10 +57,10 @@ def test_does_not_raise_color():
image = numpy.ndarray((3, 100, 100), 'uint8')
image.fill(0)
imcopy = image.copy()
try_point(imcopy, 100, 100, white)
try_point(imcopy, 100, 100, (255, 255, 255))
assert numpy.array_equal(image, imcopy) # no change is made
@nose.tools.raises(IndexError)
@nose.tools.raises(RuntimeError)
def test_raises_color():
image = numpy.ndarray((3, 100, 100), 'uint8')
......
/**
* @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>
template <typename T>
static PyObject* inner_point (PyBlitzArrayObject* image,
int y, int x, PyObject* color) {
switch (image->ndim) {
case 2:
{
if (!PyArray_CheckScalar(color) && !PyNumber_Check(color)) {
PyErr_Format(PyExc_TypeError, "drawing on a 2D image (gray-scale) requires a color which is a scalar, not `%s'", Py_TYPE(color)->tp_name);
return 0;
}
T c = PyBlitzArrayCxx_AsCScalar<T>(color);
if (PyErr_Occurred()) return 0;
try {
bob::ip::try_draw_point(*PyBlitzArrayCxx_AsBlitz<T,2>(image), y, x, c);
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
return 0;
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::try_draw_point");
return 0;
}
}
break;
case 3:
{
if (!PySequence_Check(color) || (PySequence_Fast_GET_SIZE(color) != 3)) {
PyErr_Format(PyExc_TypeError, "drawing on a 3D image (colored) requires a color which is a sequence (tuple, list, etc) with 3 components");
return 0;
}
boost::tuple<T,T,T> c(
PyBlitzArrayCxx_AsCScalar<T>(PyTuple_GET_ITEM(color, 0)),
PyBlitzArrayCxx_AsCScalar<T>(PyTuple_GET_ITEM(color, 1)),
PyBlitzArrayCxx_AsCScalar<T>(PyTuple_GET_ITEM(color, 2))
);
if (PyErr_Occurred()) return 0;
try {
bob::ip::try_draw_point(*PyBlitzArrayCxx_AsBlitz<T,3>(image), y, x, c);
}
catch (std::exception& e) {
PyErr_Format(PyExc_RuntimeError, "%s", e.what());
return 0;
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "caught unknown exception while calling C++ bob::ip::try_draw_point");
return 0;
}
}
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;
}
Py_RETURN_NONE;
}
PyObject* PyBobIpDraw_TryPoint (PyObject*, PyObject* args, PyObject* kwds) {
static const char* const_kwlist[] = {"image", "y", "x", "color", 0};
static char** kwlist = const_cast<char**>(const_kwlist);
PyBlitzArrayObject* image = 0;
Py_ssize_t x = 0;
Py_ssize_t y = 0;
PyObject* color = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&nnO", kwlist,
&PyBlitzArray_OutputConverter, &image, &x, &y, &color)) return 0;
//protects acquired resources through this scope
auto image_ = make_safe(image);
switch(image->type_num) {
case NPY_UINT8:
return inner_point<uint8_t>(image, y, x, color);
case NPY_UINT16:
return inner_point<uint16_t>(image, y, x, color);
case NPY_FLOAT64:
return inner_point<double>(image, y, x, 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;
}
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