1
0
Fork 0
mirror of https://github.com/ruby-opencv/ruby-opencv synced 2023-03-27 23:22:12 -04:00
ruby-opencv/ext/cvmat.cpp
2011-02-26 15:39:07 +09:00

5027 lines
171 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/************************************************************
cvmat.cpp -
$Author: lsxi $
Copyright (C) 2005-2008 Masakazu Yonekura
************************************************************/
#include "cvmat.h"
/*
* Document-class: OpenCV::CvMat
*
* CvMat is basic 2D matrix class in OpenCV.
*
* C structure is here.
* typedef struct CvMat{
* int type;
* int step;
* int *refcount;
* union
* {
* uchar *ptr;
* short *s;
* int *i;
* float *fl;
* double *db;
* } data;
* #ifdef __cplusplus
* union
* {
* int rows;
* int height;
* };
* union
* {
* int cols;
* int width;
* };
* #else
* int rows; // number of row
* int cols; // number of columns
* }CvMat;
*/
__NAMESPACE_BEGIN_OPENCV
__NAMESPACE_BEGIN_CVMAT
#define SUPPORT_8UC1_ONLY(value) if (cvGetElemType(CVARR(value)) != CV_8UC1) {rb_raise(rb_eNotImpError, "support single-channel 8bit unsigned image only.");}
#define SUPPORT_8U_ONLY(value) if (CV_MAT_DEPTH(cvGetElemType(CVARR(value))) != CV_8U) {rb_raise(rb_eNotImpError, "support 8bit unsigned image only.");}
#define SUPPORT_C1_ONLY(value) if (CV_MAT_CN(cvGetElemType(CVARR(value))) != 1) {rb_raise(rb_eNotImpError, "support single-channel image only.");}
#define SUPPORT_C1C3_ONLY(value) if (CV_MAT_CN(cvGetElemType(CVARR(value))) != 1 && CV_MAT_CN(cvGetElemType(CVARR(value))) != 3) {rb_raise(rb_eNotImpError, "support single-channel or 3-channnel image only.");}
#define DRAWING_OPTION(op) NIL_P(op) ? rb_const_get(rb_class(), rb_intern("DRAWING_OPTION")) : rb_funcall(rb_const_get(rb_class(), rb_intern("DRAWING_OPTION")), rb_intern("merge"), 1, op)
#define DO_COLOR(op) VALUE_TO_CVSCALAR(rb_hash_aref(op, ID2SYM(rb_intern("color"))))
#define DO_THICKNESS(op) FIX2INT(rb_hash_aref(op, ID2SYM(rb_intern("thickness"))))
#define DO_LINE_TYPE(op) FIX2INT(rb_hash_aref(op, ID2SYM(rb_intern("line_type"))) == ID2SYM("aa") ? INT2FIX(CV_AA) : rb_hash_aref(op, ID2SYM(rb_intern("line_type"))))
#define DO_SHIFT(op) FIX2INT(rb_hash_aref(op, ID2SYM(rb_intern("shift"))))
#define DO_IS_CLOSED(op) ({VALUE _is_closed = rb_hash_aref(op, ID2SYM(rb_intern("is_closed"))); NIL_P(_is_closed) ? 0 : _is_closed == Qfalse ? 0 : 1;})
#define GOOD_FEATURES_TO_TRACK_OPTION(op) NIL_P(op) ? rb_const_get(rb_class(), rb_intern("GOOD_FEATURES_TO_TRACK_OPTION")) : rb_funcall(rb_const_get(rb_class(), rb_intern("GOOD_FEATURES_TO_TRACK_OPTION")), rb_intern("merge"), 1, op)
#define GF_MAX(op) FIX2INT(rb_hash_aref(op, ID2SYM(rb_intern("max"))))
#define GF_MASK(op) MASK(rb_hash_aref(op, ID2SYM(rb_intern("mask"))))
#define GF_BLOCK_SIZE(op) FIX2INT(rb_hash_aref(op, ID2SYM(rb_intern("block_size"))))
#define GF_USE_HARRIS(op) TRUE_OR_FALSE(rb_hash_aref(op, ID2SYM(rb_intern("use_harris"))), 0)
#define GF_K(op) NUM2DBL(rb_hash_aref(op, ID2SYM(rb_intern("k"))))
#define FLOOD_FILL_OPTION(op) NIL_P(op) ? rb_const_get(rb_class(), rb_intern("FLOOD_FILL_OPTION")) : rb_funcall(rb_const_get(rb_class(), rb_intern("FLOOD_FILL_OPTION")), rb_intern("merge"), 1, op)
#define FF_CONNECTIVITY(op) NUM2INT(rb_hash_aref(op, ID2SYM(rb_intern("connectivity"))))
#define FF_FIXED_RANGE(op) TRUE_OR_FALSE(rb_hash_aref(op, ID2SYM(rb_intern("fixed_range"))), 0)
#define FF_MASK_ONLY(op) TRUE_OR_FALSE(rb_hash_aref(op, ID2SYM(rb_intern("mask_only"))), 0)
#define FIND_CONTOURS_OPTION(op) NIL_P(op) ? rb_const_get(rb_class(), rb_intern("FIND_CONTOURS_OPTION")) : rb_funcall(rb_const_get(rb_class(), rb_intern("FIND_CONTOURS_OPTION")), rb_intern("merge"), 1, op)
#define FC_MODE(op) FIX2INT(rb_hash_aref(op, ID2SYM(rb_intern("mode"))))
#define FC_METHOD(op) FIX2INT(rb_hash_aref(op, ID2SYM(rb_intern("method"))))
#define FC_OFFSET(op)VALUE_TO_CVPOINT(rb_hash_aref(op, ID2SYM(rb_intern("offset"))))
#define OPTICAL_FLOW_HS_OPTION(op) NIL_P(op) ? rb_const_get(rb_class(), rb_intern("OPTICAL_FLOW_HS_OPTION")) : rb_funcall(rb_const_get(rb_class(), rb_intern("OPTICAL_FLOW_HS_OPTION")), rb_intern("merge"), 1, op)
#define HS_LAMBDA(op) NUM2DBL(rb_hash_aref(op, ID2SYM(rb_intern("lambda"))))
#define HS_CRITERIA(op) VALUE_TO_CVTERMCRITERIA(rb_hash_aref(op, ID2SYM(rb_intern("criteria"))))
#define OPTICAL_FLOW_BM_OPTION(op) NIL_P(op) ? rb_const_get(rb_class(), rb_intern("OPTICAL_FLOW_BM_OPTION")) : rb_funcall(rb_const_get(rb_class(), rb_intern("OPTICAL_FLOW_BM_OPTION")), rb_intern("merge"), 1, op)
#define BM_BLOCK_SIZE(op) VALUE_TO_CVSIZE(rb_hash_aref(op, ID2SYM(rb_intern("block_size"))))
#define BM_SHIFT_SIZE(op) VALUE_TO_CVSIZE(rb_hash_aref(op, ID2SYM(rb_intern("shift_size"))))
#define BM_MAX_RANGE(op) VALUE_TO_CVSIZE(rb_hash_aref(op, ID2SYM(rb_intern("max_range"))))
#define FIND_FUNDAMENTAL_MAT_OPTION(op) NIL_P(op) ? rb_const_get(rb_class(), rb_intern("FIND_FUNDAMENTAL_MAT_OPTION")) : rb_funcall(rb_const_get(rb_class(), rb_intern("FIND_FUNDAMENTAL_MAT_OPTION")), rb_intern("merge"), 1, op)
#define FFM_WITH_STATUS(op) TRUE_OR_FALSE(rb_hash_aref(op, ID2SYM(rb_intern("with_status"))), 0)
#define FFM_MAXIMUM_DISTANCE(op) NUM2DBL(rb_hash_aref(op, ID2SYM(rb_intern("maximum_distance"))))
#define FFM_DESIRABLE_LEVEL(op) NUM2DBL(rb_hash_aref(op, ID2SYM(rb_intern("desirable_level"))))
VALUE rb_klass;
VALUE
rb_class()
{
return rb_klass;
}
void define_ruby_class()
{
if (rb_klass)
return;
/*
* opencv = rb_define_module("OpenCV");
*
* note: this comment is used by rdoc.
*/
VALUE opencv = rb_module_opencv();
rb_klass = rb_define_class_under(opencv, "CvMat", rb_cObject);
rb_define_alloc_func(rb_klass, rb_allocate);
VALUE drawing_option = rb_hash_new();
rb_define_const(rb_klass, "DRAWING_OPTION", drawing_option);
rb_hash_aset(drawing_option, ID2SYM(rb_intern("color")), cCvScalar::new_object(cvScalarAll(0)));
rb_hash_aset(drawing_option, ID2SYM(rb_intern("thickness")), INT2FIX(1));
rb_hash_aset(drawing_option, ID2SYM(rb_intern("line_type")), INT2FIX(8));
rb_hash_aset(drawing_option, ID2SYM(rb_intern("shift")), INT2FIX(0));
VALUE good_features_to_track_option = rb_hash_new();
rb_define_const(rb_klass, "GOOD_FEATURES_TO_TRACK_OPTION", good_features_to_track_option);
rb_hash_aset(good_features_to_track_option, ID2SYM(rb_intern("max")), INT2FIX(0xFF));
rb_hash_aset(good_features_to_track_option, ID2SYM(rb_intern("mask")), Qnil);
rb_hash_aset(good_features_to_track_option, ID2SYM(rb_intern("block_size")), INT2FIX(3));
rb_hash_aset(good_features_to_track_option, ID2SYM(rb_intern("use_harris")), Qfalse);
rb_hash_aset(good_features_to_track_option, ID2SYM(rb_intern("k")), rb_float_new(0.04));
VALUE flood_fill_option = rb_hash_new();
rb_define_const(rb_klass, "FLOOD_FILL_OPTION", flood_fill_option);
rb_hash_aset(flood_fill_option, ID2SYM(rb_intern("connectivity")), INT2FIX(4));
rb_hash_aset(flood_fill_option, ID2SYM(rb_intern("fixed_range")), Qfalse);
rb_hash_aset(flood_fill_option, ID2SYM(rb_intern("mask_only")), Qfalse);
VALUE find_contours_option = rb_hash_new();
rb_define_const(rb_klass, "FIND_CONTOURS_OPTION", find_contours_option);
rb_hash_aset(find_contours_option, ID2SYM(rb_intern("mode")), INT2FIX(CV_RETR_LIST));
rb_hash_aset(find_contours_option, ID2SYM(rb_intern("method")), INT2FIX(CV_CHAIN_APPROX_SIMPLE));
rb_hash_aset(find_contours_option, ID2SYM(rb_intern("offset")), cCvPoint::new_object(cvPoint(0,0)));
VALUE optical_flow_hs_option = rb_hash_new();
rb_define_const(rb_klass, "OPTICAL_FLOW_HS_OPTION", optical_flow_hs_option);
rb_hash_aset(optical_flow_hs_option, ID2SYM(rb_intern("lambda")), rb_float_new(0.0005));
rb_hash_aset(optical_flow_hs_option, ID2SYM(rb_intern("criteria")), cCvTermCriteria::new_object(cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1, 0.001)));
VALUE optical_flow_bm_option = rb_hash_new();
rb_define_const(rb_klass, "OPTICAL_FLOW_BM_OPTION", optical_flow_bm_option);
rb_hash_aset(optical_flow_bm_option, ID2SYM(rb_intern("block_size")), cCvSize::new_object(cvSize(4, 4)));
rb_hash_aset(optical_flow_bm_option, ID2SYM(rb_intern("shift_size")), cCvSize::new_object(cvSize(1, 1)));
rb_hash_aset(optical_flow_bm_option, ID2SYM(rb_intern("max_range")), cCvSize::new_object(cvSize(4, 4)));
VALUE find_fundamental_matrix_option = rb_hash_new();
rb_define_const(rb_klass, "FIND_FUNDAMENTAL_MAT_OPTION", find_fundamental_matrix_option);
rb_hash_aset(find_fundamental_matrix_option, ID2SYM(rb_intern("with_status")), Qfalse);
rb_hash_aset(find_fundamental_matrix_option, ID2SYM(rb_intern("maximum_distance")), rb_float_new(1.0));
rb_hash_aset(find_fundamental_matrix_option, ID2SYM(rb_intern("desirable_level")), rb_float_new(0.99));
rb_define_method(rb_klass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1);
rb_define_singleton_method(rb_klass, "load", RUBY_METHOD_FUNC(rb_load_imageM), -1);
// Ruby/OpenCV original functions
rb_define_method(rb_klass, "method_missing", RUBY_METHOD_FUNC(rb_method_missing), -1);
rb_define_method(rb_klass, "to_s", RUBY_METHOD_FUNC(rb_to_s), 0);
rb_define_method(rb_klass, "has_parent?", RUBY_METHOD_FUNC(rb_has_parent_q), 0);
rb_define_method(rb_klass, "parent", RUBY_METHOD_FUNC(rb_parent), 0);
rb_define_method(rb_klass, "inside?", RUBY_METHOD_FUNC(rb_inside_q), 1);
rb_define_method(rb_klass, "to_IplConvKernel", RUBY_METHOD_FUNC(rb_to_IplConvKernel), 1);
rb_define_method(rb_klass, "create_mask", RUBY_METHOD_FUNC(rb_create_mask), 0);
rb_define_method(rb_klass, "width", RUBY_METHOD_FUNC(rb_width), 0);
rb_define_alias(rb_klass, "columns", "width");
rb_define_alias(rb_klass, "cols", "width");
rb_define_method(rb_klass, "height", RUBY_METHOD_FUNC(rb_height), 0);
rb_define_alias(rb_klass, "rows", "height");
rb_define_method(rb_klass, "depth", RUBY_METHOD_FUNC(rb_depth), 0);
rb_define_method(rb_klass, "channel", RUBY_METHOD_FUNC(rb_channel), 0);
rb_define_method(rb_klass, "data", RUBY_METHOD_FUNC(rb_data), 0);
rb_define_method(rb_klass, "clone", RUBY_METHOD_FUNC(rb_clone), 0);
rb_define_method(rb_klass, "copy", RUBY_METHOD_FUNC(rb_copy), -1);
rb_define_method(rb_klass, "to_8u", RUBY_METHOD_FUNC(rb_to_8u), 0);
rb_define_method(rb_klass, "to_8s", RUBY_METHOD_FUNC(rb_to_8s), 0);
rb_define_method(rb_klass, "to_16u", RUBY_METHOD_FUNC(rb_to_16u), 0);
rb_define_method(rb_klass, "to_16s", RUBY_METHOD_FUNC(rb_to_16s), 0);
rb_define_method(rb_klass, "to_32s", RUBY_METHOD_FUNC(rb_to_32s), 0);
rb_define_method(rb_klass, "to_32f", RUBY_METHOD_FUNC(rb_to_32f), 0);
rb_define_method(rb_klass, "to_64f", RUBY_METHOD_FUNC(rb_to_64f), 0);
rb_define_method(rb_klass, "vector?", RUBY_METHOD_FUNC(rb_vector_q), 0);
rb_define_method(rb_klass, "square?", RUBY_METHOD_FUNC(rb_square_q), 0);
rb_define_method(rb_klass, "to_CvMat", RUBY_METHOD_FUNC(rb_to_CvMat), 0);
rb_define_method(rb_klass, "sub_rect", RUBY_METHOD_FUNC(rb_sub_rect), -2);
rb_define_alias(rb_klass, "subrect", "sub_rect");
rb_define_method(rb_klass, "slice_width", RUBY_METHOD_FUNC(rb_slice_width), 1);
rb_define_method(rb_klass, "slice_height", RUBY_METHOD_FUNC(rb_slice_height), 1);
rb_define_method(rb_klass, "row", RUBY_METHOD_FUNC(rb_row), -2);
rb_define_method(rb_klass, "col", RUBY_METHOD_FUNC(rb_col), -2);
rb_define_alias(rb_klass, "column", "col");
rb_define_method(rb_klass, "each_row", RUBY_METHOD_FUNC(rb_each_row), 0);
rb_define_method(rb_klass, "each_col", RUBY_METHOD_FUNC(rb_each_col), 0);
rb_define_alias(rb_klass, "each_column", "each_col");
rb_define_method(rb_klass, "diag", RUBY_METHOD_FUNC(rb_diag), -1);
rb_define_alias(rb_klass, "diagonal", "diag");
rb_define_method(rb_klass, "size", RUBY_METHOD_FUNC(rb_size), 0);
rb_define_method(rb_klass, "dims", RUBY_METHOD_FUNC(rb_dims), 0);
rb_define_method(rb_klass, "dim_size", RUBY_METHOD_FUNC(rb_dim_size), 1);
rb_define_method(rb_klass, "[]", RUBY_METHOD_FUNC(rb_aref), -2);
rb_define_alias(rb_klass, "at", "[]");
rb_define_method(rb_klass, "[]=", RUBY_METHOD_FUNC(rb_aset), -2);
rb_define_method(rb_klass, "fill", RUBY_METHOD_FUNC(rb_fill), -1);
rb_define_alias(rb_klass, "set", "fill");
rb_define_method(rb_klass, "fill!", RUBY_METHOD_FUNC(rb_fill_bang), -1);
rb_define_alias(rb_klass, "set!", "fill!");
rb_define_method(rb_klass, "clear", RUBY_METHOD_FUNC(rb_clear), 0);
rb_define_alias(rb_klass, "set_zero", "clear");
rb_define_method(rb_klass, "clear!", RUBY_METHOD_FUNC(rb_clear_bang), 0);
rb_define_alias(rb_klass, "set_zero!", "clear!");
rb_define_method(rb_klass, "identity", RUBY_METHOD_FUNC(rb_set_identity), -1);
rb_define_method(rb_klass, "identity!", RUBY_METHOD_FUNC(rb_set_identity_bang), -1);
rb_define_method(rb_klass, "range", RUBY_METHOD_FUNC(rb_range), -1);
rb_define_method(rb_klass, "range!", RUBY_METHOD_FUNC(rb_range_bang), -1);
rb_define_method(rb_klass, "reshape", RUBY_METHOD_FUNC(rb_reshape), 1);
rb_define_method(rb_klass, "repeat", RUBY_METHOD_FUNC(rb_repeat), 1);
rb_define_method(rb_klass, "flip", RUBY_METHOD_FUNC(rb_flip), -1);
rb_define_method(rb_klass, "flip!", RUBY_METHOD_FUNC(rb_flip_bang), -1);
rb_define_method(rb_klass, "split", RUBY_METHOD_FUNC(rb_split), 0);
rb_define_singleton_method(rb_klass, "merge", RUBY_METHOD_FUNC(rb_merge), -2);
rb_define_method(rb_klass, "rand_shuffle", RUBY_METHOD_FUNC(rb_rand_shuffle), -1);
rb_define_method(rb_klass, "rand_shuffle!", RUBY_METHOD_FUNC(rb_rand_shuffle_bang), -1);
rb_define_method(rb_klass, "lut", RUBY_METHOD_FUNC(rb_lut), 1);
rb_define_method(rb_klass, "convert_scale", RUBY_METHOD_FUNC(rb_convert_scale), 1);
rb_define_method(rb_klass, "convert_scale_abs", RUBY_METHOD_FUNC(rb_convert_scale_abs), 1);
rb_define_method(rb_klass, "add", RUBY_METHOD_FUNC(rb_add), -1);
rb_define_alias(rb_klass, "+", "add");
rb_define_method(rb_klass, "sub", RUBY_METHOD_FUNC(rb_sub), -1);
rb_define_alias(rb_klass, "-", "sub");
rb_define_method(rb_klass, "mul", RUBY_METHOD_FUNC(rb_mul), -1);
rb_define_method(rb_klass, "mat_mul", RUBY_METHOD_FUNC(rb_mat_mul), -1);
rb_define_alias(rb_klass, "*", "mat_mul");
rb_define_method(rb_klass, "div", RUBY_METHOD_FUNC(rb_div), -1);
rb_define_alias(rb_klass, "/", "div");
rb_define_method(rb_klass, "and", RUBY_METHOD_FUNC(rb_and), -1);
rb_define_alias(rb_klass, "&", "and");
rb_define_method(rb_klass, "or", RUBY_METHOD_FUNC(rb_or), -1);
rb_define_alias(rb_klass, "|", "or");
rb_define_method(rb_klass, "xor", RUBY_METHOD_FUNC(rb_xor), -1);
rb_define_alias(rb_klass, "^", "xor");
rb_define_method(rb_klass, "not", RUBY_METHOD_FUNC(rb_not), 0);
rb_define_method(rb_klass, "not!", RUBY_METHOD_FUNC(rb_not_bang), 0);
rb_define_method(rb_klass, "eq", RUBY_METHOD_FUNC(rb_eq), 1);
rb_define_method(rb_klass, "gt", RUBY_METHOD_FUNC(rb_gt), 1);
rb_define_method(rb_klass, "ge", RUBY_METHOD_FUNC(rb_ge), 1);
rb_define_method(rb_klass, "lt", RUBY_METHOD_FUNC(rb_lt), 1);
rb_define_method(rb_klass, "le", RUBY_METHOD_FUNC(rb_le), 1);
rb_define_method(rb_klass, "ne", RUBY_METHOD_FUNC(rb_ne), 1);
rb_define_method(rb_klass, "in_range", RUBY_METHOD_FUNC(rb_in_range), 2);
rb_define_method(rb_klass, "abs_diff", RUBY_METHOD_FUNC(rb_abs_diff), 1);
rb_define_method(rb_klass, "count_non_zero", RUBY_METHOD_FUNC(rb_count_non_zero), 0);
rb_define_method(rb_klass, "sum", RUBY_METHOD_FUNC(rb_sum), 0);
rb_define_method(rb_klass, "avg", RUBY_METHOD_FUNC(rb_avg), -1);
rb_define_method(rb_klass, "avg_sdv", RUBY_METHOD_FUNC(rb_avg_sdv), -1);
rb_define_method(rb_klass, "sdv", RUBY_METHOD_FUNC(rb_sdv), -1);
rb_define_method(rb_klass, "min_max_loc", RUBY_METHOD_FUNC(rb_min_max_loc), -1);
rb_define_method(rb_klass, "dot_product", RUBY_METHOD_FUNC(rb_dot_product), 1);
rb_define_method(rb_klass, "cross_product", RUBY_METHOD_FUNC(rb_cross_product), 1);
rb_define_method(rb_klass, "transform", RUBY_METHOD_FUNC(rb_transform), -1);
rb_define_method(rb_klass, "perspective_transform", RUBY_METHOD_FUNC(rb_perspective_transform), 1);
rb_define_method(rb_klass, "mul_transposed", RUBY_METHOD_FUNC(rb_mul_transposed), -2);
rb_define_method(rb_klass, "trace", RUBY_METHOD_FUNC(rb_trace), 0);
rb_define_method(rb_klass, "transpose", RUBY_METHOD_FUNC(rb_transpose), 0);
rb_define_alias(rb_klass, "t", "transpose");
rb_define_method(rb_klass, "transpose!", RUBY_METHOD_FUNC(rb_transpose_bang), 0);
rb_define_alias(rb_klass, "t!", "transpose!");
rb_define_method(rb_klass, "det", RUBY_METHOD_FUNC(rb_det), 0);
rb_define_alias(rb_klass, "determinant", "det");
rb_define_method(rb_klass, "invert", RUBY_METHOD_FUNC(rb_invert), -1);
rb_define_method(rb_klass, "solve", RUBY_METHOD_FUNC(rb_solve), -1);
rb_define_method(rb_klass, "svd", RUBY_METHOD_FUNC(rb_svd), -1);
rb_define_method(rb_klass, "svbksb", RUBY_METHOD_FUNC(rb_svbksb), -1);
rb_define_method(rb_klass, "eigenvv", RUBY_METHOD_FUNC(rb_eigenvv), -1);
rb_define_method(rb_klass, "calc_covar_matrix", RUBY_METHOD_FUNC(rb_calc_covar_matrix), -1);
rb_define_method(rb_klass, "mahalonobis", RUBY_METHOD_FUNC(rb_mahalonobis), -1);
/* drawing function */
rb_define_method(rb_klass, "line", RUBY_METHOD_FUNC(rb_line), -1);
rb_define_method(rb_klass, "line!", RUBY_METHOD_FUNC(rb_line_bang), -1);
rb_define_method(rb_klass, "rectangle", RUBY_METHOD_FUNC(rb_rectangle), -1);
rb_define_method(rb_klass, "rectangle!", RUBY_METHOD_FUNC(rb_rectangle_bang), -1);
rb_define_method(rb_klass, "circle", RUBY_METHOD_FUNC(rb_circle), -1);
rb_define_method(rb_klass, "circle!", RUBY_METHOD_FUNC(rb_circle_bang), -1);
rb_define_method(rb_klass, "ellipse", RUBY_METHOD_FUNC(rb_ellipse), -1);
rb_define_method(rb_klass, "ellipse!", RUBY_METHOD_FUNC(rb_ellipse_bang), -1);
rb_define_method(rb_klass, "ellipse_box", RUBY_METHOD_FUNC(rb_ellipse_box), -1);
rb_define_method(rb_klass, "ellipse_box!", RUBY_METHOD_FUNC(rb_ellipse_box_bang), -1);
rb_define_method(rb_klass, "fill_poly", RUBY_METHOD_FUNC(rb_fill_poly), -1);
rb_define_method(rb_klass, "fill_poly!", RUBY_METHOD_FUNC(rb_fill_poly_bang), -1);
rb_define_method(rb_klass, "fill_convex_poly", RUBY_METHOD_FUNC(rb_fill_convex_poly), -1);
rb_define_method(rb_klass, "fill_convex_poly!", RUBY_METHOD_FUNC(rb_fill_convex_poly_bang), -1);
rb_define_method(rb_klass, "poly_line", RUBY_METHOD_FUNC(rb_poly_line), -1);
rb_define_method(rb_klass, "poly_line!", RUBY_METHOD_FUNC(rb_poly_line_bang), -1);
rb_define_method(rb_klass, "put_text", RUBY_METHOD_FUNC(rb_put_text), -1);
rb_define_method(rb_klass, "put_text!", RUBY_METHOD_FUNC(rb_put_text_bang), -1);
rb_define_method(rb_klass, "dft", RUBY_METHOD_FUNC(rb_dft), -1);
rb_define_method(rb_klass, "dct", RUBY_METHOD_FUNC(rb_dct), -1);
rb_define_method(rb_klass, "sobel", RUBY_METHOD_FUNC(rb_sobel), -1);
rb_define_method(rb_klass, "laplace", RUBY_METHOD_FUNC(rb_laplace), -1);
rb_define_method(rb_klass, "canny", RUBY_METHOD_FUNC(rb_canny), -1);
rb_define_method(rb_klass, "pre_corner_detect", RUBY_METHOD_FUNC(rb_pre_corner_detect), -1);
rb_define_method(rb_klass, "corner_eigenvv", RUBY_METHOD_FUNC(rb_corner_eigenvv), -1);
rb_define_method(rb_klass, "corner_min_eigen_val", RUBY_METHOD_FUNC(rb_corner_min_eigen_val), -1);
rb_define_method(rb_klass, "corner_harris", RUBY_METHOD_FUNC(rb_corner_harris), -1);
rb_define_private_method(rb_klass, "__find_corner_sub_pix", RUBY_METHOD_FUNC(rbi_find_corner_sub_pix), -1);
rb_define_method(rb_klass, "good_features_to_track", RUBY_METHOD_FUNC(rb_good_features_to_track), -1);
rb_define_method(rb_klass, "sample_line", RUBY_METHOD_FUNC(rb_sample_line), 2);
rb_define_method(rb_klass, "rect_sub_pix", RUBY_METHOD_FUNC(rb_rect_sub_pix), -1);
rb_define_method(rb_klass, "quadrangle_sub_pix", RUBY_METHOD_FUNC(rb_quadrangle_sub_pix), -1);
rb_define_method(rb_klass, "resize", RUBY_METHOD_FUNC(rb_resize), -1);
rb_define_method(rb_klass, "warp_affine", RUBY_METHOD_FUNC(rb_warp_affine), -1);
rb_define_singleton_method(rb_klass, "rotation_matrix2D", RUBY_METHOD_FUNC(rb_rotation_matrix2D), 3);
rb_define_method(rb_klass, "warp_perspective", RUBY_METHOD_FUNC(rb_warp_perspective), -1);
rb_define_singleton_method(rb_klass, "find_homography", RUBY_METHOD_FUNC(rb_find_homograpy), -1);
//rb_define_method(rb_klass, "get_perspective_transform", RUBY_METHOD_FUNC(rb_get_perspective_transform), -1);
//rb_define_alias(rb_klass, "warp_perspective_q_matrix", "get_perspective_transform");
rb_define_method(rb_klass, "remap", RUBY_METHOD_FUNC(rb_remap), -1);
//rb_define_method(rb_klass, "log_polar", RUBY_METHOD_FUNC(rb_log_polar), -1);
rb_define_method(rb_klass, "erode", RUBY_METHOD_FUNC(rb_erode), -1);
rb_define_method(rb_klass, "erode!", RUBY_METHOD_FUNC(rb_erode_bang), -1);
rb_define_method(rb_klass, "dilate", RUBY_METHOD_FUNC(rb_dilate), -1);
rb_define_method(rb_klass, "dilate!", RUBY_METHOD_FUNC(rb_dilate_bang), -1);
rb_define_method(rb_klass, "morphology", RUBY_METHOD_FUNC(rb_morphology), -1);
rb_define_method(rb_klass, "morphology_open", RUBY_METHOD_FUNC(rb_morphology_open), -1);
rb_define_method(rb_klass, "morphology_close", RUBY_METHOD_FUNC(rb_morphology_close), -1);
rb_define_method(rb_klass, "morphology_gradient", RUBY_METHOD_FUNC(rb_morphology_gradient), -1);
rb_define_method(rb_klass, "morphology_tophat", RUBY_METHOD_FUNC(rb_morphology_tophat), -1);
rb_define_method(rb_klass, "morphology_blackhat", RUBY_METHOD_FUNC(rb_morphology_blackhat), -1);
rb_define_method(rb_klass, "smooth", RUBY_METHOD_FUNC(rb_smooth), -1);
rb_define_method(rb_klass, "smooth_blur_no_scale", RUBY_METHOD_FUNC(rb_smooth_blur_no_scale), -1);
rb_define_method(rb_klass, "smooth_blur", RUBY_METHOD_FUNC(rb_smooth_blur), -1);
rb_define_method(rb_klass, "smooth_gaussian", RUBY_METHOD_FUNC(rb_smooth_gaussian), -1);
rb_define_method(rb_klass, "smooth_median", RUBY_METHOD_FUNC(rb_smooth_median), -1);
rb_define_method(rb_klass, "smooth_bilateral", RUBY_METHOD_FUNC(rb_smooth_bilateral), -1);
rb_define_method(rb_klass, "filter2d", RUBY_METHOD_FUNC(rb_filter2d), -1);
rb_define_method(rb_klass, "copy_make_border_constant", RUBY_METHOD_FUNC(rb_copy_make_border_constant), -1);
rb_define_method(rb_klass, "copy_make_border_replicate", RUBY_METHOD_FUNC(rb_copy_make_border_replicate), -1);
rb_define_method(rb_klass, "integral", RUBY_METHOD_FUNC(rb_integral), -1);
rb_define_method(rb_klass, "threshold", RUBY_METHOD_FUNC(rb_threshold), -1);
rb_define_method(rb_klass, "threshold_binary", RUBY_METHOD_FUNC(rb_threshold_binary), -1);
rb_define_method(rb_klass, "threshold_binary_inverse", RUBY_METHOD_FUNC(rb_threshold_binary_inverse), -1);
rb_define_method(rb_klass, "threshold_trunc", RUBY_METHOD_FUNC(rb_threshold_trunc), -1);
rb_define_method(rb_klass, "threshold_to_zero", RUBY_METHOD_FUNC(rb_threshold_to_zero), -1);
rb_define_method(rb_klass, "threshold_to_zero_inverse", RUBY_METHOD_FUNC(rb_threshold_to_zero_inverse), -1);
rb_define_method(rb_klass, "pyr_down", RUBY_METHOD_FUNC(rb_pyr_down), -1);
rb_define_method(rb_klass, "pyr_up", RUBY_METHOD_FUNC(rb_pyr_up), -1);
rb_define_method(rb_klass, "flood_fill", RUBY_METHOD_FUNC(rb_flood_fill), -1);
rb_define_method(rb_klass, "flood_fill!", RUBY_METHOD_FUNC(rb_flood_fill_bang), -1);
rb_define_method(rb_klass, "find_contours", RUBY_METHOD_FUNC(rb_find_contours), -1);
rb_define_method(rb_klass, "find_contours!", RUBY_METHOD_FUNC(rb_find_contours_bang), -1);
rb_define_method(rb_klass, "pyr_segmentation", RUBY_METHOD_FUNC(rb_pyr_segmentation), -1);
rb_define_method(rb_klass, "pyr_mean_shift_filtering", RUBY_METHOD_FUNC(rb_pyr_mean_shift_filtering), -1);
rb_define_method(rb_klass, "watershed", RUBY_METHOD_FUNC(rb_watershed), 1);
rb_define_method(rb_klass, "moments", RUBY_METHOD_FUNC(rb_moments), -1);
rb_define_method(rb_klass, "hough_lines_standard", RUBY_METHOD_FUNC(rb_hough_lines_standard), -1);
rb_define_method(rb_klass, "hough_lines_probabilistic", RUBY_METHOD_FUNC(rb_hough_lines_probabilistic), -1);
rb_define_method(rb_klass, "hough_lines_multi_scale", RUBY_METHOD_FUNC(rb_hough_lines_multi_scale), -1);
rb_define_method(rb_klass, "hough_circles_gradient", RUBY_METHOD_FUNC(rb_hough_circles_gradient), -1);
//rb_define_method(rb_klass, "dist_transform", RUBY_METHOD_FUNC(rb_dist_transform), -1);
rb_define_method(rb_klass, "inpaint_ns", RUBY_METHOD_FUNC(rb_inpaint_ns), 2);
rb_define_method(rb_klass, "inpaint_telea", RUBY_METHOD_FUNC(rb_inpaint_telea), 2);
rb_define_method(rb_klass, "equalize_hist", RUBY_METHOD_FUNC(rb_equalize_hist), 0);
rb_define_method(rb_klass, "match_template", RUBY_METHOD_FUNC(rb_match_template), -1);
rb_define_method(rb_klass, "match_shapes_i1", RUBY_METHOD_FUNC(rb_match_shapes_i1), -1);
rb_define_method(rb_klass, "match_shapes_i2", RUBY_METHOD_FUNC(rb_match_shapes_i2), -1);
rb_define_method(rb_klass, "match_shapes_i3", RUBY_METHOD_FUNC(rb_match_shapes_i3), -1);
rb_define_method(rb_klass, "mean_shift", RUBY_METHOD_FUNC(rb_mean_shift), 2);
rb_define_method(rb_klass, "cam_shift", RUBY_METHOD_FUNC(rb_cam_shift), 2);
rb_define_method(rb_klass, "snake_image", RUBY_METHOD_FUNC(rb_snake_image), -1);
rb_define_method(rb_klass, "optical_flow_hs", RUBY_METHOD_FUNC(rb_optical_flow_hs), -1);
rb_define_method(rb_klass, "optical_flow_lk", RUBY_METHOD_FUNC(rb_optical_flow_lk), -1);
rb_define_method(rb_klass, "optical_flow_bm", RUBY_METHOD_FUNC(rb_optical_flow_bm), -1);
rb_define_singleton_method(rb_klass, "find_fundamental_mat_7point", RUBY_METHOD_FUNC(rb_find_fundamental_mat_7point), -1);
rb_define_singleton_method(rb_klass, "find_fundamental_mat_8point", RUBY_METHOD_FUNC(rb_find_fundamental_mat_8point), -1);
rb_define_singleton_method(rb_klass, "find_fundamental_mat_ransac", RUBY_METHOD_FUNC(rb_find_fundamental_mat_ransac), -1);
rb_define_singleton_method(rb_klass, "find_fundamental_mat_lmeds", RUBY_METHOD_FUNC(rb_find_fundamental_mat_lmeds), -1);
rb_define_singleton_method(rb_klass, "compute_correspond_epilines", RUBY_METHOD_FUNC(rb_compute_correspond_epilines), 3);
rb_define_method(rb_klass, "save_image", RUBY_METHOD_FUNC(rb_save_image), 1);
}
VALUE
rb_allocate(VALUE klass)
{
return OPENCV_OBJECT(klass, 0);
}
/*
* call-seq:
* CvMat.new(<i>row, col[, depth = CV_8U][, channel = 3]</i>) -> cvmat
*
* Create col * row matrix. Each element set 0.
*
* Each element possigle range is set by <i>depth</i>. Default is unsigned 8bit.
*
* Number of channel is set by <i>channel</i>. <i>channel</i> should be 1..4.
*
*/
VALUE
rb_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE row, column, depth, channel;
rb_scan_args(argc, argv, "22", &row, &column, &depth, &channel);
DATA_PTR(self) = cvCreateMat(FIX2INT(row), FIX2INT(column),
CV_MAKETYPE(CVMETHOD("DEPTH", depth, CV_8U), argc < 4 ? 3 : FIX2INT(channel)));
return self;
}
/*
* call-seq:
* CvMat::load(<i>filename[,iscolor = CV_LOAD_IMAGE_COLOR]</i>)
*
* Load an image from file.
* iscolor = CV_LOAD_IMAGE_COLOR, the loaded image is forced to be a 3-channel color image
* iscolor = CV_LOAD_IMAGE_GRAYSCALE, the loaded image is forced to be grayscale
* iscolor = CV_LOAD_IMAGE_UNCHANGED, the loaded image will be loaded as is.
* Currently the following file format are supported.
* * Windows bitmaps - BMP,DIB
* * JPEG files - JPEG,JPG,JPE
* * Portable Network Graphics - PNG
* * Portable image format - PBM,PGM,PPM
* * Sun rasters - SR,RAS
* * TIFF files - TIFF,TIF
*/
VALUE
rb_load_imageM(int argc, VALUE *argv, VALUE self)
{
VALUE filename, iscolor;
rb_scan_args(argc, argv, "11", &filename, &iscolor);
Check_Type(filename, T_STRING);
int _iscolor;
if (TYPE(iscolor) == T_NIL) {
_iscolor = CV_LOAD_IMAGE_COLOR;
}
else {
Check_Type(iscolor, T_FIXNUM);
_iscolor = FIX2INT(iscolor);
}
CvMat *mat;
if ((mat = cvLoadImageM(StringValueCStr(filename), _iscolor)) == NULL) {
rb_raise(rb_eStandardError, "file does not exist or invalid format image.");
}
return OPENCV_OBJECT(rb_klass, mat);
}
/*
* nodoc
*/
VALUE
rb_method_missing(int argc, VALUE *argv, VALUE self)
{
/*
const char *to_str = "\\Ato_(\\w+)";
VALUE name, args, str[3], method;
rb_scan_args(argc, argv, "1*", &name, &args);
if (RARRAY_LEN(args) != 0)
return rb_call_super(argc, argv);
if(rb_reg_match(rb_reg_new(to_str, strlen(to_str), 0), rb_funcall(name, rb_intern("to_s"), 0)) == Qnil)
return rb_call_super(argc, argv);
str[0] = rb_str_new2("%s2%s");
str[1] = rb_color_model(self);
str[2] = rb_reg_nth_match(1, rb_backref_get());
method = rb_f_sprintf(3, str);
if (rb_respond_to(rb_module_opencv(), rb_intern(StringValuePtr(method))))
return rb_funcall(rb_module_opencv(), rb_intern(StringValuePtr(method)), 1, self);
return rb_call_super(argc, argv);
*/
VALUE name, args, method;
rb_scan_args(argc, argv, "1*", &name, &args);
method = rb_funcall(name, rb_intern("to_s"), 0);
if (RARRAY_LEN(args) != 0 || !rb_respond_to(rb_module_opencv(), rb_intern(StringValuePtr(method))))
return rb_call_super(argc, argv);
return rb_funcall(rb_module_opencv(), rb_intern(StringValuePtr(method)), 1, self);
}
/*
* call-seq:
* to_s -> string
*
* Return following string.
* m = CvMat.new(100, 100, :cv8u, 3)
* m.to_s # => <CvMat:100x100,depth=cv8u,channel=3>
*/
VALUE
rb_to_s(VALUE self)
{
const int i = 6;
VALUE str[i];
str[0] = rb_str_new2("<%s:%dx%d,depth=%s,channel=%d>");
str[1] = rb_str_new2(rb_class2name(CLASS_OF(self)));
str[2] = rb_width(self);
str[3] = rb_height(self);
str[4] = rb_depth(self);
str[5] = rb_channel(self);
return rb_f_sprintf(i, str);
}
/*
* call-seq:
* has_parent? -> true or false
*
* Return <tt>true</tt> if this matrix has parent object, otherwise <tt>false</tt>.
*/
VALUE
rb_has_parent_q(VALUE self)
{
return lookup_root_object(CVMAT(self)) ? Qtrue : Qfalse;
}
/*
* call-seq:
* parent -> obj or nil
*
* Return root object that refer this object.
*/
VALUE
rb_parent(VALUE self)
{
VALUE root = lookup_root_object(CVMAT(self));
return root ? root : Qnil;
}
/*
* call-seq:
* inside?(obj) -> true or false
*
*
*/
VALUE
rb_inside_q(VALUE self, VALUE object)
{
if (cCvPoint::rb_compatible_q(cCvPoint::rb_class(), object)) {
CvMat *mat = CVMAT(self);
int x = NUM2INT(rb_funcall(object, rb_intern("x"), 0));
int y = NUM2INT(rb_funcall(object, rb_intern("y"), 0));
if (cCvRect::rb_compatible_q(cCvRect::rb_class(), object)) {
int width = NUM2INT(rb_funcall(object, rb_intern("width"), 0));
int height = NUM2INT(rb_funcall(object, rb_intern("height"), 0));
return x >= 0 && y >= 0 && x < mat->width && x + width < mat->width && y < mat->height && y + height < mat->height ? Qtrue : Qfalse;
} else {
return x >= 0 && y >= 0 && x < mat->width && y < mat->height ? Qtrue : Qfalse;
}
}
rb_raise(rb_eArgError, "argument 1 should have method \"x\", \"y\"");
}
/*
* call-seq:
* to_IplConvKernel -> iplconvkernel
*
* Create IplConvKernel from this matrix.
*/
VALUE
rb_to_IplConvKernel(VALUE self, VALUE anchor)
{
CvMat *src = CVMAT(self);
CvPoint p = VALUE_TO_CVPOINT(anchor);
IplConvKernel *kernel = cvCreateStructuringElementEx(src->cols, src->rows, p.x, p.y, CV_SHAPE_CUSTOM, src->data.i);
return DEPEND_OBJECT(cIplConvKernel::rb_class(), kernel, self);
}
/*
* call-seq:
* create_mask -> cvmat(single-channel 8bit unsinged image)
*
* Create single-channel 8bit unsinged image that filled 0.
*/
VALUE
rb_create_mask(VALUE self)
{
VALUE mask = cCvMat::new_object(cvGetSize(CVARR(self)), CV_8UC1);
cvZero(CVARR(self));
return mask;
}
/*
* call-seq:
* width -> int
*
* Return number of columns.
*/
VALUE
rb_width(VALUE self)
{
return INT2FIX(CVMAT(self)->width);
}
/*
* call-seq:
* height -> int
*
* Return number of rows.
*/
VALUE
rb_height(VALUE self)
{
return INT2FIX(CVMAT(self)->height);
}
/*
* call-seq:
* depth -> symbol
*
* Return depth symbol. (see OpenCV::DEPTH)
*/
VALUE
rb_depth(VALUE self)
{
return rb_hash_aref(rb_funcall(rb_const_get(rb_module_opencv(), rb_intern("DEPTH")), rb_intern("invert"), 0), INT2FIX(CV_MAT_DEPTH(CVMAT(self)->type)));
}
/*
* call-seq:
* channel -> int (1 < channel < 4)
*
* Return number of channel.
*/
VALUE
rb_channel(VALUE self)
{
return INT2FIX(CV_MAT_CN(CVMAT(self)->type));
}
/*
* call-seq:
* data -> binary (by String class)
*
* Return raw data of matrix.
*/
VALUE
rb_data(VALUE self)
{
IplImage *image = IPLIMAGE(self);
return rb_str_new((char *)image->imageData, image->imageSize);
}
/*
* call-seq:
* clone -> cvmat
*
* Clone matrix. The parent and child relation is not succeeded.
* Instance-specific method is succeeded.
*
* module M
* def example
* true
* end
* end
*
* mat.extend M
* mat.example #=> true
* clone = mat.clone
* clone.example #=> true
* copy = mat.copy
* copy.example #=> raise NoMethodError
*/
VALUE
rb_clone(VALUE self)
{
VALUE clone = rb_obj_clone(self);
DATA_PTR(clone) = cvClone(CVARR(self));
return clone;
}
/*
* call-seq:
* copy() -> cvmat
* copy(<i>mat</i>) -> mat
* copy(<i>val</i>) -> array(include cvmat)
*
* Copy matrix. The parent and child relation is not succeeded.
* Instance-specific method is *NOT* succeeded. see also #clone.
*
* There are 3 kind behavior depending on the argument.
*
* copy()
* Return one copied matrix.
* copy(mat)
* Copy own elements to target matrix. Return nil.
* Size (or ROI) and channel and depth should be match.
* If own width or height does not match target matrix, raise CvUnmatchedSizes
* If own channel or depth does not match target matrix, raise CvUnmatchedFormats
* copy(val)
* The amounts of the specified number are copied. Return Array with copies.
* If you give the 0 or negative value. Return nil.
* mat.copy(3) #=> [mat1, mat2, mat3]
* mat.copy(-1) #=> nil
*
* When not apply to any, raise ArgumentError
*/
VALUE
rb_copy(int argc, VALUE *argv, VALUE self)
{
VALUE value, copied, tmp;
CvMat *src = CVMAT(self);
rb_scan_args(argc, argv, "01", &value);
if (argc == 0) {
CvSize size = cvGetSize(src);
copied = new_object(cvGetSize(src), cvGetElemType(src));
cvCopy(src, CVMAT(copied));
return copied;
}else{
if (rb_obj_is_kind_of(value, rb_klass)) {
cvCopy(src, CVMAT(value));
return Qnil;
}else if (rb_obj_is_kind_of(value, rb_cFixnum)) {
int n = FIX2INT(value);
if (n > 0) {
copied = rb_ary_new2(n);
for (int i = 0; i < n; i++) {
tmp = new_object(src->rows, src->cols, cvGetElemType(src));
cvCopy(src, CVMAT(tmp));
rb_ary_store(copied, i, tmp);
}
return copied;
}else{
return Qnil;
}
}else
rb_raise(rb_eArgError, "");
}
}
VALUE
copy(VALUE mat)
{
CvMat *src = CVMAT(mat);
VALUE copied = new_object(cvGetSize(src), cvGetElemType(src));
cvCopy(src, CVMAT(copied));
return copied;
}
/*
* call-seq:
* to_8u -> cvmat(depth = CV_8U)
*
* Return the new matrix that elements is converted to unsigned 8bit.
*/
VALUE
rb_to_8u(VALUE self)
{
CvMat *src = CVMAT(self);
VALUE dest = new_object(src->rows, src->cols, CV_MAKETYPE(CV_8U, CV_MAT_CN(src->type)));
cvConvert(src, CVMAT(dest));
return dest;
}
/*
* call-seq:
* to_8s -> cvmat(depth = CV_8S)
*
* Return the new matrix that elements is converted to signed 8bit.
*/
VALUE
rb_to_8s(VALUE self)
{
CvMat *src = CVMAT(self);
VALUE dest = new_object(src->rows, src->cols, CV_MAKETYPE(CV_8S, CV_MAT_CN(src->type)));
cvConvert(src, CVMAT(dest));
return dest;
}
/*
* call-seq:
* to_16u -> cvmat(depth = CV_16U)
*
* Return the new matrix that elements is converted to unsigned 16bit.
*/
VALUE rb_to_16u(VALUE self)
{
CvMat *src = CVMAT(self);
VALUE dest = new_object(src->rows, src->cols, CV_MAKETYPE(CV_16U, CV_MAT_CN(src->type)));
cvConvert(src, CVMAT(dest));
return dest;
}
/*
* call-seq:
* to_16s -> cvmat(depth = CV_16s)
*
* Return the new matrix that elements is converted to signed 16bit.
*/
VALUE
rb_to_16s(VALUE self)
{
CvMat *src = CVMAT(self);
VALUE dest = new_object(src->rows, src->cols, CV_MAKETYPE(CV_16S, CV_MAT_CN(src->type)));
cvConvert(src, CVMAT(dest));
return dest;
}
/*
* call-seq:
* to_32s -> cvmat(depth = CV_32S)
*
* Return the new matrix that elements is converted to signed 32bit.
*/
VALUE
rb_to_32s(VALUE self)
{
CvMat *src = CVMAT(self);
VALUE dest = new_object(src->rows, src->cols, CV_MAKETYPE(CV_32S, CV_MAT_CN(src->type)));
cvConvert(src, CVMAT(dest));
return dest;
}
/*
* call-seq:
* to_32f -> cvmat(depth = CV_32F)
*
* Return the new matrix that elements is converted to 32bit floating-point.
*/
VALUE
rb_to_32f(VALUE self)
{
CvMat *src = CVMAT(self);
VALUE dest = new_object(src->rows, src->cols, CV_MAKETYPE(CV_32F, CV_MAT_CN(src->type)));
cvConvert(src, CVMAT(dest));
return dest;
}
/*
* call-seq:
* to_64F -> cvmat(depth = CV_64F)
*
* Return the new matrix that elements is converted to 64bit floating-point.
*/
VALUE
rb_to_64f(VALUE self)
{
CvMat *src = CVMAT(self);
VALUE dest = new_object(src->rows, src->cols, CV_MAKETYPE(CV_64F, CV_MAT_CN(src->type)));
cvConvert(src, CVMAT(dest));
return dest;
}
/*
* call-seq:
* vector? -> true or false
*
* If #width or #height is 1, return true. Otherwise return false.
*/
VALUE
rb_vector_q(VALUE self)
{
CvMat *mat = CVMAT(self);
return (mat->width == 1|| mat->height == 1) ? Qtrue : Qfalse;
}
/*
* call-seq:
* square? -> true or false
*
* If #width == #height return true. Otherwise return false.
*/
VALUE
rb_square_q(VALUE self)
{
CvMat *mat = CVMAT(self);
return mat->width == mat->height ? Qtrue : Qfalse;
}
/************************************************************
cxcore function
************************************************************/
/*
* Return CvMat object with reference to caller-object.
*
* src = CvMat.new(10, 10)
* src.has_parent? #=> false
* src.parent #=> nil
* mat = src.to_CvMat
* mat.has_parent? #=> true
* mat.parent #=> CvMat object "src"
*
* This case, 'src' is root-object. and 'mat' is child-object refer to 'src'.
* src <=refer= mat
* In C, 'src->data' and 'mat->data' is common. Therefore, they cause the change each other.
* object 'src' don't GC.
*/
VALUE
rb_to_CvMat(VALUE self)
{
return DEPEND_OBJECT(rb_klass, cvGetMat(CVARR(self), CVALLOC(CvMat)), self);
}
/*
* call-seq:
* sub_rect(<i>rect</i>) -> cvmat
* sub_rect(<i>topleft</i>, <i>size</i>) -> cvmat
* sub_rect(<i>x, y, width, height</i>) -> cvmat
*
* Return parts of self as CvMat.
*
* <i>p</i> or <i>x</i>,<i>y</i> mean top-left coordinate.
* <i>size</i> or <i>width</i>,<i>height</i> is size.
*
* link:../images/CvMat_sub_rect.png
*/
VALUE
rb_sub_rect(VALUE self, VALUE args)
{
CvRect area;
CvPoint topleft;
CvSize size;
switch(RARRAY_LEN(args)) {
case 1:
area = VALUE_TO_CVRECT(RARRAY_PTR(args)[0]);
break;
case 2:
topleft = VALUE_TO_CVPOINT(RARRAY_PTR(args)[0]);
size = VALUE_TO_CVSIZE(RARRAY_PTR(args)[1]);
area.x = topleft.x;
area.y = topleft.y;
area.width = size.width;
area.height = size.height;
break;
case 4:
area.x = NUM2INT(RARRAY_PTR(args)[0]);
area.y = NUM2INT(RARRAY_PTR(args)[1]);
area.width = NUM2INT(RARRAY_PTR(args)[2]);
area.height = NUM2INT(RARRAY_PTR(args)[3]);
break;
default:
rb_raise(rb_eArgError, "wrong number of arguments (%ld of 1 or 2 or 4)", RARRAY_LEN(args));
}
return DEPEND_OBJECT(rb_klass,
cvGetSubRect(CVARR(self), CVALLOC(CvMat), area),
self);
}
/*
* call-seq:
* slice_width(<i>n</i>)
*
* The matrix is divided into <i>n</i> piece by the width.
* If it cannot be just divided, warning is displayed.
*
* e.g.
* m = OpenCV::CvMat.new(10, 10) #=> size 10x10 matrix
* ml, mr = m.slice_width(2) #=> 5x10 and 5x10 matrix
*
* ml, mm, mr = m.slice_width(3)#=> 3x10 3x10 3x10 matrix
* warning : width does not div correctly.
*/
VALUE
rb_slice_width(VALUE self, VALUE num)
{
int n = NUM2INT(num);
if (n < 1) {rb_raise(rb_eArgError, "number of piece should be > 0");}
CvSize size = cvGetSize(CVARR(self));
if (size.width % n != 0) {rb_warn("width does not div correctly.");}
int div_x = size.width / n;
VALUE ary = rb_ary_new2(n);
for (int i = 0; i < n; i++) {
CvRect rect = {div_x * i, 0, div_x, size.height};
rb_ary_push(ary, DEPEND_OBJECT(rb_klass, cvGetSubRect(CVARR(self), CVALLOC(CvMat), rect), self));
}
return ary;
}
/*
* call-seq:
* slice_height(<i>n</i>)
*
* The matrix is divided into <i>n</i> piece by the height.
* If it cannot be just divided, warning is displayed.
*
* see also #slice_width.
*/
VALUE
rb_slice_height(VALUE self, VALUE num)
{
int n = NUM2INT(num);
if (n < 1) {rb_raise(rb_eArgError, "number of piece should be > 0");}
CvSize size = cvGetSize(CVARR(self));
if (size.height % n != 0) {rb_warn("height does not div correctly.");}
int div_y = size.height / n;
VALUE ary = rb_ary_new2(n);
for (int i = 0; i < n; i++) {
CvRect rect = {0, div_y * i, size.width, div_y};
rb_ary_push(ary, DEPEND_OBJECT(rb_klass, cvGetSubRect(CVARR(self), CVALLOC(CvMat), rect), self));
}
return ary;
}
/*
* call-seq:
* row(<i>n</i>) -> Return row
* row(<i>n1, n2, ...</i>) -> Return Array of row
*
* Return row(or rows) of matrix.
* argument should be Fixnum or CvSlice compatible object.
*/
VALUE
rb_row(VALUE self, VALUE args)
{
int len = RARRAY_LEN(args);
if (len < 1) {rb_raise(rb_eArgError, "wrong number of argument.(more than 1)");}
VALUE ary = rb_ary_new2(len);
for (int i = 0; i < len; i++) {
VALUE value = rb_ary_entry(args, i);
if (FIXNUM_P(value)) {
rb_ary_store(ary, i, DEPEND_OBJECT(rb_klass, cvGetRow(CVARR(self), CVALLOC(CvMat), FIX2INT(value)), self));
}else{
CvSlice slice = VALUE_TO_CVSLICE(value);
rb_ary_store(ary, i, DEPEND_OBJECT(rb_klass, cvGetRows(CVARR(self), CVALLOC(CvMat), slice.start_index, slice.end_index), self));
}
}
return RARRAY_LEN(ary) > 1 ? ary : rb_ary_entry(ary, 0);
}
/*
* call-seq:
* col(<i>n</i>) -> Return column
* col(<i>n1, n2, ...</i>) -> Return Array of columns
*
* Return column(or columns) of matrix.
* argument should be Fixnum or CvSlice compatible object.
*/
VALUE
rb_col(VALUE self, VALUE args)
{
int len = RARRAY_LEN(args);
if (len < 1) {rb_raise(rb_eArgError, "wrong number of argument.(more than 1)");}
VALUE ary = rb_ary_new2(len);
for (int i = 0; i < len; i++) {
VALUE value = rb_ary_entry(args, i);
if (FIXNUM_P(value)) {
rb_ary_store(ary, i, DEPEND_OBJECT(rb_klass, cvGetCol(CVARR(self), CVALLOC(CvMat), FIX2INT(value)), self));
}else{
CvSlice slice = VALUE_TO_CVSLICE(value);
rb_ary_store(ary, i, DEPEND_OBJECT(rb_klass, cvGetCols(CVARR(self), CVALLOC(CvMat), slice.start_index, slice.end_index), self));
}
}
return RARRAY_LEN(ary) > 1 ? ary : rb_ary_entry(ary, 0);
}
/*
* call-seq:
* each_row{|row| ... } -> self
*
* Calls block once for each row in self, passing that element as a parameter.
*
* see also CvMat#each_col
*/
VALUE
rb_each_row(VALUE self)
{
int rows = CVMAT(self)->rows;
for (int i = 0; i < rows; i++) {
rb_yield(DEPEND_OBJECT(rb_klass, cvGetRow(CVARR(self), CVALLOC(CvMat), i), self));
}
return self;
}
/*
* call-seq:
* each_col{|col| ... } -> self
*
* Calls block once for each column in self, passing that element as a parameter.
*
* see also CvMat#each_row
*/
VALUE
rb_each_col(VALUE self)
{
int cols = CVMAT(self)->cols;
for (int i = 0; i < cols; i++) {
rb_yield(DEPEND_OBJECT(rb_klass, cvGetCol(CVARR(self), CVALLOC(CvMat), i), self));
}
return self;
}
/*
* call-seq:
* diag(<i>[val = 0]</i>) -> cvmat
*
* Return one of array diagonals.
* <i>val</i> is zeo corresponds to the main diagonal, -1 corresponds to the diagonal above the main etc, 1 corresponds to the diagonal below the main etc.
*
*/
VALUE
rb_diag(int argc, VALUE *argv, VALUE self)
{
VALUE val;
if (rb_scan_args(argc, argv, "01", &val) < 1) {
val = INT2FIX(0);
}
return DEPEND_OBJECT(rb_klass, cvGetDiag(CVARR(self), CVALLOC(CvMat), NUM2INT(val)), self);
}
/*
* call-seq:
* size -> cvsize
*
* Return size by CvSize
*/
VALUE
rb_size(VALUE self)
{
return cCvSize::new_object(cvGetSize(CVARR(self)));
}
/*
VALUE rb_elem_type(VALUE self) {
return INT2FIX(cvGetElemType(CVARR(self)));
}
*/
/*
* call-seq:
* dims -> array(int, int, ...)
*
* Return number of array dimensions and their sizes or the size of particular dimension.
* In case of CvMat it always returns 2 regardless of number of matrix rows.
*/
VALUE
rb_dims(VALUE self)
{
int size[CV_MAX_DIM];
int dims = cvGetDims(CVARR(self), size);
VALUE ary = rb_ary_new2(dims);
for (int i = 0; i < dims; i++) {
rb_ary_store(ary, i, INT2FIX(size[i]));
}
return ary;
}
/*
* call-seq:
* dim_size(<i>index</i>) -> int
*
* Return number of dimension.
* almost same as CvMat#dims[<i>index</i>].
* If the dimension specified with index doesn't exist, CvStatusOutOfRange raise.
*/
VALUE
rb_dim_size(VALUE self, VALUE index)
{
return INT2FIX(cvGetDimSize(CVARR(self), FIX2INT(index)));
}
/*
* call-seq:
* [<i>idx1[,idx2]...</i>]
*
* Return value of the particular array element as CvScalar.
*/
VALUE
rb_aref(VALUE self, VALUE args)
{
int index[CV_MAX_DIM];
for (int i = 0; i < RARRAY_LEN(args); i++) {
index[i] = NUM2INT(rb_ary_entry(args, i));
}
CvScalar scalar = cvScalarAll(0);
switch(RARRAY_LEN(args)) {
case 1:
scalar = cvGet1D(CVARR(self), index[0]);
break;
case 2:
scalar = cvGet2D(CVARR(self), index[0], index[1]);
break;
default:
scalar = cvGetND(CVARR(self), index);
}
return cCvScalar::new_object(scalar);
}
/*
* call-seq:
* [<i>idx1[,idx2]...</i>] = <i>value</i>
*
* Set value of the particular array element to <i>value</i>.
* <i>value</i> should be CvScalar.
*/
VALUE
rb_aset(VALUE self, VALUE args)
{
CvScalar scalar = VALUE_TO_CVSCALAR(rb_ary_pop(args));
int index[CV_MAX_DIM];
for (int i = 0; i < RARRAY_LEN(args); i++) {
index[i] = NUM2INT(rb_ary_entry(args, i));
}
switch(RARRAY_LEN(args)) {
case 1:
cvSet1D(CVARR(self), index[0], scalar);
break;
case 2:
cvSet2D(CVARR(self), index[0], index[1], scalar);
break;
default:
cvSetND(CVARR(self), index, scalar);
}
return self;
}
/*
* call-seq:
* fill(<i>value[, mask]</i>) -> cvmat
*
* Return CvMat copied value to every selected element. value should be CvScalar or compatible object.
* self[I] = value if mask(I)!=0
*
* note: This method support ROI on IplImage class. but COI not support. COI should not be set.
* image = IplImage.new(10, 20) #=> create 3 channel image.
* image.coi = 1 #=> set COI
* image.fill(CvScalar.new(10, 20, 30)) #=> raise CvBadCOI error.
*/
VALUE
rb_fill(int argc, VALUE *argv, VALUE self)
{
return rb_fill_bang(argc, argv, copy(self));
}
/*
* call-seq:
* fill!(<i>value[, mask]</i>) -> self
*
* Copie value to every selected element.
* self[I] = value if mask(I)!=0
*
* see also #fill.
*/
VALUE
rb_fill_bang(int argc, VALUE *argv, VALUE self)
{
VALUE value, mask;
rb_scan_args(argc, argv, "11", &value, &mask);
cvSet(CVARR(self), VALUE_TO_CVSCALAR(value), MASK(mask));
return self;
}
/*
* call-seq:
* save_image(<i>filename</i>) -> self
*
* Saves an image to file. The image format is chosen depending on the filename extension.
* Only 8bit single-channel or 3-channel(with 'BGR' channel order) image can be saved.
*
* e.g.
* image = OpenCV::CvMat.new(10, 10, CV_8U, 3)
* image.save_image("image.jpg") #=> save as JPEG format
* image.save_image("image.png") #=> save as PNG format
*/
VALUE
rb_save_image(VALUE self, VALUE filename)
{
Check_Type(filename, T_STRING);
cvSaveImage(StringValueCStr(filename), CVARR(self));
return self;
}
/*
* call-seq:
* clear -> cvmat
*
* Return new matrix all element-value cleared.
*/
VALUE
rb_clear(VALUE self)
{
return rb_clear_bang(copy(self));
}
/*
* call-seq:
* clear! -> self
*
* Clear all element-value. Return self.
*/
VALUE
rb_clear_bang(VALUE self)
{
cvSetZero(CVARR(self));
return self;
}
/*
* call-seq:
* identity(<i>[val = [1]]</i>) -> cvmat
*
* Return initializes scaled identity matrix.
* <i>val</i> should be CvScalar.
*
* arr(i, j) = val if i = j, 0 otherwise
*/
VALUE
rb_set_identity(int argc, VALUE *argv, VALUE self)
{
return rb_set_identity_bang(argc, argv, copy(self));
}
/*
* call-seq:
* identity!(<i>[val = [1]]</i>) -> self
*
* Initialize scaled identity matrix.
* <i>val</i> should be CvScalar.
*
* arr(i, j) = val if i = j, 0 otherwise
*/
VALUE
rb_set_identity_bang(int argc, VALUE *argv, VALUE self)
{
VALUE val;
CvScalar value;
if (rb_scan_args(argc, argv, "01", &val) < 1) {
value = cvRealScalar(1);
}else{
value = VALUE_TO_CVSCALAR(val);
}
cvSetIdentity(CVARR(self), value);
return self;
}
/*
* call-seq:
* range(start, end) -> cvmat
*
* Create and return filled matrix with given range of numbers.
*
* see range!
*/
VALUE
rb_range(int argc, VALUE *argv, VALUE self)
{
return rb_range_bang(argc, argv, copy(self));
}
/*
* call-seq:
* range!(start, end) -> self
*
* Fills matrix with given range of numbers.
*
* initializes the matrix as following:
* arr(i,j)=(end-start)*(i*cols(arr)+j)/(cols(arr)*rows(arr))
* For example, the following code will initilize 1D vector with subsequent integer numbers.
* m = CvMat.new(1, 10, :cv32s)
* m.range!(0, m.cols); // m will be initialized as [0,1,2,3,4,5,6,7,8,9]
*/
VALUE
rb_range_bang(int argc, VALUE *argv, VALUE self)
{
VALUE start, end;
rb_scan_args(argc, argv, "20", &start, &end);
cvRange(CVARR(self), NUM2DBL(start), NUM2DBL(end));
return self;
}
/*
* call-seq:
* reshape(<i>[:rows => num][, :channel => cn]</i>) -> cvmat(refer self)
*
* Change shape of matrix/image without copying data.
*
* e.g.
* mat = CvMat.new(3, 3, CV_8U, 3) #=> 3x3 3-channel matrix
* vec = mat.reshape(:rows => 1) #=> 1x9 3-channel matrix
* ch1 = mat.reshape(:channel => 1) #=> 9x9 1-channel matrix
*/
VALUE
rb_reshape(VALUE self, VALUE hash)
{
if (TYPE(hash) != T_HASH)
rb_raise(rb_eTypeError, "argument should be Hash that contaion key (:row, :channel).");
VALUE channel = rb_hash_aref(hash, ID2SYM(rb_intern("channel")));
VALUE rows = rb_hash_aref(hash, ID2SYM(rb_intern("rows")));
return DEPEND_OBJECT(rb_klass, cvReshape(CVARR(self), CVALLOC(CvMat), NIL_P(channel) ? 0 : FIX2INT(channel), NIL_P(rows) ? 0 : FIX2INT(rows)), self);
}
/*
* call-seq:
* repeat(<i>mat</i>) -> cvmat
*
* Tiled <i>mat</i> by self.
*/
VALUE
rb_repeat(VALUE self, VALUE object)
{
if (!rb_obj_is_kind_of(object, rb_class()))
rb_raise(rb_eTypeError, "argument should be CvMat subclass.");
cvRepeat(CVARR(self), CVARR(object));
return object;
}
/*
* call-seq:
* flip(:x) -> cvmat
* flip(:y) -> cvmat
* flip(:xy) -> cvmat
* flip -> cvmat
*
* Return new flipped 2D array.
* * flip(:x) - flip around horizontal
* * flip(:y) - flip around vertical
* * flip(:xy) - flip around both axises
* * flip - flip around vertical
*/
VALUE
rb_flip(int argc, VALUE *argv, VALUE self)
{
return rb_flip_bang(argc, argv, copy(self));
}
/*
* call-seq:
* flip!(:x) -> self
* flip!(:y) -> self
* flip!(:xy) -> self
* flip! -> self
*
* Flip 2D array. Return self.
*
* see also CvMat#flip
*/
VALUE
rb_flip_bang(int argc, VALUE *argv, VALUE self)
{
VALUE format;
int mode = 0;
if (rb_scan_args(argc, argv, "01", &format) > 0) {
if (rb_to_id(format) == rb_intern("x"))
mode = 1;
else if (rb_to_id(format) == rb_intern("y"))
mode = 0;
else if (rb_to_id(format) == rb_intern("xy"))
mode = -1;
else
rb_warn("argument may be :x or :y or :xy");
}
cvFlip(CVARR(self), NULL, mode);
return self;
}
/*
* call-seq:
* split -> array(include cvmat)
*
* Divides multi-channel array into several single-chanel arrays.
*
* e.g.
* image = CvMat.new 640, 480, CV_8U, 3 #=> 3-channel image
* image.split #=> [image1, image2, image3] : each image have single-channel
*
* e.g. switch red <-> blue channel.
* image = IplImage.load "sample.bmp"
* i = image.split
* new_image = CvMat.merge i[2], i[1], i[0]
*/
VALUE
rb_split(VALUE self)
{
int type = CVMAT(self)->type, depth = CV_MAT_DEPTH(type), channel = CV_MAT_CN(type);
CvSize size = cvGetSize(CVARR(self));
CvMat *dest[] = {NULL, NULL, NULL, NULL};
for (int i = 0; i < channel; i++)
dest[i] = cvCreateMat(size.height, size.width, CV_MAKETYPE(depth, 1));
cvSplit(CVARR(self), dest[0], dest[1], dest[2], dest[3]);
VALUE ary = rb_ary_new2(channel);
for (int i = 0; i < channel; i++)
rb_ary_store(ary, i, OPENCV_OBJECT(rb_klass, dest[i]));
return ary;
}
/*
* call-seq:
* CvMat.merge(<i>mat1[,mat2][,mat3][,mat4]</i>) -> cvmat
*
* Composes multi-channel array from several single-channel arrays.
* Each argument should be single-channel image(CvMat or subclass).
* All image should be same size and same depth.
*
* see also CvMat#split
*/
VALUE
rb_merge(VALUE klass, VALUE args)
{
VALUE object, dest;
int len = RARRAY_LEN(args);
if (!(len > 0) || len > CV_CN_MAX) {
rb_raise(rb_eArgError, "wrong number of argument (%d for 1..4)", len);
}
CvMat *src[] = {NULL, NULL, NULL, NULL}, *tmp = 0;
for (int i = 0; i < len; i++) {
if (rb_obj_is_kind_of((object = rb_ary_entry(args, i)), rb_klass)) {
src[i] = CVMAT(object);
if (CV_MAT_CN(src[i]->type) != 1) {
rb_raise(rb_eStandardError, "image should be single-channel CvMat.");
}
if (!tmp)
tmp = src[i];
else{
if (!CV_ARE_SIZES_EQ(tmp, src[i]))
rb_raise(rb_eStandardError, "image size should be same.");
if (!CV_ARE_DEPTHS_EQ(tmp, src[i]))
rb_raise(rb_eStandardError, "image depth should be same.");
}
}else if (NIL_P(object)) {
src[i] = NULL;
}else
rb_raise(rb_eTypeError, "argument should be CvMat or subclass of it.");
}
dest = new_object(cvGetSize(tmp), CV_MAKETYPE(CV_MAT_DEPTH(tmp->type), len));
cvMerge(src[0], src[1], src[2], src[3], CVARR(dest));
return dest;
}
/*
* call-seq:
* CvMat.mix_channels(srcs,dests,from_to = {1 => 1, 2 => 2, 3 => 3, 4 => 4}) -> dests
*/
VALUE
rb_mix_channels(int argc, VALUE *argv, VALUE self)
{
VALUE srcs, dests, from_to;
rb_scan_args(argc, argv, "21", &srcs, &dests, &from_to);
/* not yet */
return Qnil;
}
/*
* call-seq:
* rand_shuffle([seed = nil][,iter_factor = 1]) -> cvmat
*
* Return shuffled matrix
*
* see rand_shuffle!
*/
VALUE
rb_rand_shuffle(int argc, VALUE *argv, VALUE self)
{
return rb_rand_shuffle_bang(argc, argv, copy(self));
}
/*
* call-seq:
* rand_shuffle!([seed = nil][,iter_factor = 1]) -> cvmat
*
* Shuffles the matrix by swapping randomly chosen pairs of the matrix elements on each iteration
* (where each element may contain several components in case of multi-channel arrays). The number of
* iterations (i.e. pairs swapped) is (iter_factor*mat.rows*mat.cols).round, so iter_factor=0 means
* that no shuffling is done, iter_factor=1 means that the function swaps rows(mat)*cols(mat) random
* pairs etc.
*/
VALUE
rb_rand_shuffle_bang(int argc, VALUE *argv, VALUE self)
{
VALUE seed, iter;
CvRNG rng;
rb_scan_args(argc, argv, "02", &seed, &iter);
if(NIL_P(seed))
cvRandShuffle(CVARR(self), NULL, IF_INT(iter, 1));
else{
rng = cvRNG(rb_num2ll(seed));
cvRandShuffle(CVARR(self), &rng, IF_INT(iter, 1));
}
return self;
}
/*
* call-seq:
* lut(<i>lookup_table</i>) -> cvmat
*
* Return new matrix performed lookup-table transform.
*
* <i>lookup_table</i> should be CvMat that have 256 element (e.g. 1x256 matrix).
* Otherwise, raise CvStatusBadArgument error.
*
* And <i>lookup_table</i> should either have a single-channel, or the same number of channels.
* When single-channel lookup-table given, same table is used for all channels.
*/
VALUE
rb_lut(VALUE self, VALUE lut)
{
VALUE dest = copy(self);
cvLUT(CVARR(self), CVARR(dest), CVARR(lut));
return dest;
}
/*
* call-seq:
* convert_scale(<i>:depth => nil, :scale => 1.0, :shift => 0.0</i>)
*
* Return new array with optional linear transformation.
* mat(I) = src(I) * scale + (shift, shift, ...)
*/
VALUE
rb_convert_scale(VALUE self, VALUE hash)
{
if (TYPE(hash) != T_HASH)
rb_raise(rb_eTypeError, "argument should be Hash that contaion key [:depth, :scale, :shift].");
VALUE depth = rb_hash_aref(hash, ID2SYM(rb_intern("depth"))),
scale = rb_hash_aref(hash, ID2SYM(rb_intern("scale"))),
shift = rb_hash_aref(hash, ID2SYM(rb_intern("shift"))),
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CVMETHOD("DEPTH", depth, CV_MAT_DEPTH(CVMAT(self)->type)), CV_MAT_CN(CVMAT(self)->type)));
cvConvertScale(CVARR(self), CVARR(dest), IF_DBL(scale, 1.0), IF_DBL(shift, 0.0));
return dest;
}
/*
* call-seq:
* convert_scale_abs(<i>:scale => 1.0, :shift => 0.0</i>)
*
* Return new array with optional linear transformation.
* It is similar to CvMat#convert_scale, but it stores absolute values of the conversion result
* mat(I) = (src(I) * scale + (shift, shift, ...)).abs
*/
VALUE
rb_convert_scale_abs(VALUE self, VALUE hash)
{
if (TYPE(hash) != T_HASH)
rb_raise(rb_eTypeError, "argument should be Hash that contaion key [:depth, :scale, :shift].");
VALUE
scale = rb_hash_aref(hash, ID2SYM(rb_intern("scale"))),
shift = rb_hash_aref(hash, ID2SYM(rb_intern("shift"))),
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_8U, CV_MAT_CN(CVMAT(self)->type)));
cvConvertScaleAbs(CVARR(self), CVARR(dest), IF_DBL(scale, 1.0), IF_DBL(shift, 0.0));
return dest;
}
/*
* call-seq:
* add(<i>val[,mask]</i>) -> cvmat
*
* Return new matrix computed per-element sum.
* <i>val</i> should be CvMat or CvScalar.
* If <i>val</i> is CvMat, it must have same type (depth and channel).
* <i>mask</i> should be CvMat(8bit single-channel).
* For each element (I)
* dst(I) = src1(I) + src2(I) if mask(I) != 0
*/
VALUE
rb_add(int argc, VALUE *argv, VALUE self)
{
VALUE val, mask, dest;
rb_scan_args(argc, argv, "11", &val, &mask);
dest = copy(self);
if (rb_obj_is_kind_of(val, rb_klass))
cvAdd(CVARR(self), CVARR(val), CVARR(dest), MASK(mask));
else
cvAddS(CVARR(self), VALUE_TO_CVSCALAR(val), CVARR(dest), MASK(mask));
return dest;
}
/*
* call-seq:
* sub(<i>val[,mask]</i>) -> cvmat
*
* Return new matrix computed per-element difference.
* <i>val</i> should be CvMat or CvScalar.
* If <i>val</i> is CvMat, it must have same type (depth and channel).
* <i>mask</i> should be CvMat(8bit single-channel).
* For each element (I)
* dst(I) = src1(I) - src2(I) if mask(I) != 0
*/
VALUE
rb_sub(int argc, VALUE *argv, VALUE self)
{
VALUE val, mask, dest;
rb_scan_args(argc, argv, "11", &val, &mask);
dest = copy(self);
if (rb_obj_is_kind_of(val, rb_klass)) {
cvSub(CVARR(self), CVARR(val), CVARR(dest), MASK(mask));
}
else {
cvSubS(CVARR(self), VALUE_TO_CVSCALAR(val), CVARR(dest), MASK(mask));
}
return dest;
}
/*
* call-seq:
* mul(<i>val[,scale = 1.0]</i>) -> cvmat
*
* Return new matrix computed per-element product.
* <i>val</i> should be CvMat or CvScalar.
* If <i>val</i> is CvMat, it must have same type (depth and channel).
* For each element (I)
* dst(I) = scale * src1(I) * src2(I)
*/
VALUE
rb_mul(int argc, VALUE *argv, VALUE self)
{
VALUE val, scale, dest;
if (rb_scan_args(argc, argv, "11", &val, &scale) < 2)
scale = rb_float_new(1.0);
dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
if (rb_obj_is_kind_of(val, rb_klass)) {
cvMul(CVARR(self), CVARR(val), CVARR(dest), NUM2DBL(scale));
}else{
CvScalar scl = VALUE_TO_CVSCALAR(val);
VALUE mat = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSet(CVARR(mat), scl);
cvMul(CVARR(self), CVARR(mat), CVARR(dest), NUM2DBL(scale));
}
return dest;
}
/*
* call-seq:
* mat_mul(<i>val[,shiftvec]</i>) -> cvmat
* Performs matrix multiplication
* dst = src1 * src2 + shiftvec
* <i>val</i> and <i>shiftvec</i> should be CvMat
* All the matrices should have the same data type and coordinated sizes.
* Real or complex floating-point matrices are supported.
*/
VALUE
rb_mat_mul(int argc, VALUE *argv, VALUE self)
{
VALUE val, shiftvec, dest;
rb_scan_args(argc, argv, "11", &val, &shiftvec);
dest = new_object(CVMAT(self)->rows, CVMAT(val)->cols, cvGetElemType(CVARR(self)));
if (NIL_P(shiftvec))
cvMatMul(CVARR(self), CVARR(val), CVARR(dest));
else
cvMatMulAdd(CVARR(self), CVARR(val), CVARR(shiftvec), CVARR(dest));
return dest;
}
/*
* call-seq:
* div(<i>val[,scale = 1.0]</i>) -> cvmat
*
* Return new matrix computed per-element division.
* <i>val</i> should be CvMat or CvScalar.
* If <i>val</i> is CvMat, it must have same type (depth and channel).
* For each element (I)
* dst(I) = scale * src1(I) / src2(I)
*/
VALUE
rb_div(int argc, VALUE *argv, VALUE self)
{
VALUE val, scale, dest;
if (rb_scan_args(argc, argv, "11", &val, &scale) < 2)
scale = rb_float_new(1.0);
dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
if (rb_obj_is_kind_of(val, rb_klass)) {
cvDiv(CVARR(self), CVARR(val), CVARR(dest), NUM2DBL(scale));
}else{
CvScalar scl = VALUE_TO_CVSCALAR(val);
VALUE mat = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSet(CVARR(mat), scl);
cvDiv(CVARR(self), CVARR(mat), CVARR(dest), NUM2DBL(scale));
}
return dest;
}
/*
* call-seq:
* and(<i>val[,mask]</i>) -> cvmat
*
* Return new matrix computed per-element bit-wise conjunction.
* <i>val</i> should be CvMat or CvScalar.
* If <i>val</i> is CvMat, it must have same type (depth and channel).
* For each element (I)
* dst(I) = src1(I) & src2(I) if mask(I) != 0
*/
VALUE
rb_and(int argc, VALUE *argv, VALUE self)
{
VALUE val, mask, dest;
rb_scan_args(argc, argv, "11", &val, &mask);
dest = copy(self);
if (rb_obj_is_kind_of(val, rb_klass))
cvAnd(CVARR(self), CVARR(val), CVARR(dest), MASK(mask));
else
cvAndS(CVARR(self), VALUE_TO_CVSCALAR(val), CVARR(dest), MASK(mask));
return dest;
}
/*
* call-seq:
* or(<i>val[,mask]</i>) -> cvmat
*
* Return new matrix computed per-element bit-wise disjunction.
* <i>val</i> should be CvMat or CvScalar.
* If <i>val</i> is CvMat, it must have same type (depth and channel).
* For each element (I)
* dst(I) = src1(I) | src2(I) if mask(I) != 0
*/
VALUE
rb_or(int argc, VALUE *argv, VALUE self)
{
VALUE val, mask, dest;
rb_scan_args(argc, argv, "11", &val, &mask);
dest = copy(self);
if (rb_obj_is_kind_of(val, rb_klass))
cvOr(CVARR(self), CVARR(val), CVARR(dest), MASK(mask));
else
cvOrS(CVARR(self), VALUE_TO_CVSCALAR(val), CVARR(dest), MASK(mask));
return dest;
}
/*
* call-seq:
* xor(<i>val[,mask]</i>) -> cvmat
*
* Return new matrix computed per-element bit-wise "exclusive or" operation.
* <i>val</i> should be CvMat or CvScalar.
* If <i>val</i> is CvMat, it must have same type (depth and channel).
* For each element (I)
* dst(I) = src1(I) ^ src2(I) if mask(I) != 0
*/
VALUE
rb_xor(int argc, VALUE *argv, VALUE self)
{
VALUE val, mask, dest;
rb_scan_args(argc, argv, "11", &val, &mask);
dest = copy(self);
if (rb_obj_is_kind_of(val, rb_klass))
cvXor(CVARR(self), CVARR(val), CVARR(dest), MASK(mask));
else
cvXorS(CVARR(self), VALUE_TO_CVSCALAR(val), CVARR(dest), MASK(mask));
return dest;
}
/*
* call-seq:
* not -> cvmat
*
* Return new matrix performed per-element bit-wise inversion.
* dst(I) =~ src(I)
*/
VALUE
rb_not(VALUE self)
{
VALUE dest = copy(self);
cvNot(CVARR(self), CVARR(dest));
return dest;
}
/*
* call-seq:
* not! -> self
*
* Performe per-element bit-wise inversion.
*/
VALUE
rb_not_bang(VALUE self)
{
cvNot(CVARR(self), CVARR(self));
return self;
}
VALUE
rb_cmp_internal(VALUE self, VALUE val, int operand)
{
VALUE dest = new_object(cvGetSize(CVARR(self)), CV_8U);
if (rb_obj_is_kind_of(val, rb_klass))
cvCmp(CVARR(self), CVARR(val), CVARR(dest), operand);
else if (CV_MAT_CN(cvGetElemType(CVARR(self))) == 1 && rb_obj_is_kind_of(val, rb_cNumeric)) {
cvCmpS(CVARR(self), NUM2DBL(val), CVARR(dest), operand);
}else{
VALUE mat = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSet(CVARR(mat), VALUE_TO_CVSCALAR(val));
cvCmp(CVARR(self), CVARR(mat), CVARR(dest), operand);
}
return dest;
}
/*
* call-seq:
* eq(<i>val</i>) -> cvmat
*
* Return new matrix performed per-element comparision "equal".
* dst(I) = (self(I) == val(I) ? 0xFF : 0)
*/
VALUE
rb_eq(VALUE self, VALUE val)
{
return rb_cmp_internal(self, val, CV_CMP_EQ);
}
/*
* call-seq:
* gt(<i>val</i>) -> cvmat
*
* Return new matrix performed per-element comparision "greater than".
* dst(I) = (self(I) > val(I) ? 0xFF : 0)
*/
VALUE
rb_gt(VALUE self, VALUE val)
{
return rb_cmp_internal(self, val, CV_CMP_GT);
}
/*
* call-seq:
* ge(<i>val</i>) -> cvmat
*
* Return new matrix performed per-element comparision "greater or equal".
* dst(I) = (self(I) >= val(I) ? 0xFF : 0)
*/
VALUE
rb_ge(VALUE self, VALUE val)
{
return rb_cmp_internal(self, val, CV_CMP_GE);
}
/*
* call-seq:
* lt(<i>val</i>) -> cvmat
*
* Return new matrix performed per-element comparision "less than".
* dst(I) = (self(I) < val(I) ? 0xFF : 0)
*/
VALUE
rb_lt(VALUE self, VALUE val)
{
return rb_cmp_internal(self, val, CV_CMP_LT);
}
/*
* call-seq:
* le(<i>val</i>) -> cvmat
*
* Return new matrix performed per-element comparision "less or equal".
* dst(I) = (self(I) <= val(I) ? 0xFF : 0)
*/
VALUE
rb_le(VALUE self, VALUE val)
{
return rb_cmp_internal(self, val, CV_CMP_LE);
}
/*
* call-seq:
* ne(<i>val</i>) -> cvmat
*
* Return new matrix performed per-element comparision "not equal".
* dst(I) = (self(I) != val(I) ? 0xFF : 0)
*/
VALUE
rb_ne(VALUE self, VALUE val)
{
return rb_cmp_internal(self, val, CV_CMP_NE);
}
/*
* call-seq:
* in_range(<i>min, max</i>) -> cvmat
*
* Check that element lie between two object.
* <i>min</i> and <i>max</i> should be CvMat that have same size and type, or CvScalar.
* Return new matrix performed per-element,
* dst(I) = within the range ? 0xFF : 0
*/
VALUE
rb_in_range(VALUE self, VALUE min, VALUE max)
{
VALUE dest = dest = new_object(cvGetSize(CVARR(self)), CV_8UC1), tmp;
if (rb_obj_is_kind_of(min, rb_klass) && rb_obj_is_kind_of(max, rb_klass)) {
cvInRange(CVARR(self), CVARR(min), CVARR(max), CVARR(dest));
}else if (rb_obj_is_kind_of(min, rb_klass)) {
tmp = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSet(CVARR(tmp), VALUE_TO_CVSCALAR(max));
cvInRange(CVARR(self), CVARR(min), CVARR(tmp), CVARR(dest));
}else if (rb_obj_is_kind_of(max, rb_klass)) {
tmp = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSet(CVARR(tmp), VALUE_TO_CVSCALAR(min));
cvInRange(CVARR(self), CVARR(tmp), CVARR(max), CVARR(dest));
}else
cvInRangeS(CVARR(self), VALUE_TO_CVSCALAR(min), VALUE_TO_CVSCALAR(max), CVARR(dest));
return dest;
}
/*
* call-seq:
* abs_diff(<i>val</i>) -> cvmat
*
* Calculate absolute difference between two.
* <i>val</i> should be CvMat that have same size and same type, or CvScalar.
* dst(I) = (src(I) - val(I)).abs
*/
VALUE
rb_abs_diff(VALUE self, VALUE val)
{
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
if (rb_obj_is_kind_of(val, rb_klass)) {
cvAbsDiff(CVARR(self), CVARR(val), CVARR(dest));
}else{
cvAbsDiffS(CVARR(self), CVARR(dest), VALUE_TO_CVSCALAR(val));
}
return dest;
}
/*
* call-seq:
* count_non_zero -> int
*
* Returns the number of non-zero elements.
* result = sumI arr(I)!=0
*
* In case of IplImage both ROI and COI are supported.
*/
VALUE
rb_count_non_zero(VALUE self)
{
return INT2FIX(cvCountNonZero(CVARR(self)));
}
/*
* call-seq:
* sum -> scalar
*
* Return summerizes elements as CvScalar. Independently for each channel.
*
* note: If COI is setted in IplImage, the method processes the selected channel only and store the sum to the first component scalar[0].
*/
VALUE
rb_sum(VALUE self)
{
return cCvScalar::new_object(cvSum(CVARR(self)));
}
/*
* call-seq:
* avg(<i>[mask]</i>) -> mean(as scalar)
*
* Return the average(mean) of elements as CvScalar. Independently for each channel.
*/
VALUE
rb_avg(int argc, VALUE *argv, VALUE self)
{
VALUE mask, mean;
rb_scan_args(argc, argv, "01", &mask);
return cCvScalar::new_object(cvAvg(CVARR(self), MASK(mask)));
}
/*
* call-seq:
* avg_sdv(<i>mask</i>) -> [mean(as scalar), std_dev(as scalar)]
*
* Calculates the average value and standard deviation of array elements, independently for each channel.
*
* note: same as [CvMat#avg, CvMat#sdv]
*/
VALUE
rb_avg_sdv(int argc, VALUE *argv, VALUE self)
{
VALUE mask, mean, std_dev;
rb_scan_args(argc, argv, "01", &mask);
mean = cCvScalar::new_object();
std_dev = cCvScalar::new_object();
cvAvgSdv(CVARR(self), CVSCALAR(mean), CVSCALAR(std_dev), MASK(mask));
return rb_ary_new3(2, mean, std_dev);
}
/*
* call-seq:
* sdv(<i>[mask]</i>) -> std_dev(as scalar)
*
* Return the standard deviation of elements as CvScalar. Independently for each channel.
*/
VALUE
rb_sdv(int argc, VALUE *argv, VALUE self)
{
VALUE mask, std_dev;
rb_scan_args(argc, argv, "01", &mask);
std_dev = cCvScalar::new_object();
cvAvgSdv(CVARR(self), NULL, CVSCALAR(std_dev), MASK(mask));
return std_dev;
}
/*
* call-seq:
* min_max_loc(<i>[mask]</i>) -> [min_val, max_val, min_loc(as point), max_loc(as point)]
*
* Finds minimum and maximum element values and their positions.
* The extremums are searched over the whole array, selected ROI(in case of IplImage) or, if mask is not NULL, in the specified array region.
* If the array has more than one channel, it must be IplImage with COI set.
* In case if multi-dimensional arrays min_loc.x and max_loc.x will contain raw (linear) positions of the extremums.
*/
VALUE
rb_min_max_loc(int argc, VALUE *argv, VALUE self)
{
VALUE mask, min_loc, max_loc;
double min_val = 0.0, max_val = 0.0;
rb_scan_args(argc, argv, "01", &mask);
min_loc = cCvPoint::new_object();
max_loc = cCvPoint::new_object();
cvMinMaxLoc(CVARR(self), &min_val, &max_val, CVPOINT(min_loc), CVPOINT(max_loc), MASK(mask));
return rb_ary_new3(4,
rb_float_new(min_val),
rb_float_new(max_val),
min_loc,
max_loc);
}
/*
* call-seq:
* dot_product(<i>mat</i>) -> float
*
* Calculates dot product of two arrays in Euclidian metrics.
* <i>mat</i> should be CvMat have same size and same type.
*
* src1.src2 = sum(src1(I) * src2(I))
*/
VALUE
rb_dot_product(VALUE self, VALUE mat)
{
if (!rb_obj_is_kind_of(mat, rb_klass))
rb_raise(rb_eTypeError, "argument should be CvMat.");
return rb_float_new(cvDotProduct(CVARR(self), CVARR(mat)));
}
/*
* call-seq:
* cross_product(<i>mat</i>) -> cvmat
*
* Calculate cross product of two 3D vectors.
* <i>mat</i> should be CvMat have same size and same type.
*/
VALUE
rb_cross_product(VALUE self, VALUE mat)
{
if (!rb_obj_is_kind_of(mat, rb_klass))
rb_raise(rb_eTypeError, "argument should be CvMat.");
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvCrossProduct(CVARR(self), CVARR(mat), CVARR(dest));
return dest;
}
/*
* call-seq:
* transform(<i>transmat[,shiftvec]</i>) -> cvmat
*
* performs matrix transform of every element.
* dst(I) = transmat * src(I) + shiftvec
*/
VALUE
rb_transform(int argc, VALUE *argv, VALUE self)
{
VALUE transmat, shiftvec;
rb_scan_args(argc, argv, "11", &transmat, &shiftvec);
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
if (NIL_P(shiftvec))
cvTransform(CVARR(self), CVARR(dest), CVMAT(transmat), NULL);
else
cvTransform(CVARR(self), CVARR(dest), CVMAT(transmat), CVMAT(shiftvec));
return dest;
}
/*
* call-seq:
* perspective_transform(<i>mat</i>) -> cvmat
*
* Return performed perspective matrix transform of vector array.
* <i>mat</i> should be 3x3 or 4x4 transform matrix (CvMat).
* Every element (by treating it as 2D or 3D vector) in the following way:
* (x, y, z) -> (x'/w, y'/w, z'/w) or
* (x, y) -> (x'/w, y'/w)
* where
* (x', y', z', w') = mat4x4*(x, y, z, 1) or
* (x', y', w') = mat3x3*(x, y, 1)
* and
* w = w' if w'!=0, inf otherwise.
*/
VALUE
rb_perspective_transform(VALUE self, VALUE mat)
{
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvPerspectiveTransform(CVARR(self), CVARR(dest), CVMAT(mat));
return dest;
}
/*
* call-seq:
* mul_transposed(<i>:order => :default or :inverse, :delta => nil or cvmat</i>)
*
* Calculates the product of self and its transposition.
*
* options
* * :order -> should be :default or :inverse (default is :default)
* see below.
* * :delta -> should be nil or CvMat (default is nil)
* An optional array, subtracted from source before multiplication.
*
* mul_transposed evaluates:
* :order => :default
* dst = (self - delta) * (self - delta)T
* :order => :inverse
* dst = (self - delta)T * (self - delta)
*
*/
VALUE
rb_mul_transposed(VALUE self, VALUE args)
{
//VALUE options = extract_options_from_args_bang(args);
//assert_valid_keys(options, 2, "order", "delta");
//VALUE order;
//OPTIONS(order, options, "order", ID2SYM(rb_intern("default")));
//ID2SYM(rb_intern("order")), rb_intern("")
return Qnil;
}
/*
* call-seq:
* trace -> scalar
*
* Returns trace of matrix. "trace" is sum of diagonal elements of the matrix.
*/
VALUE
rb_trace(VALUE self)
{
return cCvScalar::new_object(cvTrace(CVARR(self)));
}
/*
* call-seq:
* transpose -> cvmat
*
* Return transposed matrix.
*/
VALUE
rb_transpose(VALUE self)
{
CvSize size = cvGetSize(CVARR(self));
VALUE dest = new_object(size.width, size.height, cvGetElemType(CVARR(self)));
cvTranspose(CVARR(self), CVARR(dest));
return dest;
}
/*
* call-seq:
* transpose! -> self
*
* Transposed matrix.
*
* <b>rectangular matrix only (CvMat#square? = true).</b>
*/
VALUE
rb_transpose_bang(VALUE self)
{
cvTranspose(CVARR(self), CVARR(self));
return self;
}
/*
* call-seq:
* det -> float
*
* Return determinant of matrix.
* <i>self</i> should be single-channel and floating-point depth.
*/
VALUE
rb_det(VALUE self)
{
return rb_float_new(cvDet(CVARR(self)));
}
/*
* call-seq:
* invert(<i>inversion_method=:lu[,delta]</i>) -> float
*
* Finds inverse or pseudo-inverse of matrix.
* <i>inversion_method</i> should be following symbol.
* * :lu
* Gaussian elimincation with optimal pivot element chose.
* Return self determinant (self must be square).
* * :svd
* Singular value decomposition(SVD) method.
* Return the inversed condition number of self(ratio of the smallest singular value to the largest singular value)
* and 0 if self is all zeros. The SVD method calculate a pseudo-inverse matrix if self is singular.
* * :svd_sym or :svd_symmetric
* SVD method for a symmetric positively-defined matrix.
*
* <i>self</i> type should be single-channel and floating-point matrix.
*/
VALUE
rb_invert(int argc, VALUE *argv, VALUE self)
{
VALUE symbol;
rb_scan_args(argc, argv, "01", &symbol);
int method = CVMETHOD("INVERSION_METHOD", symbol, CV_LU);
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvInvert(CVARR(self), CVARR(dest), method);
return dest;
}
/*
* call-seq:
* solve(<i>mat, inversion_method=:lu</i>)
*
* Solves linear system or least-squares problem (the latter is possible with SVD method).
*
* <i>inversion_method</i> should be following symbol.
* * :lu
* Gaussian elimincation with optimal pivot element chose.
* Return self determinant (self must be square).
* * :svd
* Singular value decomposition(SVD) method.
* Return the inversed condition number of self(ratio of the smallest singular value to the largest singular value)
* and 0 if self is all zeros. The SVD method calculate a pseudo-inverse matrix if self is singular.
* * :svd_sym or :svd_symmetric
* SVD method for a symmetric positively-defined matrix.
*/
VALUE
rb_solve(int argc, VALUE *argv, VALUE self)
{
VALUE mat, symbol;
rb_scan_args(argc, argv, "11", &mat, &symbol);
if (!rb_obj_is_kind_of(mat, rb_klass))
rb_raise(rb_eTypeError, "argument 1 (right-hand part of the linear system) should be %s.)", rb_class2name(rb_klass));
VALUE dest = new_object(CVMAT(self)->rows, CVMAT(mat)->cols, cvGetElemType(CVARR(self)));
cvSolve(CVARR(self), CVARR(mat), CVARR(dest), CVMETHOD("INVERSION_METHOD", symbol, CV_LU));
return dest;
}
/*
* call-seq:
* svd(u = nil, v = nil</i>)
*
* not implementated.
* Performs singular value decomposition of real floating-point matrix.
*/
VALUE
rb_svd(int argc, VALUE *argv, VALUE self)
{
rb_raise(rb_eNotImpError, "");
/*
VALUE u = Qnil, v = Qnil;
rb_scan_args(argc, argv, "02", &u, &v);
CvMat
*matU = NIL_P(u) ? NULL : CVARR(u),
*matV = NIL_P(v) ? NULL : CVARR(v);
cvSVD(CVARR(self), matU, matV);
return dest;
*/
}
/*
* call-seq:
* svbksb
*
* not yet.
*/
VALUE
rb_svbksb(int argc, VALUE *argv, VALUE self)
{
rb_raise(rb_eNotImpError, "");
}
/*
* call-seq:
* eigenvv!(<i>[eps = 0.0]</i>) -> [eigen_vectors(cvmat), eigen_values(cvmat)]
*
* Computes eigenvalues and eigenvectors of symmetric matrix.
* <i>self</i> should be symmetric square matrix. <i>self</i> is modified during the processing.
*
* self * eigen_vectors(i,:)' = eigen_values(i) * eigen_vectors(i,:)'
*
* Currently the function is slower than #svd yet less accurate, so if <i>self</i> is known to be positively-defined
* (e.g., it is a convariation matrix), it is recommanded to use #svd to find eigenvalues and eigenvectors of <i>self</i>,
* especially if eigenvectors are not required.
*/
VALUE
rb_eigenvv(int argc, VALUE *argv, VALUE self)
{
VALUE epsilon, lowindex, highindex;
rb_scan_args(argc, argv, "03", &epsilon, &lowindex, &highindex);
double eps = (NIL_P(epsilon)) ? 0.0 : NUM2DBL(epsilon);
int lowidx = (NIL_P(lowindex)) ? -1 : NUM2INT(lowindex);
int highidx = (NIL_P(highindex)) ? -1 : NUM2INT(highindex);
CvSize size = cvGetSize(CVARR(self));
int type = cvGetElemType(CVARR(self));
VALUE eigen_vectors = new_object(size, type), eigen_values = new_object(size.height, 1, type);
cvEigenVV(CVARR(self), CVARR(eigen_vectors), CVARR(eigen_values), eps, lowidx, highidx);
return rb_ary_new3(2, eigen_vectors, eigen_values);
}
/*
* call-seq:
* calc_covar_matrix()
*
* not yet.
*
*/
VALUE
rb_calc_covar_matrix(int argc, VALUE *argv, VALUE self)
{
rb_raise(rb_eNotImpError, "");
}
/*
* call-seq:
* mahalonobis(vec, mat) -> float
*
* not yet.
*/
VALUE
rb_mahalonobis(int argc, VALUE *argv, VALUE self)
{
rb_raise(rb_eNotImpError, "");
}
/*
* call-seq:
* dft(<i>anyflags...</i>) -> cvmat
*
* Performs forward or inverse Discrete Fourier Transform(DFT) of 1D or 2D floating-point array.
* Argument should be following symbol or combination of these.
*
* * :forward or :inverse
* Do forward or inverse transform. The result is not scaled.
* * :scale
* Scale the result: divide it by the number of array elements.
* * :rows
* Do forward or inverse transform of every individual row of the self.
* This flag allow user to transofrm multiple vectors simulaneously and can be used to decrease the overhand
* (which sometimes several times larger then the processing itself), to do 3D and higher-dimensional transforms etc.
*
* e.g.
* mat.dft(:inverse)
* mat.dft(:forward, :scale) etc...
*/
VALUE
rb_dft(int argc, VALUE *argv, VALUE self)
{
int type = CV_DXT_FORWARD;
if (argc > 0) {
for (int i = 0; i < argc; i++) {
type |= CVMETHOD("DXT_FLAG", argv[i]);
}
}
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvDFT(CVARR(self), CVARR(dest), type);
return dest;
}
/*
* call-seq:
* dct(<i>anyflags...</i>) -> cvmat
*
* Performs forward or inverse Discrete Cosine Transform(DCT) of 1D or 2D floating-point array.
* Argument should be following symbol or combination of these.
*
* * :forward or :inverse
* Do forward or inverse transform.
* * :rows
* Do forward or inverse transform of every individual row of the self.
* This flag allow user to transofrm multiple vectors simulaneously and can be used to decrease the overhand
* (which sometimes several times larger then the processing itself), to do 3D and higher-dimensional transforms etc.
*/
VALUE
rb_dct(int argc, VALUE *argv, VALUE self)
{
int type = CV_DXT_FORWARD;
if (argc > 0) {
for (int i = 0; i < argc; i++) {
type |= CVMETHOD("DXT_FLAG", argv[i]);
}
}
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvDCT(CVARR(self), CVARR(dest), type);
return dest;
}
/*
* call-seq:
* line(<i>p1, p2[, drawing_option]</i>) -> mat
*
* Return image is drawn a line segment connecting two points.
*
* <i>drawing_option</i> should be Hash include these keys.
* :color
* Line color.
* :thickness
* Line Thickness.
* :line_type
* Type of the line:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the point coordinates.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*
* for example
* mat = CvMat.new(100, 100)
* mat.line(CvPoint.new(10, 10), CvPoint.new(90, 90), :thickness => 3, :line_type => :aa)
*/
VALUE
rb_line(int argc, VALUE *argv, VALUE self)
{
return rb_line_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* line!(<i>p1, p2[, drawing_option]</i>) -> self
*
* Draws a line segment connecting two points.
* Same as CvMat#line, but modifies the receiver in place.
* see CvMat#line
*/
VALUE
rb_line_bang(int argc, VALUE *argv, VALUE self)
{
VALUE p1, p2, drawing_option;
rb_scan_args(argc, argv, "21", &p1, &p2, &drawing_option);
drawing_option = DRAWING_OPTION(drawing_option);
cvLine(CVARR(self), VALUE_TO_CVPOINT(p1), VALUE_TO_CVPOINT(p2),
DO_COLOR(drawing_option),
DO_THICKNESS(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* rectangle(<i>p1, p2[, drawing_option]</i>) -> mat
*
* Return image is drawn a rectangle with two opposite corners <i>p1</i> and <i>p2</i>.
*
* <i>drawing_options</i> should be Hash include these keys.
* :color
* Line color.
* :thickness
* Thickness of lines that make up the rectangle.
* Negative values make the function to draw a filled rectangle.
* :line_type
* Type of the line:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the point coordinates.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*/
VALUE
rb_rectangle(int argc, VALUE *argv, VALUE self)
{
return rb_rectangle_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* rectangle!(<i>p1, p2[, drawing_option]</i>) -> self
*
* Draws simple, thick or filled rectangle.
* Same as CvMat#rectangle, but modifies the receiver in place.
* see CvMat#rectangle
*/
VALUE
rb_rectangle_bang(int argc, VALUE *argv, VALUE self)
{
VALUE p1, p2, drawing_option;
rb_scan_args(argc, argv, "21", &p1, &p2, &drawing_option);
drawing_option = DRAWING_OPTION(drawing_option);
cvRectangle(CVARR(self), VALUE_TO_CVPOINT(p1), VALUE_TO_CVPOINT(p2),
DO_COLOR(drawing_option),
DO_THICKNESS(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* circle(<i>center, radius[,drawing_option]</i>) -> cvmat
*
* Return image is drawn a simple or filled circle with given center and radius.
*
* <i>drawing_options</i> should be Hash include these keys.
* :color
* Circle color.
* :thickness
* Thickness of the circle outline if positive, otherwise that a filled circle has to be drawn.
* :line_type
* Type of the circle boundary:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the center coordinates and radius value.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*/
VALUE
rb_circle(int argc, VALUE *argv, VALUE self)
{
return rb_circle_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* circle!(<i>center, radius[,drawing_option]</i>) -> cvmat
*
* Draw a circle.
* Same as CvMat#circle, but modifies the receiver in place.
*
* see CvMat#circle
*/
VALUE
rb_circle_bang(int argc, VALUE *argv, VALUE self)
{
VALUE center, radius, drawing_option;
rb_scan_args(argc, argv, "21", &center, &radius, &drawing_option);
drawing_option = DRAWING_OPTION(drawing_option);
cvCircle(CVARR(self), VALUE_TO_CVPOINT(center), NUM2INT(radius),
DO_COLOR(drawing_option),
DO_THICKNESS(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* ellipse(<i>center, axis, angle, start_angle, end_angle[,drawing_option]</i>) -> mat
*
* Return image is drawn a simple or thick elliptic arc or fills an ellipse sector.
*
* <i>drawing_options</i> should be Hash include these keys.
* :color
* Ellipse color.
* :thickness
* Thickness of the ellipse arc.
* :line_type
* Type of the ellipse boundary:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the center coordinates and axes' value.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*/
VALUE
rb_ellipse(int argc, VALUE *argv, VALUE self)
{
return rb_ellipse_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* ellipse!(<i>center, axis, angle, start_angle, end_angle[,drawing_option]</i>) -> self
*
* Draws simple or thick elliptic arc or fills ellipse sector.
* Same as CvMat#ellipse, but modifies the receiver in place.
*
* see CvMat#ellipse
*/
VALUE
rb_ellipse_bang(int argc, VALUE *argv, VALUE self)
{
VALUE center, axis, angle, start_angle, end_angle, drawing_option;
rb_scan_args(argc, argv, "51", &center, &axis, &angle, &start_angle, &end_angle, &drawing_option);
drawing_option = DRAWING_OPTION(drawing_option);
cvEllipse(CVARR(self), VALUE_TO_CVPOINT(center),
VALUE_TO_CVSIZE(axis),
NUM2DBL(angle), NUM2DBL(start_angle), NUM2DBL(end_angle),
DO_COLOR(drawing_option),
DO_THICKNESS(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* ellipse_box(<i>box[, drawing_option]</i>) -> mat
*
* Return image is drawn a simple or thick ellipse outline, or fills an ellipse.
* The method provides a convenient way to draw an ellipse approximating some shape.
*
* <i>drawing_options</i> should be Hash include these keys.
* :color
* Ellipse color.
* :thickness
* Thickness of the ellipse drawn.
* :line_type
* Type of the ellipse boundary:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the box vertex coordinates.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*/
VALUE
rb_ellipse_box(int argc, VALUE *argv, VALUE self)
{
return rb_ellipse_box_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* ellipse_box!(<i>box[, drawing_option]</i>) -> self
*
* Draws simple or thick elliptic arc or fills ellipse sector.
* Same as CvMat#ellipse_box, but modifies the receiver in place.
*
* see CvMat#ellipse_box
*/
VALUE
rb_ellipse_box_bang(int argc, VALUE *argv, VALUE self)
{
VALUE box, drawing_option;
rb_scan_args(argc, argv, "11", &box, &drawing_option);
drawing_option = DRAWING_OPTION(drawing_option);
cvEllipseBox(CVARR(self), VALUE_TO_CVBOX2D(box),
DO_COLOR(drawing_option),
DO_THICKNESS(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* fill_poly(<i>points[,drawing_option]</i>) -> mat
*
* Return image is filled an area bounded by several polygonal contours.
* The method fills complex areas, for example, areas with holes, contour self-intersection, etc.
*/
VALUE
rb_fill_poly(int argc, VALUE *argv, VALUE self)
{
return rb_fill_poly_bang(argc, argv, self);
}
/*
* call-seq:
* fill_poly!(<i>points[,drawing_option]</i>) -> self
*
* Fills polygons interior.
* Same as CvMat#fill_poly, but modifies the receiver in place.
*
* drawing_options should be Hash include these keys.
* :color
* Polygon color.
* :line_type
* Type of the polygon boundaries:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the vertex coordinates.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*/
VALUE
rb_fill_poly_bang(int argc, VALUE *argv, VALUE self)
{
VALUE polygons, drawing_option;
VALUE points;
int i, j;
int num_polygons;
int *num_points;
CvPoint **p;
rb_scan_args(argc, argv, "11", &polygons, &drawing_option);
// TODO: Check type of argument
drawing_option = DRAWING_OPTION(drawing_option);
num_polygons = RARRAY_LEN(polygons);
num_points = ALLOCA_N(int, num_polygons);
p = ALLOCA_N(CvPoint*, num_polygons);
for (j = 0; j < num_polygons; j++) {
points = rb_ary_entry(polygons, j);
num_points[j] = RARRAY_LEN(points);
p[j] = ALLOCA_N(CvPoint, num_points[j]);
for (i = 0; i < num_points[j]; i++) {
p[j][i] = VALUE_TO_CVPOINT(rb_ary_entry(points, i));
}
}
cvFillPoly(CVARR(self),
p,
num_points,
num_polygons,
DO_COLOR(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* fill_convex_poly(<i>points[,drawing_option]</i>) -> mat
*
* Return image is filled convex polygon interior.
* This method is much faster than The function CvMat#fill_poly
* and can fill not only the convex polygons but any monotonic polygon,
* i.e. a polygon whose contour intersects every horizontal line (scan line)
* twice at the most.
*
* <i>drawing_options</i> should be Hash include these keys.
* :color
* Polygon color.
* :line_type
* Type of the polygon boundaries:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the vertex coordinates.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*/
VALUE
rb_fill_convex_poly(int argc, VALUE *argv, VALUE self)
{
return rb_fill_convex_poly_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* fill_convex_poly!(<i>points[,drawing_option]</i>) -> self
*
* Fills convex polygon.
* Same as CvMat#fill_convex_poly, but modifies the receiver in place.
*
* see CvMat#fill_convex_poly
*/
VALUE
rb_fill_convex_poly_bang(int argc, VALUE *argv, VALUE self)
{
VALUE points, drawing_option;
int i, num_points;
CvPoint *p;
rb_scan_args(argc, argv, "11", &points, &drawing_option);
// TODO: Check type of argument
drawing_option = DRAWING_OPTION(drawing_option);
num_points = RARRAY_LEN(points);
p = ALLOCA_N(CvPoint, num_points);
for (i = 0; i < num_points; i++)
p[i] = VALUE_TO_CVPOINT(rb_ary_entry(points, i));
cvFillConvexPoly(CVARR(self),
p,
num_points,
DO_COLOR(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* poly_line(<i>points[,drawing_option]</i>) -> mat
*
* Return image drawed a single or multiple polygonal curves.
*
* <i>drawing_option</i> should be Hash include these keys.
* :is_closed
* Indicates whether the polylines must be drawn closed.
* If closed, the method draws the line from the last vertex
* of every contour to the first vertex.
* :color
* Polyline color.
* :thickness
* Thickness of the polyline edges
* :line_type
* Type of line segments:
* * 0 or 8 - 8-connected line(default).
* * 4 - 4-connected line.
* * negative-value - antialiased line.
* :shift
* Number of fractional bits in the vertex coordinates.
*
* note: <i>drawing_option</i>'s default value is CvMat::DRAWING_OPTION.
*/
VALUE
rb_poly_line(int argc, VALUE *argv, VALUE self)
{
return rb_poly_line_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* poly_line!(<i>points[,drawing_option]</i>) -> self
*
* Draws simple or thick polygons.
*
* Same as CvMat#poly_line, but modifies the receiver in place.
*
* see CvMat#poly_line
*/
VALUE
rb_poly_line_bang(int argc, VALUE *argv, VALUE self)
{
VALUE polygons, drawing_option;
VALUE points;
int i, j;
int num_polygons;
int *num_points;
CvPoint **p;
rb_scan_args(argc, argv, "11", &polygons, &drawing_option);
// TODO: Check type of argument
drawing_option = DRAWING_OPTION(drawing_option);
num_polygons = RARRAY_LEN(polygons);
num_points = ALLOCA_N(int, num_polygons);
p = ALLOCA_N(CvPoint*, num_polygons);
for (j = 0; j < num_polygons; j++) {
points = rb_ary_entry(polygons, j);
num_points[j] = RARRAY_LEN(points);
p[j] = ALLOCA_N(CvPoint, num_points[j]);
for (i = 0; i < num_points[j]; i++) {
p[j][i] = VALUE_TO_CVPOINT(rb_ary_entry(points, i));
}
}
cvPolyLine(CVARR(self),
p,
num_points,
num_polygons,
DO_IS_CLOSED(drawing_option),
DO_COLOR(drawing_option),
DO_THICKNESS(drawing_option),
DO_LINE_TYPE(drawing_option),
DO_SHIFT(drawing_option));
return self;
}
/*
* call-seq:
* put_text(<i>str, point, font[,color]</i>) -> cvmat
*
* Return image is drawn text string.
* <i>font</i> should be CvFont object.
*/
VALUE
rb_put_text(int argc, VALUE *argv, VALUE self)
{
return rb_put_text_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* put_text!(<i>str, point ,font[,color]</i>) -> self
*
* Draws text string. Return self.
*/
VALUE
rb_put_text_bang(int argc, VALUE *argv, VALUE self)
{
VALUE text, point, font, color;
rb_scan_args(argc, argv, "22", &text, &point, &font, &color);
cvPutText(CVARR(self), StringValueCStr(text), VALUE_TO_CVPOINT(point), CVFONT(font), *CVSCALAR(color));
return self;
}
/*
* call-seq:
* sobel(<i>xorder,yorder[,aperture_size=3]</i>) -> cvmat
*
* Calculates first, second, third or mixed image derivatives using extended Sobel operator.
* <i>self</i> should be single-channel 8bit unsigned or 32bit floating-point.
*
* link:../images/CvMat_sobel.png
*/
VALUE
rb_sobel(int argc, VALUE *argv, VALUE self)
{
VALUE xorder, yorder, aperture_size, dest;
if (rb_scan_args(argc, argv, "21", &xorder, &yorder, &aperture_size) < 3)
aperture_size = INT2FIX(3);
switch(CV_MAT_DEPTH(CVMAT(self)->type)) {
case CV_8U:
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_16S, 1));
break;
case CV_32F:
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
break;
default:
rb_raise(rb_eRuntimeError, "source depth should be CV_8U or CV_32F.");
}
cvSobel(CVARR(self), CVARR(dest), NUM2INT(xorder), NUM2INT(yorder), NUM2INT(aperture_size));
return dest;
}
/*
* call-seq:
* laplace(<i>[aperture_size = 3]</i>) -> cvmat
*
* Calculates Laplacian of the image.
* <i>self</i> should be single-channel 8bit unsigned or 32bit floating-point.
*/
VALUE
rb_laplace(int argc, VALUE *argv, VALUE self)
{
VALUE aperture_size, dest;
if (rb_scan_args(argc, argv, "01", &aperture_size) < 1)
aperture_size = INT2FIX(3);
switch(CV_MAT_DEPTH(CVMAT(self)->type)) {
case CV_8U:
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_16S, 1));
break;
case CV_32F:
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
break;
default:
rb_raise(rb_eRuntimeError, "source depth should be CV_8U or CV_32F.");
}
cvLaplace(CVARR(self), CVARR(dest), NUM2INT(aperture_size));
return dest;
}
/*
* call-seq:
* canny(<i>thresh1,thresh2[,aperture_size = 3]</i>) -> cvmat
*
* Canny algorithm for edge detection.
*/
VALUE
rb_canny(int argc, VALUE *argv, VALUE self)
{
VALUE thresh1, thresh2, aperture_size;
if (rb_scan_args(argc, argv, "21", &thresh1, &thresh2, &aperture_size) < 3)
aperture_size = INT2FIX(3);
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvCanny(CVARR(self), CVARR(dest), NUM2INT(thresh1), NUM2INT(thresh2), NUM2INT(aperture_size));
return dest;
}
/*
* call-seq:
* pre_corner_detect(<i>[aperture_size = 3]</i>) -> cvmat
*
* Calculates feature map for corner detection.
* <i>aperture_size</i> is parameter for sobel operator(see #sobel).
*
* The corners can be found as local maximums of the function.
*/
VALUE
rb_pre_corner_detect(int argc, VALUE *argv, VALUE self)
{
VALUE aperture_size, dest;
if (rb_scan_args(argc, argv, "01", &aperture_size) < 1)
aperture_size = INT2FIX(3);
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
cvPreCornerDetect(CVARR(self), CVARR(dest), NUM2INT(aperture_size));
return dest;
}
/*
* call-seq:
* corner_eigenvv(<i>block_size[,aperture_size]</i>) -> cvmat
*
* For every pixel considers <i>block_size x block_size</i> neighborhood S(p).
* It calculates convariation matrix of derivatives over the neighborhood.
*/
VALUE
rb_corner_eigenvv(int argc, VALUE *argv, VALUE self)
{
VALUE block_size, aperture_size, dest;
if (rb_scan_args(argc, argv, "11", &block_size, &aperture_size) < 2)
aperture_size = INT2FIX(3);
Check_Type(block_size, T_FIXNUM);
CvSize size = cvGetSize(CVARR(self));
dest = new_object(cvSize(size.width * 6, size.height), CV_MAKETYPE(CV_32F, 1));
cvCornerEigenValsAndVecs(CVARR(self), CVARR(dest), NUM2INT(block_size), NUM2INT(aperture_size));
return dest;
}
/*
* call-seq:
* corner_min_eigen_val(<i>block_size[,aperture_size = 3]</i>) -> cvmat
*
* Calculates minimal eigenvalue of gradient matrices for corner detection.
*/
VALUE
rb_corner_min_eigen_val(int argc, VALUE *argv, VALUE self)
{
VALUE block_size, aperture_size, dest;
if (rb_scan_args(argc, argv, "11", &block_size, &aperture_size) < 2)
aperture_size = INT2FIX(3);
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
cvCornerMinEigenVal(CVARR(self), CVARR(dest), FIX2INT(block_size), FIX2INT(aperture_size));
return dest;
}
/*
* call-seq:
* corner_harris(<i>block_size[,aperture_size = 3][,k = 0.04]</i>) -> cvmat
*
* Return image Applied Harris edge detector.
*/
VALUE
rb_corner_harris(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE block_size, aperture_size, k, dest;
rb_scan_args(argc, argv, "12", &block_size, &aperture_size, &k);
dest = new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
cvCornerHarris(CVARR(self), CVARR(dest), FIX2INT(block_size), IF_INT(aperture_size, 3), IF_DBL(k, 0.04));
return dest;
}
/*
* call-seq:
* find_corner_sub_pix(<i></i>)
*
* Refines corner locations.
* This method iterates to find the sub-pixel accurate location of corners,
* or radial saddle points, as shown in on the picture below.
*/
VALUE
rbi_find_corner_sub_pix(int argc, VALUE *argv, VALUE self)
{
/*
VALUE corners, win, zero_zone, criteria;
rb_scan_args(argc, argv, "13", &corners, &win, &zero_zone, &criteria);
if (!rb_obj_is_kind_of(corners, mPointSet::rb_module()))
rb_raise(rb_eTypeError, "argument 1 (corners) should be %s.", rb_class2name(mPointSet::rb_module()));
int count = CVSEQ(corners)->total;
VALUE storage = cCvMemStorage::new_object();
CvPoint2D32f *pointset = POINTSET2D32f(corners);
//cvFindCornerSubPix(CVARR(self), pointset, count, VALUE_TO_CVSIZE(win), VALUE_TO_CVSIZE(zero_zone), VALUE_TO_CVTERMCRITERIA(criteria));
//return cCvSeq::new_sequence();
*/
return Qnil;
}
/*
* call-seq:
* good_features_to_track(<i>quality_level, min_distance[, good_features_to_track_option]</i>)
* -> array (include CvPoint2D32f)
* Determines strong corners on an image.
*
* quality_level Multiplier for the max/min eigenvalue; specifies the minimal accepted quality of image corners
* min_distance Limit, specifying the minimum possible distance between the returned corners; Euclidian distance is used
* <i>good_features_to_track_option</i> should be Hash include these keys.
* :mask
* Region of interest. The function selects points either in the specified region or in the whole image
* if the mask is nil.
* :block_size
* Size of the averaging block, passed to the underlying CornerMinEigenVal or CornerHarris used by the function.
* :use_harris
* If true, Harris operator ( CornerHarris ) is used instead of default CornerMinEigenVal
* :k
* Free parameter of Harris detector; used only if ( :use_harris => true )
* note: <i>good_features_to_track_option</i>'s default value is CvMat::GOOD_FEATURES_TO_TRACK_OPTION
*/
VALUE
rb_good_features_to_track(int argc, VALUE *argv, VALUE self)
{
VALUE quality_level, min_distance, good_features_to_track_option;
CvMat *eigen, *tmp;
int i;
rb_scan_args(argc, argv, "21", &quality_level, &min_distance, &good_features_to_track_option);
good_features_to_track_option = GOOD_FEATURES_TO_TRACK_OPTION(good_features_to_track_option);
CvMat *src = CVMAT(self);
CvSize size = cvGetSize(src);
eigen = cvCreateMat(size.height, size.width, CV_MAKETYPE(CV_32F, 1));
tmp = cvCreateMat(size.height, size.width, CV_MAKETYPE(CV_32F, 1));
int np = GF_MAX(good_features_to_track_option);
if(!(np > 0))
rb_raise(rb_eArgError, "option :max should be positive value.");
CvPoint2D32f *p32 = (CvPoint2D32f*)cvAlloc(sizeof(CvPoint2D32f) * np);
if(!p32)
rb_raise(rb_eNoMemError, "failed to allocate memory.");
cvGoodFeaturesToTrack(src, &eigen, &tmp, p32, &np, NUM2DBL(quality_level), NUM2DBL(min_distance),
GF_MASK(good_features_to_track_option),
GF_BLOCK_SIZE(good_features_to_track_option),
GF_USE_HARRIS(good_features_to_track_option),
GF_K(good_features_to_track_option));
VALUE corners = rb_ary_new2(np);
for (i = 0; i < np; i++)
rb_ary_store(corners, i, cCvPoint2D32f::new_object(p32[i]));
cvFree(&p32);
cvReleaseMat(&eigen);
cvReleaseMat(&tmp);
return corners;
}
/*
* call-seq:
* sample_line(p1, p2[,connectivity = 8]) {|pixel| }
*
* not yet.
*/
VALUE
rb_sample_line(int argc, VALUE *argv, VALUE self)
{
/*
VALUE p1, p2, connectivity;
if (rb_scan_args(argc, argv, "21", &p1, &p2, &connectivity) < 3)
connectivity = INT2FIX(8);
CvPoint point1 = VALUE_TO_CVPOINT(p1), point2 = VALUE_TO_CVPOINT(p2);
int size;
switch(FIX2INT(connectivity)) {
case 4:
size = abs(point2.x - point1.x) + abs(point2.y - point1.y) + 1;
break;
case 8:
size = maxint(abs(point2.x - point1.x) + 1, abs(point2.y - point1.y) + 1);
break;
default:
rb_raise(rb_eArgError, "argument 3(connectivity) should be 4 or 8. 8 is default.");
}
VALUE buf = cCvMat::new_object(1, size, cvGetElemType(CVARR(self)));
cvSampleLine(CVARR(self), point1, point2, CVMAT(buf)->data.ptr, FIX2INT(connectivity));
if (rb_block_given_p()) {
for(int i = 0; i < size; i++) {
//Data_Wrap_Struct(cCvScalar::rb_class(), 0, 0, CVMAT(buf)->data.ptr[]);
//rb_yield(cCvScalar::new_object);
}
}
return buf;
*/
return Qnil;
}
/*
* call-seq:
* rect_sub_pix(<i>center[, size = self.size]</i>) -> cvmat
*
* Retrieves pixel rectangle from image with sub-pixel accuracy.
* Extracts pixels from <i>self</i>.
* dst(x,y) = self(x + center.x - (size.width - 1) * 0.5, y + center.y - (size.height - 1) * 0.5)
* where the values of pixels at non-integer coordinates are retrived using bilinear iterpolation.
* Every channel of multiple-channel images is processed independently.
* Whereas the rectangle center must be inside the image, the whole rectangle may be partially occludedl.
* In this case, the replication border mode is used to get pixel values beyond the image boundaries.
*/
VALUE
rb_rect_sub_pix(int argc, VALUE *argv, VALUE self)
{
VALUE center, size;
CvSize _size;
if (rb_scan_args(argc, argv, "11", &center, &size) < 2)
_size = cvGetSize(CVARR(self));
else
_size = VALUE_TO_CVSIZE(size);
VALUE dest = new_object(_size, cvGetElemType(CVARR(self)));
cvGetRectSubPix(CVARR(self), CVARR(dest), VALUE_TO_CVPOINT2D32F(center));
return dest;
}
/*
* call-seq:
* quandrangle_sub_pix(<i>map_matrix[, size = self.size]</i>) -> cvmat
*
* Retrives pixel quadrangle from image with sub-pixel accuracy.
* Extracts pixel from <i>self</i> at sub-pixel accuracy and store them:
*/
VALUE
rb_quadrangle_sub_pix(int argc, VALUE *argv, VALUE self)
{
VALUE map_matrix, size;
CvSize _size;
if (rb_scan_args(argc, argv, "11", &map_matrix, &size) < 2)
_size = cvGetSize(CVARR(self));
else
_size = VALUE_TO_CVSIZE(size);
if (!rb_obj_is_kind_of(map_matrix, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 1 (map matrix) should be %s (2x3).", rb_class2name(cCvMat::rb_class()));
VALUE dest = new_object(_size, cvGetElemType(CVARR(self)));
cvGetQuadrangleSubPix(CVARR(self), CVARR(dest), CVMAT(map_matrix));
return dest;
}
/*
* call-seq:
* resize(<i>size[,interpolation = :linear]</i>) -> cvmat
*
* Resize image.
* <i>interpolation</i> is interpolation method:
* * :nn
* nearest-neighbor interpolation.
* * :linear
* bilinear interpolation (used by default)
* * :area
* resampling using pixel area relation. It is preferred method for image decimation that give moire-free results.
* In case of zooming it is similar to NN method.
* * :cubic
* bicubic interpolation.
* Return <i>self</i> resized image that it fits exactly to <i>size</i>. If ROI is set, the method consideres the ROI as supported as usual.
*/
VALUE
rb_resize(int argc, VALUE *argv, VALUE self)
{
VALUE size, interpolation;
rb_scan_args(argc, argv, "11", &size, &interpolation);
VALUE dest = new_object(VALUE_TO_CVSIZE(size), cvGetElemType(CVARR(self)));
cvResize(CVARR(self), CVARR(dest), CVMETHOD("INTERPOLATION_METHOD", interpolation, CV_INTER_LINEAR));
return dest;
}
/*
* call-seq:
* warp_affine(<i>map_matrix[,interpolation = :linear][,option = :fill_outliers][,fillval = 0]</i>) -> cvmat
*
* Applies affine transformation to the image.
*/
VALUE
rb_warp_affine(int argc, VALUE *argv, VALUE self)
{
VALUE map_matrix, interpolation, option, fill_value;
if (rb_scan_args(argc, argv, "13", &map_matrix, &interpolation, &option, &fill_value) < 4)
fill_value = INT2FIX(0);
if (!rb_obj_is_kind_of(map_matrix, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 1 (map matrix) should be %s (2x3).", rb_class2name(cCvMat::rb_class()));
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvWarpAffine(CVARR(self), CVARR(dest), CVMAT(map_matrix),
CVMETHOD("INTERPOLATION_METHOD", interpolation, CV_INTER_LINEAR) | CVMETHOD("WARP_FLAG", option, CV_WARP_FILL_OUTLIERS), VALUE_TO_CVSCALAR(fill_value));
return dest;
}
/*
* call-seq:
* CvMat.find_homograpy(<i>src_points, dst_points[,method = :all][,ransac_reproj_threshold = 0][,get_status = nil]</i>) -> cvmat
*
* Finds the perspective transformation between two planes.
* <i>src_points:</i> Coordinates of the points in the original plane, 2xN, Nx2, 3xN or Nx3 1-channel array (the latter two are for representation in homogeneous coordinates), where N is the number of points. 1xN or Nx1 2- or 3-channel array can also be passed.
* <i>dst_points:</i> Point coordinates in the destination plane, 2xN, Nx2, 3xN or Nx3 1-channel, or 1xN or Nx1 2- or 3-channel array.
* <i>method:</i> The method used to computed homography matrix; one of the following symbols:
* :all - a regular method using all the points
* :ransac - RANSAC-based robust method
* :lmeds - Least-Median robust method
* <i>ransac_reproj_threshold:</i> The maximum allowed reprojection error to treat a point pair as an inlier (used in the RANSAC method only). If src_points and dst_points are measured in pixels, it usually makes sense to set this parameter somewhere in the range 1 to 10.
* <i>get_status</i> If true, the optional output mask set by a robust method (:ransac or :lmeds) is returned additionally.
*/
VALUE
rb_find_homograpy(int argc, VALUE *argv, VALUE self)
{
VALUE src_points, dst_points, method, ransac_reproj_threshold, get_status;
rb_scan_args(argc, argv, "23", &src_points, &dst_points, &method, &ransac_reproj_threshold, &get_status);
VALUE homography = new_object(cvSize(3, 3), CV_32FC1);
int _method = CVMETHOD("HOMOGRAPHY_CALC_METHOD", method, 0);
double _ransac_reproj_threshold = NIL_P(ransac_reproj_threshold) ? 0.0 : NUM2DBL(ransac_reproj_threshold);
if ((_method != 0) && (!NIL_P(get_status)) && IF_BOOL(get_status, 1, 0, 0)) {
CvMat *src = CVMAT(src_points);
int num_points = MAX(src->rows, src->cols);
VALUE status = new_object(cvSize(num_points, 1), CV_8UC1);
cvFindHomography(src, CVMAT(dst_points), CVMAT(homography),
_method, _ransac_reproj_threshold, CVMAT(status));
return rb_assoc_new(homography, status);
}
else {
cvFindHomography(CVMAT(src_points), CVMAT(dst_points), CVMAT(homography),
_method, _ransac_reproj_threshold, NULL);
return homography;
}
}
/*
* call-seq:
* CvMat.rotation_matrix2D(<i>center,angle,scale</i>) -> cvmat
*
* Create new affine matrix of 2D rotation (2x3 32bit floating-point matrix).
* <i>center</i> is center of rotation (x, y).
* <i>angle</i> is rotation angle in degrees.
* Positive values mean counter-clockwise rotation
* (the coordinate origin is assumed at top-left corner).
* <i>scale</i> is isotropic scale factor.
*
* [ a b | (1 - a) * center.x - b * center.y ]
* [-b a | (b * center.x + (1 + a) * center.y ]
* where a = scale * cos(angle), b = scale * sin(angle)
*/
VALUE
rb_rotation_matrix2D(VALUE self, VALUE center, VALUE angle, VALUE scale)
{
VALUE map_matrix = new_object(cvSize(3,2), CV_MAKETYPE(CV_32F, 1));
cv2DRotationMatrix(VALUE_TO_CVPOINT2D32F(center), NUM2DBL(angle), NUM2DBL(scale), CVMAT(map_matrix));
return map_matrix;
}
/*
* call-seq:
* warp_perspective(<i>map_matrix[,interpolation=:linear][,option =:fill_outliers][,fillval=0])</i>) -> cvmat
*
* Applies perspective transformation to the image.
*/
VALUE
rb_warp_perspective(int argc, VALUE *argv, VALUE self)
{
VALUE map_matrix, interpolation, option, fillval;
if (rb_scan_args(argc, argv, "13", &map_matrix, &interpolation, &option, &fillval) < 4)
fillval = INT2FIX(0);
if (!rb_obj_is_kind_of(map_matrix, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 1 (map matrix) should be %s (3x3).", rb_class2name(cCvMat::rb_class()));
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvWarpPerspective(CVARR(self), CVARR(dest), CVMAT(map_matrix),
CVMETHOD("INTERPOLATION_METHOD", interpolation, CV_INTER_LINEAR) | CVMETHOD("WARP_FLAG",option, CV_WARP_FILL_OUTLIERS), VALUE_TO_CVSCALAR(fillval));
return dest;
}
/*
* call-seq:
* remap(<i>mapx,mapy[,interpolation=:linear][,option=:fill_outliers][,fillval=0]</i>) -> cvmat
*
* Applies generic geometrical transformation to the image.
* Transforms source image using the specified map:
* dst(x,y)<-src(mapx(x,y),mapy(x,y))
* Similar to other geometrical transformations, some interpolation method (specified by user) is used to
* extract pixels with non-integer coordinates.
*/
VALUE
rb_remap(int argc, VALUE *argv, VALUE self)
{
VALUE mapx, mapy, interpolation, option, fillval;
if (rb_scan_args(argc, argv, "23", &mapx, &mapy, &interpolation, &option, &fillval) < 5)
fillval = INT2FIX(0);
if (!rb_obj_is_kind_of(mapx, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 1 (map of x-coordinates) should be %s(CV_32F and single-channel).", rb_class2name(cCvMat::rb_class()));
if (!rb_obj_is_kind_of(mapy, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 2 (map of y-coordinates) should be %s(CV_32F and single-channel).", rb_class2name(cCvMat::rb_class()));
VALUE dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvRemap(CVARR(self), CVARR(dest), CVARR(mapx), CVARR(mapy),
CVMETHOD("INTERPOLATION_METHOD", interpolation, CV_INTER_LINEAR) | CVMETHOD("WARP_FLAG", option, CV_WARP_FILL_OUTLIERS), VALUE_TO_CVSCALAR(fillval));
return dest;
}
/*
* call-seq:
* log_polar(<i>center, magnitude, </i>)
*
* Remaps image to log-polar space.
*/
VALUE
rb_log_polar(int argc, VALUE *argv, VALUE self)
{
/*
VALUE size, center, m, flags, fillval, dest;
rb_scan_args(argc, argv, "3*", &size, &center, &m, &flags);
dest = cCvMat::new_object();
cvLogPolar(CVARR(self), CVARR(dest),
VALUE_TO_CVPOINT2D32F(center), NUM2DBL(m),
CVMETHOD("INTERPOLATION_METHOD", interpolation, CV_INTER_LINEAR) | CVMETHOD("WARP_FLAG", option, CV_WARP_FILL_OUTLIEARS), VALUE_TO_CVSCALAR(fillval));
return dest;
*/
return Qnil;
}
/*
* call-seq:
* erode(<i>[element = nil, iteration = 1]</i>) -> cvmat
*
* Create erodes image by using arbitrary structuring element.
* <i>element</i> is structuring element used for erosion.
* <i>element</i> should be IplConvKernel. If it is nil, a 3x3 rectangular structuring element is used.
* <i>iterations</i> is number of times erosion is applied.
*/
VALUE
rb_erode(int argc, VALUE *argv, VALUE self)
{
return rb_erode_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* erode!(<i>[element = nil][,iteration = 1]</i>) -> self
*
* Erodes image by using arbitrary structuring element.
* see also #erode.
*/
VALUE
rb_erode_bang(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration;
rb_scan_args(argc, argv, "02", &element, &iteration);
cvErode(CVARR(self), CVARR(self), IPLCONVKERNEL(element), IF_INT(iteration, 1));
return self;
}
/*
* call-seq:
* dilate(<i>[element = nil][,iteration = 1]</i>) -> cvmat
*
* Create dilates image by using arbitrary structuring element.
* <i>element</i> is structuring element used for erosion.
* <i>element</i> should be IplConvKernel. If it is nil, a 3x3 rectangular structuring element is used.
* <i>iterations</i> is number of times erosion is applied.
*/
VALUE
rb_dilate(int argc, VALUE *argv, VALUE self)
{
return rb_dilate_bang(argc, argv, rb_clone(self));
}
/*
* call-seq:
* dilate!(<i>[element = nil][,iteration = 1]</i>) -> self
*
* Dilate image by using arbitrary structuring element.
* see also #dilate.
*/
VALUE
rb_dilate_bang(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration;
rb_scan_args(argc, argv, "02", &element, &iteration);
cvDilate(CVARR(self), CVARR(self), IPLCONVKERNEL(element), IF_INT(iteration, 1));
return self;
}
VALUE
rb_morphology_internal(VALUE element, VALUE iteration, int operation, VALUE self)
{
CvArr* self_ptr = CVARR(self);
CvSize size = cvGetSize(self_ptr);
VALUE dest = new_object(size, cvGetElemType(self_ptr));
if (operation == CV_MOP_GRADIENT) {
CvMat* temp = cvCreateMat(size.height, size.width, cvGetElemType(self_ptr));
cvMorphologyEx(self_ptr, CVARR(dest), temp, IPLCONVKERNEL(element), CV_MOP_GRADIENT, IF_INT(iteration, 1));
cvReleaseMat(&temp);
}
else {
cvMorphologyEx(self_ptr, CVARR(dest), 0, IPLCONVKERNEL(element), operation, IF_INT(iteration, 1));
}
return dest;
}
/*
* call-seq:
* morpholohy(<i>operation[,element = nil][,iteration = 1]</i>) -> cvmat
*
* Performs advanced morphological transformations.
* <i>operation</i>
* Type of morphological operation, one of:
* CV_MOP_OPEN - opening
* CV_MOP_CLOSE - closing
* CV_MOP_GRADIENT - morphological gradient
* CV_MOP_TOPHAT - top hat
* CV_MOP_BLACKHAT - black hat
*/
VALUE
rb_morphology(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration, operation;
rb_scan_args(argc, argv, "12", &operation, &element, &iteration);
return rb_morphology_internal(element, iteration, CVMETHOD("MORPHOLOGICAL_OPERATION", operation, -1), self);
}
/*
* call-seq:
* morpholohy_open(<i>[element = nil][,iteration = 1]</i>) -> cvmat
*
* Performs advanced morphological transformations "Opening".
* dilate(erode(src,element),element)
*/
VALUE
rb_morphology_open(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration;
rb_scan_args(argc, argv, "02", &element, &iteration);
return rb_morphology_internal(element, iteration, CV_MOP_OPEN, self);
}
/*
* call-seq:
* morpholohy_close(<i>[element = nil][,iteration = 1]</i>) -> cvmat
*
* Performs advanced morphological transformations "Closing".
* erode(dilate(src,element),element)
*/
VALUE
rb_morphology_close(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration;
rb_scan_args(argc, argv, "02", &element, &iteration);
return rb_morphology_internal(element, iteration, CV_MOP_CLOSE, self);
}
/*
* call-seq:
* morpholohy_gradient(<i>[element = nil][,iteration = 1]</i>) -> cvmat
*
* Performs advanced morphological transformations "Morphological gradient".
* dilate(src,element)-erode(src,element)
*/
VALUE
rb_morphology_gradient(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration;
rb_scan_args(argc, argv, "02", &element, &iteration);
return rb_morphology_internal(element, iteration, CV_MOP_GRADIENT, self);
}
/*
* call-seq:
* morpholohy_tophat(<i>[element = nil][,iteration = 1]</i>) -> cvmat
*
* Performs advanced morphological transformations "tophat".
* src-open(src,element)
*/
VALUE
rb_morphology_tophat(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration;
rb_scan_args(argc, argv, "02", &element, &iteration);
return rb_morphology_internal(element, iteration, CV_MOP_TOPHAT, self);
}
/*
* call-seq:
* morpholohy_blackhat(<i>[element = nil][,iteration = 1]</i>) -> cvmat
*
* Performs advanced morphological transformations "blackhat".
* close(src,element)-src
*/
VALUE
rb_morphology_blackhat(int argc, VALUE *argv, VALUE self)
{
VALUE element, iteration, dest;
rb_scan_args(argc, argv, "02", &element, &iteration);
return rb_morphology_internal(element, iteration, CV_MOP_BLACKHAT, self);
}
VALUE
rb_smooth(int argc, VALUE *argv, VALUE self)
{
VALUE smoothtype, p1, p2, p3, p4;
rb_scan_args(argc, argv, "14", &smoothtype, &p1, &p2, &p3, &p4);
int _smoothtype = CVMETHOD("SMOOTHING_TYPE", smoothtype, -1);
VALUE (*smooth_func)(int c, VALUE* v, VALUE s);
switch (_smoothtype) {
case CV_BLUR_NO_SCALE:
smooth_func = rb_smooth_blur_no_scale;
break;
case CV_BLUR:
smooth_func = rb_smooth_blur;
break;
case CV_GAUSSIAN:
smooth_func = rb_smooth_gaussian;
break;
case CV_MEDIAN:
smooth_func = rb_smooth_median;
break;
case CV_BILATERAL:
smooth_func = rb_smooth_bilateral;
break;
default:
smooth_func = rb_smooth_gaussian;
break;
}
return (*smooth_func)(argc - 1, argv + 1, self);
}
/*
* call-seq:
* smooth_blur_no_scale(<i>[p1 = 3, p2 = 3]</i>) -> cvmat
*
* Smooths the image by simple blur with no scaling.
* * 8bit unsigned -> return 16bit unsigned
* * 32bit floating point -> return 32bit floating point
* <b>support single-channel image only.</b>
*/
VALUE
rb_smooth_blur_no_scale(int argc, VALUE *argv, VALUE self)
{
SUPPORT_C1_ONLY(self);
VALUE p1, p2, dest;
rb_scan_args(argc, argv, "02", &p1, &p2);
int type = cvGetElemType(CVARR(self)), dest_type;
switch (CV_MAT_DEPTH(type)) {
case CV_8U:
dest_type = CV_16U;
break;
case CV_32F:
dest_type = CV_32F;
break;
default:
rb_raise(rb_eNotImpError, "unsupport format. (support 8bit unsigned/signed or 32bit floating point only)");
}
dest = new_object(cvGetSize(CVARR(self)), dest_type);
cvSmooth(CVARR(self), CVARR(dest), CV_BLUR_NO_SCALE, IF_INT(p1, 3), IF_INT(p2, 3));
return dest;
}
/*
* call-seq:
* smooth_blur(<i>[p1 = 3, p2 = 3]</i>) -> cvmat
*
* Smooths the image by simple blur.
* Summation over a pixel <i>p1</i> x <i>p2</i> neighborhood with subsequent scaling by 1 / (p1*p2).
*/
VALUE
rb_smooth_blur(int argc, VALUE *argv, VALUE self)
{
SUPPORT_C1C3_ONLY(self);
VALUE p1, p2, dest;
rb_scan_args(argc, argv, "02", &p1, &p2);
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSmooth(CVARR(self), CVARR(dest), CV_BLUR, IF_INT(p1, 3), IF_INT(p2, 3));
return dest;
}
/*
* call-seq:
* smooth_gaussian(<i>[p1 = 3, p2 = 3, p3 = 0.0, p4 = 0.0]</i>) -> cvmat
*
* Smooths the image by gaussian blur.
* Convolving image with <i>p1</i> x <i>p2</i> Gaussian kernel.
*
* <i>p3</i> may specify Gaussian sigma (standard deviation).
* If it is zero, it is calculated from the kernel size:
* sigma = (n/2 - 1)*0.3 + 0.8, where n = p1 for horizontal kernel,
* n = p2 for vertical kernel.
*
* <i>p4</i> is in case of non-square Gaussian kernel the parameter.
* It may be used to specify a different (from p3) sigma in the vertical direction.
*/
VALUE
rb_smooth_gaussian(int argc, VALUE *argv, VALUE self)
{
SUPPORT_C1C3_ONLY(self);
VALUE p1, p2, p3, p4, dest;
rb_scan_args(argc, argv, "04", &p1, &p2, &p3, &p4);
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSmooth(CVARR(self), CVARR(dest), CV_GAUSSIAN, IF_INT(p1, 3), IF_INT(p2, 3), IF_DBL(p3, 0.0), IF_DBL(p4, 0.0));
return dest;
}
/*
* call-seq:
* smooth_median(<i>[p1 = 3]</i>) -> cvmat
*
* Smooths the image by median blur.
* Finding median of <i>p1</i> x <i>p1</i> neighborhood (i.e. the neighborhood is square).
*/
VALUE
rb_smooth_median(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8U_ONLY(self);
SUPPORT_C1C3_ONLY(self);
VALUE p1, dest;
rb_scan_args(argc, argv, "01", &p1);
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSmooth(CVARR(self), CVARR(dest), CV_MEDIAN, IF_INT(p1, 3));
return dest;
}
/*
* call-seq:
* smooth_bilateral(<i>[p1 = 3][p2 = 3]</i>) -> cvmat
*
* Smooths the image by bilateral filter.
* Applying bilateral 3x3 filtering with color sigma=<i>p1</i> and space sigma=<i>p2</i>.
*/
VALUE
rb_smooth_bilateral(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8U_ONLY(self);
SUPPORT_C1C3_ONLY(self);
VALUE p1, p2, dest;
rb_scan_args(argc, argv, "02", &p1, &p2);
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvSmooth(CVARR(self), CVARR(dest), CV_BILATERAL, IF_INT(p1, 3), IF_INT(p2, 3));
return dest;
}
/*
* call-seq:
* filter2d(<i>kernel[,anchor]</i>) -> cvmat
*
* Convolves image with the kernel.
* Convolution kernel, single-channel floating point matrix (or same depth of self's).
* If you want to apply different kernels to different channels,
* split the image using CvMat#split into separate color planes and process them individually.
*/
VALUE
rb_filter2d(int argc, VALUE *argv, VALUE self)
{
VALUE kernel, anchor, dest;
rb_scan_args(argc, argv, "11", &kernel, &anchor);
if (!rb_obj_is_kind_of(kernel, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 1 (kernel) should be %s.", rb_class2name(cCvMat::rb_class()));
int type = cvGetElemType(CVARR(kernel));
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvFilter2D(CVARR(self), CVARR(dest), CVMAT(kernel), NIL_P(anchor) ? cvPoint(-1,-1) : VALUE_TO_CVPOINT(anchor));
return dest;
}
/*
* call-seq:
* copy_make_border_constant(<i>size, offset[,value = CvScalar.new(0)]</i>)
*
* Copies image and makes border around it.
* Border is filled with the fixed value, passed as last parameter of the function.
*/
VALUE
rb_copy_make_border_constant(int argc, VALUE *argv, VALUE self)
{
VALUE size, offset, value, dest;
rb_scan_args(argc, argv, "21", &size, &offset, &value);
dest = cCvMat::new_object(VALUE_TO_CVSIZE(size), cvGetElemType(CVARR(self)));
cvCopyMakeBorder(CVARR(self), CVARR(dest), VALUE_TO_CVPOINT(offset), IPL_BORDER_CONSTANT, NIL_P(value) ? cvScalar(0) : VALUE_TO_CVSCALAR(value));
return dest;
}
/*
* call-seq:
* copy_make_border_replicate(<i>size, offset</i>)
*
* Copies image and makes border around it.
* The pixels from the top and bottom rows,
* the left-most and right-most columns are replicated to fill the border.
*/
VALUE
rb_copy_make_border_replicate(int argc, VALUE *argv, VALUE self)
{
VALUE size, offset, dest;
rb_scan_args(argc, argv, "20", &size, &offset);
dest = cCvMat::new_object(VALUE_TO_CVSIZE(size), cvGetElemType(CVARR(self)));
cvCopyMakeBorder(CVARR(self), CVARR(dest), VALUE_TO_CVPOINT(offset), IPL_BORDER_REPLICATE);
return dest;
}
/*
* call-seq:
* integral(<i>need_sqsum = false, need_tilted_sum = false</i>) -> [cvmat, cvmat or nil, cvmat or nil]
*
* Calculates integral images.
* If <i>need_sqsum</i> = true, calculate the integral image for squared pixel values.
* If <i>need_tilted_sum</i> = true, calculate the integral for the image rotated by 45 degrees.
*
* sum(X,Y)=sumx<X,y<Yimage(x,y)
* sqsum(X,Y)=sumx<X,y<Yimage(x,y)2
* tilted_sum(X,Y)=sumy<Y,abs(x-X)<yimage(x,y)
*
* Using these integral images, one may calculate sum, mean, standard deviation over arbitrary up-right or rotated rectangular region of the image in a constant time.
*/
VALUE
rb_integral(int argc, VALUE *argv, VALUE self)
{
VALUE sum, sqsum, tilted_sum, dest;
rb_scan_args(argc, argv, "02", &sqsum, &tilted_sum);
CvSize size = cvSize(cvGetSize(CVARR(self)).width + 1, cvGetSize(CVARR(self)).height + 1);
int cn = CV_MAT_CN(cvGetElemType(CVARR(self)));
sum = cCvMat::new_object(size, CV_MAKETYPE(CV_64F, cn));
sqsum = (sqsum == Qtrue ? cCvMat::new_object(size, CV_MAKETYPE(CV_64F, cn)) : Qnil);
tilted_sum = (tilted_sum == Qtrue ? cCvMat::new_object(size, CV_MAKETYPE(CV_64F, cn)) : Qnil);
cvIntegral(CVARR(self), CVARR(sum), NIL_P(sqsum) ? NULL : CVARR(sqsum), NIL_P(tilted_sum) ? NULL : CVARR(tilted_sum));
dest = rb_ary_new3(1, sum);
if(sqsum)
rb_ary_push(dest, sqsum);
if(tilted_sum)
rb_ary_push(dest, tilted_sum);
return dest;
}
VALUE
rb_threshold_internal(int threshold_type, VALUE threshold, VALUE max_value, VALUE use_otsu, VALUE self)
{
CvArr* self_ptr = CVARR(self);
VALUE dest = cCvMat::new_object(cvGetSize(self_ptr), cvGetElemType(self_ptr));
int otsu = (use_otsu == Qtrue) && ((threshold_type & CV_THRESH_OTSU) == 0);
int type = threshold_type | (otsu ? CV_THRESH_OTSU : 0);
double otsu_threshold = cvThreshold(self_ptr, CVARR(dest), NUM2DBL(threshold), NUM2DBL(max_value), type);
if ((use_otsu == Qtrue) || (threshold_type & CV_THRESH_OTSU)) {
return rb_assoc_new(dest, DBL2NUM(otsu_threshold));
}
else
return dest;
}
/*
* call-seq:
* threshold(<i>threshold, max_value, threshold_type[,use_otsu = false]</i>)
*
* Applies fixed-level threshold to array elements.
*
*/
VALUE
rb_threshold(int argc, VALUE *argv, VALUE self)
{
VALUE threshold, max_value, threshold_type, use_otsu;
rb_scan_args(argc, argv, "31", &threshold, &max_value, &threshold_type, &use_otsu);
const int INVALID_TYPE = -1;
int type = CVMETHOD("THRESHOLD_TYPE", threshold_type, INVALID_TYPE);
if (type == INVALID_TYPE)
rb_raise(rb_eArgError, "Invalid threshold type.");
return rb_threshold_internal(type, threshold, max_value, use_otsu, self);
}
/*
* call-seq:
* threshold_binary(<i>threshold, max_value[,use_otsu = false]</i>)
*
* Applies fixed-level threshold to array elements.
*
* dst(x,y) = max_value, if src(x,y)>threshold
* 0, otherwise
*/
VALUE
rb_threshold_binary(int argc, VALUE *argv, VALUE self)
{
VALUE threshold, max_value, use_otsu;
rb_scan_args(argc, argv, "21", &threshold, &max_value, &use_otsu);
return rb_threshold_internal(CV_THRESH_BINARY, threshold, max_value, use_otsu, self);
}
/*
* call-seq:
* threshold_binary_inverse(<i>threshold, max_value[,use_otsu = false]</i>)
*
* Applies fixed-level threshold to array elements.
*
* dst(x,y) = 0, if src(x,y)>threshold
* max_value, otherwise
*/
VALUE
rb_threshold_binary_inverse(int argc, VALUE *argv, VALUE self)
{
VALUE threshold, max_value, use_otsu;
rb_scan_args(argc, argv, "21", &threshold, &max_value, &use_otsu);
return rb_threshold_internal(CV_THRESH_BINARY_INV, threshold, max_value, use_otsu, self);
}
/*
* call-seq:
* threshold_trunc(<i>threshold[,use_otsu = false]</i>)
*
* Applies fixed-level threshold to array elements.
*
* dst(x,y) = threshold, if src(x,y)>threshold
* src(x,y), otherwise
*/
VALUE
rb_threshold_trunc(int argc, VALUE *argv, VALUE self)
{
VALUE threshold, use_otsu;
rb_scan_args(argc, argv, "11", &threshold, &use_otsu);
return rb_threshold_internal(CV_THRESH_TRUNC, threshold, INT2NUM(0), use_otsu, self);
}
/*
* call-seq:
* threshold_to_zero(<i>threshold[,use_otsu = false]</i>)
*
* Applies fixed-level threshold to array elements.
*
* dst(x,y) = src(x,y), if src(x,y)>threshold
* 0, otherwise
*/
VALUE
rb_threshold_to_zero(int argc, VALUE *argv, VALUE self)
{
VALUE threshold, use_otsu;
rb_scan_args(argc, argv, "11", &threshold, &use_otsu);
return rb_threshold_internal(CV_THRESH_TOZERO, threshold, INT2NUM(0), use_otsu, self);
}
/*
* call-seq:
* threshold_to_zero_inverse(<i>threshold[,use_otsu = false]</i>)
*
* Applies fixed-level threshold to array elements.
*
* dst(x,y) = 0, if src(x,y)>threshold
* src(x,y), otherwise
*/
VALUE
rb_threshold_to_zero_inverse(int argc, VALUE *argv, VALUE self)
{
VALUE threshold, use_otsu;
rb_scan_args(argc, argv, "11", &threshold, &use_otsu);
return rb_threshold_internal(CV_THRESH_TOZERO_INV, threshold, INT2NUM(0), use_otsu, self);
}
/*
* call-seq:
* pyr_down(<i>[filter = :gaussian_5x5]</i>) -> cvmat
*
* Return downsamples image.
*
* This operation performs downsampling step of Gaussian pyramid decomposition.
* First it convolves source image with the specified filter and then downsamples the image by rejecting even rows and columns.
*
* note: filter - only :gaussian_5x5 is currently supported.
*/
VALUE
rb_pyr_down(int argc, VALUE *argv, VALUE self)
{
VALUE filter_type, dest;
rb_scan_args(argc, argv, "01", &filter_type);
int filter = CV_GAUSSIAN_5x5;
if (argc > 0) {
switch (TYPE(filter_type)) {
case T_SYMBOL:
// currently suport CV_GAUSSIAN_5x5 only.
break;
default:
rb_raise(rb_eArgError, "argument 1 (filter_type) should be Symbol.");
}
}
CvSize size = cvGetSize(CVARR(self));
dest = cCvMat::new_object(size.height / 2, size.width / 2, cvGetElemType(CVARR(self)));
cvPyrDown(CVARR(self), CVARR(dest), filter);
return dest;
}
/*
* call-seq:
* pyr_up(<i>[filter = :gaussian_5x5]</i>) -> cvmat
*
* Return upsamples image.
*
* This operation performs up-sampling step of Gaussian pyramid decomposition.
* First it upsamples the source image by injecting even zero rows and columns and then convolves result with the specified filter multiplied by 4 for interpolation.
* So the destination image is four times larger than the source image.
*
* note: filter - only :gaussian_5x5 is currently supported.
*/
VALUE
rb_pyr_up(int argc, VALUE *argv, VALUE self)
{
VALUE filter_type, dest;
rb_scan_args(argc, argv, "01", &filter_type);
int filter = CV_GAUSSIAN_5x5;
if (argc > 0) {
switch (TYPE(filter_type)) {
case T_SYMBOL:
// currently suport CV_GAUSSIAN_5x5 only.
break;
default:
rb_raise(rb_eArgError, "argument 1 (filter_type) should be Symbol.");
}
}
CvSize size = cvGetSize(CVARR(self));
dest = cCvMat::new_object(size.height * 2, size.width * 2, cvGetElemType(CVARR(self)));
cvPyrUp(CVARR(self), CVARR(dest), filter);
return dest;
}
/*
* call-seq:
* flood_fill(<i>seed_point, new_val, lo_diff, up_diff[,flood_fill_option]</i>) -> [cvmat, cvconnectedcomp, iplimage(mask)]
*
* Return image filled a connnected compoment with given color.
* This operation fills a connected component starting from the seed point with the specified color.
* The connectivity is determined by the closeness of pixel values.
* The pixel at (x, y) is considered to belong to the repainted domain if:
*
* src(x',y')-lo_diff<=src(x,y)<=src(x',y')+up_diff, grayscale image, floating range
* src(seed.x,seed.y)-lo<=src(x,y)<=src(seed.x,seed.y)+up_diff, grayscale image, fixed range
* src(x',y')r-lo_diffr<=src(x,y)r<=src(x',y')r+up_diffr and
* src(x',y')g-lo_diffg<=src(x,y)g<=src(x',y')g+up_diffg and
* src(x',y')b-lo_diffb<=src(x,y)b<=src(x',y')b+up_diffb, color image, floating range
* src(seed.x,seed.y)r-lo_diffr<=src(x,y)r<=src(seed.x,seed.y)r+up_diffr and
* src(seed.x,seed.y)g-lo_diffg<=src(x,y)g<=src(seed.x,seed.y)g+up_diffg and
* src(seed.x,seed.y)b-lo_diffb<=src(x,y)b<=src(seed.x,seed.y)b+up_diffb, color image, fixed range
*
* where src(x',y') is value of one of pixel neighbors.
* That is, to be added to the connected component, a pixel's color/brightness should be close enough to:
* * color/brightness of one of its neighbors that are already referred to the connected component in case of floating range
* * color/brightness of the seed point in case of fixed range.
*
* arguments
* * seed_point -The starting point.
* * new_val - New value of repainted domain pixels.
* * lo_diff - Maximal lower brightness/color difference between the currently observed pixel and one of its neighbor belong to the component or seed pixel to add the pixel to component. In case of 8-bit color images it is packed value.
* * up_diff - Maximal upper brightness/color difference between the currently observed pixel and one of its neighbor belong to the component or seed pixel to add the pixel to component. In case of 8-bit color images it is packed value.
*
* and flood_fill_option
* :connectivity => 4 or 8, 4 default
* Connectivity determines which neighbors of a pixel are considered.
* :fixed_range => true or false, false default
* If set the difference between the current pixel and seed pixel is considered, otherwise difference between neighbor pixels is considered (the range is floating).
* :mask_only => true or false, false default
* If set, the function does not fill the image(new_val is ignored), but the fills mask.
*
* note: <i>flood_fill_option</i>'s default value is CvMat::FLOOD_FILL_OPTION.
*/
VALUE
rb_flood_fill(int argc, VALUE *argv, VALUE self)
{
return rb_flood_fill_bang(argc, argv, copy(self));
}
/*
* call-seq:
* flood_fill!(<i>seed_point, new_val, lo_diff, up_diff[,flood_fill_option]</i>) -> [self, cvconnectedcomp, iplimage(mask)]
*
* Fills a connected component with given color.
* see CvMat#flood_fill
*/
VALUE
rb_flood_fill_bang(int argc, VALUE *argv, VALUE self)
{
VALUE seed_point, new_val, lo_diff, up_diff, flood_fill_option, mask, comp;
rb_scan_args(argc, argv, "23", &seed_point, &new_val, &lo_diff, &up_diff, &flood_fill_option);
flood_fill_option = FLOOD_FILL_OPTION(flood_fill_option);
int flags = FF_CONNECTIVITY(flood_fill_option);
if (FF_FIXED_RANGE(flood_fill_option)) {
flags |= CV_FLOODFILL_FIXED_RANGE;
}
if (FF_MASK_ONLY(flood_fill_option)) {
flags |= CV_FLOODFILL_MASK_ONLY;
}
CvSize size = cvGetSize(CVARR(self));
mask = cIplImage::new_object(size.width + 2, size.height + 2, CV_MAKETYPE(CV_8U, 1));
comp = cCvConnectedComp::new_object();
cvFloodFill(CVARR(self),
VALUE_TO_CVPOINT(seed_point),
VALUE_TO_CVSCALAR(new_val),
NIL_P(lo_diff) ? cvScalar(0) : VALUE_TO_CVSCALAR(lo_diff),
NIL_P(up_diff) ? cvScalar(0) : VALUE_TO_CVSCALAR(up_diff),
CVCONNECTEDCOMP(comp),
flags,
CVARR(mask));
cvSetImageROI(IPLIMAGE(mask), cvRect(1, 1, size.width, size.height));
return rb_ary_new3(3, self, comp, mask);
}
/*
* call-seq:
* find_contours([find_contours_options]) -> cvchain or cvcontour or nil
*
* Finds contours in binary image, and return contours as CvContour or CvChain.
* If contours not found, return nil.
*
* <i>flood_fill_option</i> should be Hash include these keys.
* :mode - Retrieval mode.
* :external - retrive only the extreme outer contours
* :list - retrieve all the contours and puts them in the list.(default)
* :ccomp - retrieve all the contours and organizes them into two-level hierarchy:
* top level are external boundaries of the components, second level are bounda boundaries of the holes
* :tree - retrieve all the contours and reconstructs the full hierarchy of nested contours
* Connectivity determines which neighbors of a pixel are considered.
* :method - Approximation method.
* :code - output contours in the Freeman chain code. All other methods output polygons (sequences of vertices).
* :approx_none - translate all the points from the chain code into points;
* :approx_simple - compress horizontal, vertical, and diagonal segments, that is, the function leaves only their ending points;(default)
* :approx_tc89_l1
* :approx_tc89_kcos - apply one of the flavors of Teh-Chin chain approximation algorithm.
* If set the difference between the current pixel and seed pixel is considered,
* otherwise difference between neighbor pixels is considered (the range is floating).
* :offset - Offset, by which every contour point is shifted.
* This is useful if the contours are extracted from the image ROI
* and then they should be analyzed in the whole image context. Should be CvPoint.
*
* note: <i>find_contours_option</i>'s default value is CvMat::FIND_CONTOURS_OPTION.
*
* <b>support single-channel 8bit unsigned image only.</b>
*
* note: Non-zero pixels are treated as 1's, zero pixels remain 0's
* that is image treated as binary. To get such a binary image from grayscale,
* one may use threshold, adaptive_threshold or canny.
*/
VALUE
rb_find_contours(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
return rb_find_contours_bang(argc, argv, copy(self));
}
/*
* call-seq:
* find_contours!([find_contours_options]) -> cvchain or chcontour or nil
*
* Finds contours in binary image.
* The function modifies the source image content.
* (Because the copy is not made, it is slightly faster than find_contours.)
*
* see find_contours
*
* <b>support single-channel 8bit unsigned image only.</b>
*/
VALUE
rb_find_contours_bang(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE find_contours_option, klass, element_klass, storage;
rb_scan_args(argc, argv, "01", &find_contours_option);
CvSeq *contour = NULL;
find_contours_option = FIND_CONTOURS_OPTION(find_contours_option);
int mode = FC_MODE(find_contours_option);
int method = FC_METHOD(find_contours_option);
int header_size, element_size;
if (method == CV_CHAIN_CODE) {
klass = cCvChain::rb_class();
element_klass = cCvChainCode::rb_class();
header_size = sizeof(CvChain);
element_size = sizeof(CvChainCode);
}
else {
klass = cCvContour::rb_class();
element_klass = cCvPoint::rb_class();
header_size = sizeof(CvContour);
element_size = sizeof(CvPoint);
}
storage = cCvMemStorage::new_object();
if(cvFindContours(CVARR(self), CVMEMSTORAGE(storage),
&contour, header_size, mode, method, FC_OFFSET(find_contours_option)) == 0)
return Qnil;
return cCvSeq::new_sequence(klass, contour, element_klass, storage);
}
/*
* call-seq:
* pyr_segmentation(<i>level, threshold1, threshold2</i>) -> [cvmat, cvseq(include cvconnectedcomp)]
*
* Does image segmentation by pyramids.
* The pyramid builds up to the level <i>level<i>.
* The links between any pixel a on <i>level<i>i and
* its candidate father pixel b on the adjacent level are established if
* p(c(a),c(b)) < threshold1. After the connected components are defined, they are joined into several clusters. Any two segments A and B belong to the same cluster, if
* p(c(A),c(B)) < threshold2. The input image has only one channel, then
* p(c^2,c^2)=|c^2-c^2|. If the input image has three channels (red, green and blue), then
* p(c^2,c^2)=0,3*(c^2 r-c^2 r)+0.59*(c^2 g-c^2 g)+0,11*(c^2 b-c^2 b) . There may be more than one connected component per a cluster.
*
* Return segmented image and sequence of connected components.
* <b>support single-channel or 3-channel 8bit unsigned image only</b>
*/
VALUE
rb_pyr_segmentation(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8U_ONLY(self);
SUPPORT_C1C3_ONLY(self);
VALUE level, threshold1, threshold2, storage, dest;
rb_scan_args(argc, argv, "30", &level, &threshold1, &threshold2);
IplImage *src = IPLIMAGE(self);
int l = FIX2INT(level);
double t1 = NUM2DBL(threshold1), t2 = NUM2DBL(threshold2);
CvRect roi = cvGetImageROI(src);
if (l <= 0)
rb_raise(rb_eArgError, "argument 1 (level) should be > 0.");
if(((roi.width | roi.height) & ((1 << l) - 1)) != 0)
rb_raise(rb_eArgError, "bad image size on level %d.", FIX2INT(level));
if (t1 < 0)
rb_raise(rb_eArgError, "argument 2 (threshold for establishing the link) should be >= 0.");
if (t2 < 0)
rb_raise(rb_eArgError, "argument 3 (threshold for the segments clustering) should be >= 0.");
dest = cIplImage::new_object(cvGetSize(src), cvGetElemType(src));
CvSeq *comp = 0;
storage = cCvMemStorage::new_object();
cvPyrSegmentation(src,
IPLIMAGE(dest),
CVMEMSTORAGE(storage),
&comp,
l, t1, t2);
if(!comp)
comp = cvCreateSeq(CV_SEQ_CONNECTED_COMP, sizeof(CvSeq), sizeof(CvConnectedComp), CVMEMSTORAGE(storage));
return rb_ary_new3(2, dest, cCvSeq::new_sequence(cCvSeq::rb_class(), comp, cCvConnectedComp::rb_class(), storage));
}
/*
* call-seq:
* pyr_mean_shift_filtering(<i>sp, sr[,max_level = 1][termcrit = CvTermCriteria.new(5,1)]</i>) -> cvmat
*
* Does meanshift image segmentation.
*
* sp - The spatial window radius.
* sr - The color window radius.
* max_level - Maximum level of the pyramid for the segmentation.
* termcrit - Termination criteria: when to stop meanshift iterations.
*
* This method is implements the filtering stage of meanshift segmentation,
* that is, the output of the function is the filtered "posterized" image with color gradients and fine-grain texture flattened.
* At every pixel (X,Y) of the input image (or down-sized input image, see below)
* the function executes meanshift iterations, that is, the pixel (X,Y) neighborhood in the joint space-color hyperspace is considered:
* {(x,y): X-sp≤x≤X+sp && Y-sp≤y≤Y+sp && ||(R,G,B)-(r,g,b)|| ≤ sr},
* where (R,G,B) and (r,g,b) are the vectors of color components at (X,Y) and (x,y),
* respectively (though, the algorithm does not depend on the color space used,
* so any 3-component color space can be used instead).
* Over the neighborhood the average spatial value (X',Y')
* and average color vector (R',G',B') are found and they act as the neighborhood center on the next iteration:
* (X,Y)~(X',Y'), (R,G,B)~(R',G',B').
* After the iterations over, the color components of the initial pixel (that is, the pixel from where the iterations started)
* are set to the final value (average color at the last iteration):
* I(X,Y) <- (R*,G*,B*).
* Then max_level > 0, the gaussian pyramid of max_level+1 levels is built,
* and the above procedure is run on the smallest layer.
* After that, the results are propagated to the larger layer and the iterations are run again
* only on those pixels where the layer colors differ much (>sr) from the lower-resolution layer,
* that is, the boundaries of the color regions are clarified.
*
* Note, that the results will be actually different from the ones obtained by running the meanshift procedure on the whole original image (i.e. when max_level==0).
*/
VALUE
rb_pyr_mean_shift_filtering(int argc, VALUE *argv, VALUE self)
{
VALUE spatial_window_radius, color_window_radius, max_level, termcrit, dest;
rb_scan_args(argc, argv, "22", &spatial_window_radius, &color_window_radius, &max_level, &termcrit);
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvPyrMeanShiftFiltering(CVARR(self), CVARR(dest),
NUM2DBL(spatial_window_radius),
NUM2DBL(color_window_radius),
IF_INT(max_level, 1),
NIL_P(termcrit) ? cvTermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 5, 1) : VALUE_TO_CVTERMCRITERIA(termcrit));
return dest;
}
/*
* call-seq:
* watershed -> cvmat(mean markers:cv32s)
*
* Does watershed segmentation.
*/
VALUE
rb_watershed(VALUE self, VALUE markers)
{
cvWatershed(CVARR(self), CVARR(markers));
return markers;
}
/*
* call-seq:
* moments -> array(include CvMoments)
*
* Calculates moments.
*/
VALUE
rb_moments(int argc, VALUE *argv, VALUE self)
{
VALUE is_binary, moments;
rb_scan_args(argc, argv, "01", &is_binary);
IplImage image = *IPLIMAGE(self);
int cn = CV_MAT_CN(cvGetElemType(CVARR(self)));
moments = rb_ary_new();
for(int i = 1; i <= cn; i++) {
cvSetImageCOI(&image, i);
rb_ary_push(moments, cCvMoments::new_object(&image, TRUE_OR_FALSE(is_binary, 0)));
}
return moments;
}
/*
* call-seq:
* hough_line_standard(<i>rho, theta, threshold</i>) -> cvseq(include CvLine)
*
* Finds lines in binary image using standard(classical) Hough transform.
* * rho - Distance resolution in pixel-related units.
* * theta - Angle resolution measured in radians.
* * threshold - Threshold parameter. A line is returned by the function if the corresponding accumulator value is greater than threshold.
*/
VALUE
rb_hough_lines_standard(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE rho, theta, threshold, storage;
rb_scan_args(argc, argv, "30", &rho, &theta, &threshold);
storage = cCvMemStorage::new_object();
CvSeq *seq = cvHoughLines2(CVARR(copy(self)), CVMEMSTORAGE(storage),
CV_HOUGH_STANDARD, NUM2DBL(rho), NUM2DBL(theta), NUM2INT(threshold));
return cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvLine::rb_class(), storage);
}
/*
* call-seq:
* hough_line_probabilistic(<i>rho, theta, threshold, min_length, max_gap</i>) -> cvseq(include CvTwoPoints)
*
* Finds lines in binary image using probabilistic Hough transform.
* * rho - Distance resolution in pixel-related units.
* * theta - Angle resolution measured in radians.
* * threshold - Threshold parameter. A line is returned by the function if the corresponding accumulator value is greater than threshold.
* * min_length - The minimum line length.
* * max_gap - The maximum gap between line segments lieing on the same line to treat them as the single line segment (i.e. to join them).
*/
VALUE
rb_hough_lines_probabilistic(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE rho, theta, threshold, p1, p2, storage;
rb_scan_args(argc, argv, "50", &rho, &theta, &threshold, &p1, &p2);
storage = cCvMemStorage::new_object();
CvSeq *seq = cvHoughLines2(CVARR(copy(self)), CVMEMSTORAGE(storage),
CV_HOUGH_PROBABILISTIC, NUM2DBL(rho), NUM2DBL(theta), NUM2INT(threshold),
NUM2DBL(p1), NUM2DBL(p2));
return cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvTwoPoints::rb_class(), storage);
}
/*
* call-seq:
* hough_line_multi_scale(<i>rho, theta, threshold, div_rho, div_theta</i>) -> cvseq(include CvLine)
*
* Finds lines in binary image using multi-scale variant of classical Hough transform.
* * rho - Distance resolution in pixel-related units.
* * theta - Angle resolution measured in radians.
* * threshold - Threshold parameter. A line is returned by the function if the corresponding accumulator value is greater than threshold.
* * div_rho = divisor for distance resolution rho.
* * div_theta = divisor for angle resolution theta.
*/
VALUE
rb_hough_lines_multi_scale(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE rho, theta, threshold, p1, p2, storage;
rb_scan_args(argc, argv, "50", &rho, &theta, &threshold, &p1, &p2);
storage = cCvMemStorage::new_object();
CvSeq *seq = cvHoughLines2(CVARR(copy(self)), CVMEMSTORAGE(storage),
CV_HOUGH_MULTI_SCALE, NUM2DBL(rho), NUM2DBL(theta), NUM2INT(threshold),
NUM2DBL(p1), NUM2DBL(p2));
return cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvLine::rb_class(), storage);
}
/*
* call-seq:
* hough_circles_gradient(<i>dp, min_dist, threshold_canny, threshold_accumulate, min_radius = 0, max_radius = max(width,height)</i>) -> cvseq(include CvCircle32f)
*
* Finds circles in grayscale image using Hough transform.
*/
VALUE
rb_hough_circles_gradient(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE dp, min_dist, threshold_canny, threshold_accumulate, min_radius, max_radius, storage;
rb_scan_args(argc, argv, "24", &dp, &min_dist, &threshold_canny, &threshold_accumulate, &min_radius, &max_radius);
storage = cCvMemStorage::new_object();
CvSeq *seq = cvHoughCircles(CVARR(self), CVMEMSTORAGE(storage),
CV_HOUGH_GRADIENT, NUM2DBL(dp), NUM2DBL(min_dist),
NUM2DBL(threshold_canny), NUM2DBL(threshold_accumulate),
IF_INT(min_radius, 0), IF_INT(max_radius, 0));
return cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvCircle32f::rb_class(), storage);
}
/*
* call-seq:
* inpaint_ns(<i>mask, radius</i>) -> cvmat
*
* Inpaints the selected region in the image by Navier-Stokes based method.
* The radius of circlular neighborhood of each point inpainted that is considered by the algorithm.
*/
VALUE
rb_inpaint_ns(VALUE self, VALUE mask, VALUE radius)
{
SUPPORT_8U_ONLY(self);
SUPPORT_C1C3_ONLY(self);
VALUE dest;
if (!(rb_obj_is_kind_of(mask, cCvMat::rb_class())) || cvGetElemType(CVARR(mask)) != CV_8UC1)
rb_raise(rb_eTypeError, "argument 1 (mask) should be mask image.");
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvInpaint(CVARR(self), CVARR(mask), CVARR(dest), NUM2DBL(radius), CV_INPAINT_NS);
return dest;
}
/*
* call-seq:
* inpaint_telea(<i>mask, radius</i>) -> cvmat
*
* Inpaints the selected region in the image by The method by Alexandru Telea's method.
* The radius of circlular neighborhood of each point inpainted that is considered by the algorithm.
*/
VALUE
rb_inpaint_telea(VALUE self, VALUE mask, VALUE radius)
{
SUPPORT_8U_ONLY(self);
SUPPORT_C1C3_ONLY(self);
VALUE dest;
if (!(rb_obj_is_kind_of(mask, cCvMat::rb_class())) || cvGetElemType(CVARR(mask)) != CV_8UC1)
rb_raise(rb_eTypeError, "argument 1 (mask) should be mask image.");
dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvInpaint(CVARR(self), CVARR(mask), CVARR(dest), NUM2DBL(radius), CV_INPAINT_TELEA);
return dest;
}
/*
* call-seq:
* equalize_hist - cvmat
*
* Equalize histgram of grayscale of image.
*
* equalizes histogram of the input image using the following algorithm:
* 1. calculate histogram H for src.
* 2. normalize histogram, so that the sum of histogram bins is 255.
* 3. compute integral of the histogram:
* H(i) = sum0≤j≤iH(j)
* 4. transform the image using H as a look-up table: dst(x,y)=H(src(x,y))
* The algorithm normalizes brightness and increases contrast of the image.
*
* <b>support single-channel 8bit image (grayscale) only.</b>
*/
VALUE
rb_equalize_hist(VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self)));
cvEqualizeHist(CVARR(self), CVARR(dest));
return dest;
}
/*
* call-seq:
* match_template(<i>template[,method = :sqdiff]</i>) -> cvmat(result)
*
* Compares template against overlapped image regions.
* <i>method</i> is specifies the way the template must be compared with image regions.
* <i>method</i> should be following symbol. (see CvMat::MATCH_TEMPLATE_METHOD 's key and value.)
*
* * :sqdiff
* R(x,y)=sumx',y'[T(x',y')-I(x+x',y+y')]2
* * :sqdiff_normed
* R(x,y)=sumx',y'[T(x',y')-I(x+x',y+y')]2/sqrt[sumx',y'T(x',y')2*sumx',y'I(x+x',y+y')2]
* * :ccorr
* R(x,y)=sumx',y'[T(x',y')*I(x+x',y+y')]
* * :ccorr_normed
* R(x,y)=sumx',y'[T(x',y')*I(x+x',y+y')]/sqrt[sumx',y'T(x',y')2*sumx',y'I(x+x',y+y')2]
* * :ccoeff
* R(x,y)=sumx',y'[T'(x',y')*I'(x+x',y+y')],
* where T'(x',y')=T(x',y') - 1/(w*h)*sumx",y"T(x",y")
* I'(x+x',y+y')=I(x+x',y+y') - 1/(w*h)*sumx",y"I(x+x",y+y")
* * :ccoeff_normed
* R(x,y)=sumx',y'[T'(x',y')*I'(x+x',y+y')]/sqrt[sumx',y'T'(x',y')2*sumx',y'I'(x+x',y+y')2]
*
* After the match_template finishes comparison, the best matches can be found as global
* minimums (:sqdiff*) or maximums(:ccorr* or :ccoeff*) using minmax function.
* In case of color image and template summation in both numerator and each sum in denominator
* is done over all the channels (and separate mean values are used for each channel).
*/
VALUE
rb_match_template(int argc, VALUE *argv, VALUE self)
{
VALUE templ, method, result;
rb_scan_args(argc, argv, "11", &templ, &method);
if (!(rb_obj_is_kind_of(templ, cCvMat::rb_class())))
rb_raise(rb_eTypeError, "argument 1 (template) should be %s.", rb_class2name(cCvMat::rb_class()));
if (cvGetElemType(CVARR(self)) != cvGetElemType(CVARR(templ)))
rb_raise(rb_eTypeError, "template should be same type of self.");
CvSize src_size = cvGetSize(CVARR(self)), template_size = cvGetSize(CVARR(self));
result = cCvMat::new_object(cvSize(src_size.width - template_size.width + 1,
src_size.height - template_size.height + 1),
CV_32FC1);
cvMatchTemplate(CVARR(self), CVARR(templ), CVARR(result), CVMETHOD("MATCH_TEMPLATE_METHOD", CV_TM_SQDIFF));
return result;
}
/*
* call-seq:
* match_shapes_i1(<i>object</i>) -> float
*
* Compares two shapes(self and object). <i>object</i> should be CvMat or CvContour.
*
* A ~ object1, B - object2):
* I1(A,B)=sumi=1..7abs(1/mAi - 1/mBi)
*/
VALUE
rb_match_shapes_i1(int argc, VALUE *argv, VALUE self)
{
VALUE object;
rb_scan_args(argc, argv, "10", &object);
if ((!(rb_obj_is_kind_of(object, cCvMat::rb_class()))) && (!(rb_obj_is_kind_of(object, cCvContour::rb_class()))))
rb_raise(rb_eTypeError, "argument 1 (shape) should be %s or %s", rb_class2name(cCvMat::rb_class()), rb_class2name(cCvContour::rb_class()));
return rb_float_new(cvMatchShapes(CVARR(self), CVARR(object), CV_CONTOURS_MATCH_I1));
}
/*
* call-seq:
* match_shapes_i2(<i>object</i>) -> float
*
* Compares two shapes(self and object). <i>object</i> should be CvMat or CvContour.
*
* A ~ object1, B - object2):
* I2(A,B)=sumi=1..7abs(mAi - mBi)
*/
VALUE
rb_match_shapes_i2(int argc, VALUE *argv, VALUE self)
{
VALUE object;
rb_scan_args(argc, argv, "10", &object);
if ((!(rb_obj_is_kind_of(object, cCvMat::rb_class()))) && (!(rb_obj_is_kind_of(object, cCvContour::rb_class()))))
rb_raise(rb_eTypeError, "argument 1 (shape) should be %s or %s", rb_class2name(cCvMat::rb_class()), rb_class2name(cCvContour::rb_class()));
return rb_float_new(cvMatchShapes(CVARR(self), CVARR(object), CV_CONTOURS_MATCH_I2));
}
/*
* call-seq:
* match_shapes_i3(<i>object</i>) -> float
*
* Compares two shapes(self and object). <i>object</i> should be CvMat or CvContour.
*
* A ~ object1, B - object2):
* I3(A,B)=sumi=1..7abs(mAi - mBi)/abs(mAi)
*/
VALUE
rb_match_shapes_i3(int argc, VALUE *argv, VALUE self)
{
VALUE object;
rb_scan_args(argc, argv, "10", &object);
if ((!(rb_obj_is_kind_of(object, cCvMat::rb_class()))) && (!(rb_obj_is_kind_of(object, cCvContour::rb_class()))))
rb_raise(rb_eTypeError, "argument 1 (shape) should be %s or %s", rb_class2name(cCvMat::rb_class()), rb_class2name(cCvContour::rb_class()));
return rb_float_new(cvMatchShapes(CVARR(self), CVARR(object), CV_CONTOURS_MATCH_I3));
}
/*
* call-seq:
* mean_shift(window, criteria) -> comp
*
* Implements CAMSHIFT object tracking algrorithm.
* First, it finds an object center using mean_shift and, after that,
* calculates the object size and orientation.
*/
VALUE
rb_mean_shift(VALUE self, VALUE window, VALUE criteria)
{
VALUE comp = cCvConnectedComp::new_object();
cvMeanShift(CVARR(self), VALUE_TO_CVRECT(window), VALUE_TO_CVTERMCRITERIA(criteria), CVCONNECTEDCOMP(comp));
return comp;
}
/*
* call-seq:
* cam_shift(window, criteria) -> [comp, box]
*
* Implements CAMSHIFT object tracking algrorithm. First, it finds an object center using cvMeanShift and,
* after that, calculates the object size and orientation. The function returns number of iterations made
* within cvMeanShift.
*/
VALUE
rb_cam_shift(VALUE self, VALUE window, VALUE criteria)
{
VALUE comp, box;
comp = cCvConnectedComp::new_object();
box = cCvBox2D::new_object();
cvCamShift(CVARR(self), VALUE_TO_CVRECT(window), VALUE_TO_CVTERMCRITERIA(criteria), CVCONNECTEDCOMP(comp), CVBOX2D(box));
return rb_ary_new3(2, comp, box);
}
/*
* call-seq:
* snake_image(<i>points, alpha, beta, gamma, window, criteria[, calc_gradient = true]</i>) -> cvseq(pointset)
*
* Updates snake in order to minimize its total energy that is a sum of internal energy
* that depends on contour shape (the smoother contour is, the smaller internal energy is)
* and external energy that depends on the energy field and reaches minimum at the local energy
* extremums that correspond to the image edges in case of image gradient.
* The parameter criteria.epsilon is used to define the minimal number of points that must be moved
* during any iteration to keep the iteration process running.
*
* If at some iteration the number of moved points is less than criteria.epsilon or
* the function performed criteria.max_iter iterations, the function terminates.
*
* points
* Contour points (snake).
* alpha
* Weight[s] of continuity energy, single float or array of length floats, one per each contour point.
* beta
* Weight[s] of curvature energy, similar to alpha.
* gamma
* Weight[s] of image energy, similar to alpha.
* window
* Size of neighborhood of every point used to search the minimum, both win.width and win.height must be odd.
* criteria
* Termination criteria.
* calc_gradient
* Gradient flag. If not 0, the function calculates gradient magnitude for every image pixel and consideres
* it as the energy field, otherwise the input image itself is considered.
*/
VALUE
rb_snake_image(int argc, VALUE *argv, VALUE self)
{
VALUE points, alpha, beta, gamma, window, criteria, calc_gradient, storage;
rb_scan_args(argc, argv, "43", &points, &alpha, &beta, &gamma, &window, &criteria, &calc_gradient);
CvPoint *pointset = 0;
CvSeq *seq = 0;
int length = CVPOINTS_FROM_POINT_SET(points, &pointset);
int coeff = (TYPE(alpha) == T_ARRAY || TYPE(beta) == T_ARRAY || TYPE(gamma) == T_ARRAY) ? CV_ARRAY : CV_VALUE;
float *a = 0, *b = 0, *c = 0;
IplImage stub;
if(coeff == CV_VALUE){
a = ALLOC(float);
a[0] = (float)NUM2DBL(alpha);
b = ALLOC(float);
b[0] = (float)NUM2DBL(beta);
c = ALLOC(float);
c[0] = (float)NUM2DBL(gamma);
}else{ // CV_ARRAY
rb_raise(rb_eNotImpError, "");
// todo
}
CvSize w = VALUE_TO_CVSIZE(window);
CvTermCriteria tc = VALUE_TO_CVTERMCRITERIA(criteria);
cvSnakeImage(cvGetImage(CVARR(self), &stub), pointset, length,
a, b, c, coeff, w, tc, IF_BOOL(calc_gradient, 1, 0, 1));
storage = cCvMemStorage::new_object();
seq = cvCreateSeq(CV_SEQ_POINT_SET, sizeof(CvSeq), sizeof(CvPoint), CVMEMSTORAGE(storage));
cvSeqPushMulti(seq, pointset, length);
return cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvPoint::rb_class(), storage);
}
/*
* call-seq:
* optical_flow_hs(<i>prev[,velx = nil][,vely = nil][,options]</i>) -> [cvmat, cvmat]
*
* Calculates optical flow for two images (previous -> self) using Horn & Schunck algorithm.
* Return horizontal component of the optical flow and vertical component of the optical flow.
* <i>prev</i> is previous image
* <i>velx</i> is previous velocity field of x-axis, and <i>vely</i> is previous velocity field of y-axis.
*
* options
* * :lambda -> should be Float (default is 0.0005)
* Lagrangian multiplier.
* * :criteria -> should be CvTermCriteria object (default is CvTermCriteria(1, 0.001))
* Criteria of termination of velocity computing.
* note: <i>option</i>'s default value is CvMat::OPTICAL_FLOW_HS_OPTION.
*
* sample code
* velx, vely = nil, nil
* while true
* current = capture.query
* velx, vely = current.optical_flow_hs(prev, velx, vely) if prev
* prev = current
* end
*/
VALUE
rb_optical_flow_hs(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE prev, velx, vely, options;
int use_previous = 0;
rb_scan_args(argc, argv, "13", &prev, &velx, &vely, &options);
options = OPTICAL_FLOW_HS_OPTION(options);
if (!rb_obj_is_kind_of(prev, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 1 (previous image) should be %s", rb_class2name(cCvMat::rb_class()));
if (NIL_P(velx) && NIL_P(vely)) {
velx = cCvMat::new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
vely = cCvMat::new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
} else {
if (rb_obj_is_kind_of(velx, cCvMat::rb_class()) && rb_obj_is_kind_of(vely, cCvMat::rb_class()))
use_previous = 1;
else
rb_raise(rb_eArgError, "Necessary to give both argument 2(previous velocity field x) and argument 3(previous velocity field y)");
}
cvCalcOpticalFlowHS(CVARR(prev), CVARR(self), use_previous, CVARR(velx), CVARR(vely), HS_LAMBDA(options), HS_CRITERIA(options));
return rb_ary_new3(2, velx, vely);
}
/*
* call-seq:
* optical_flow_lk(<i>prev, win_size</i>) -> [cvmat, cvmat]
*
* Calculates optical flow for two images (previous -> self) using Lucas & Kanade algorithm
* Return horizontal component of the optical flow and vertical component of the optical flow.
*
* <i>win_size</i> is size of the averaging window used for grouping pixels.
*/
VALUE
rb_optical_flow_lk(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE prev, win_size, velx, vely;
rb_scan_args(argc, argv, "20", &prev, &win_size);
if (!rb_obj_is_kind_of(prev, cCvMat::rb_class()))
rb_raise(rb_eTypeError, "argument 1 (previous image) should be %s", rb_class2name(cCvMat::rb_class()));
velx = cCvMat::new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
vely = cCvMat::new_object(cvGetSize(CVARR(self)), CV_MAKETYPE(CV_32F, 1));
cvCalcOpticalFlowLK(CVARR(prev), CVARR(self), VALUE_TO_CVSIZE(win_size), CVARR(velx), CVARR(vely));
return rb_ary_new3(2, velx, vely);
}
/*
* call-seq:
* optical_flow_bm(<i>prev[,velx = nil][,vely = nil][,option]</i>) -> [cvmat, cvmat]
*
* Calculates optical flow for two images (previous -> self) using block matching method.
* Return horizontal component of the optical flow and vertical component of the optical flow.
* <i>prev</i> is previous image.
* <i>velx</i> is previous velocity field of x-axis, and <i>vely</i> is previous velocity field of y-axis.
*
* options
* * :block_size -> should be CvSize (default is CvSize(4,4))
* Size of basic blocks that are compared.
* * :shift_size -> should be CvSize (default is CvSize(1,1))
* Block coordinate increments.
* * :max_range -> should be CvSize (default is CVSize(4,4))
* Size of the scanned neighborhood in pixels around block.
* note: <i>option</i>'s default value is CvMat::OPTICAL_FLOW_BM_OPTION.
*
* Velocity is computed for every block, but not for every pixel,
* so velocity image pixels correspond to input image blocks.
* input/output velocity field's size should be (self.width / block_size.width)x(self.height / block_size.height).
* e.g. image.size is 320x240 and block_size is 4x4, velocity field's size is 80x60.
*
*/
VALUE
rb_optical_flow_bm(int argc, VALUE *argv, VALUE self)
{
SUPPORT_8UC1_ONLY(self);
VALUE prev, velx, vely, options;
int use_previous = 0;
rb_scan_args(argc, argv, "13", &prev, &velx, &vely, &options);
options = OPTICAL_FLOW_BM_OPTION(options);
CvSize
image_size = cvGetSize(CVARR(self)),
block_size = BM_BLOCK_SIZE(options),
shift_size = BM_SHIFT_SIZE(options),
max_range = BM_MAX_RANGE(options),
velocity_size = cvSize(image_size.width / block_size.width, image_size.height / block_size.height);
if (NIL_P(velx) && NIL_P(vely)) {
velx = cCvMat::new_object(velocity_size, CV_MAKETYPE(CV_32F, 1));
vely = cCvMat::new_object(velocity_size, CV_MAKETYPE(CV_32F, 1));
} else {
if (rb_obj_is_kind_of(velx, cCvMat::rb_class()) && rb_obj_is_kind_of(vely, cCvMat::rb_class()))
use_previous = 1;
else
rb_raise(rb_eArgError, "Necessary to give both argument 2(previous velocity field x) and argument 3(previous velocity field y)");
}
cvCalcOpticalFlowBM(CVARR(prev), CVARR(self),
block_size, shift_size, max_range, use_previous,
CVARR(velx), CVARR(vely));
return rb_ary_new3(2, velx, vely);
}
/*
* call-seq:
* CvMat.find_fundamental_mat_7point(<i>points1, points2[,options = {}]</i>) -> fundamental_matrix(cvmat) or nil
*
* Calculates fundamental matrix from corresponding points, use for 7-point algorism. Return fundamental matrix(9x3).
* <i>points1</i> and <i>points2</i> should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix.
* <i>option</i> should be Hash include these keys.
* :with_status (true or false)
* If set true, return fundamental_matrix and status. [fundamental_matrix, status]
* Otherwise return fundamental matrix only(default).
*
* note: <i>option</i>'s default value is CvMat::FIND_FUNDAMENTAL_MAT_OPTION.
* note: 9x3 fundamental matrix means 3x3 three fundamental matrices.
*/
VALUE
rb_find_fundamental_mat_7point(int argc, VALUE *argv, VALUE klass)
{
VALUE points1, points2, option, fundamental_matrix, status;
int num = 0;
rb_scan_args(argc, argv, "21", &points1, &points2, &option);
option = FIND_FUNDAMENTAL_MAT_OPTION(option);
fundamental_matrix = cCvMat::new_object(9, 3, CV_32FC1);
if(FFM_WITH_STATUS(option)){
status = cCvMat::new_object(cvGetSize(CVARR(points1)), CV_8UC1);
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_7POINT, 0, 0, CVMAT(status));
return rb_ary_new3(2, fundamental_matrix, status);
}else{
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_7POINT, 0, 0, NULL);
return fundamental_matrix;
}
}
/*
* call-seq:
* CvMat.find_fundamental_mat_8point(<i>points1, points2[,options = {}]</i>) -> fundamental_matrix(cvmat) or nil
*
* Calculates fundamental matrix from corresponding points, use for 8-point algorism.
* <i>points1</i> and <i>points2</i> should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix.
* <i>option</i> should be Hash include these keys.
* :with_status (true or false)
* If set true, return fundamental_matrix and status. [fundamental_matrix, status]
* Otherwise return fundamental matrix only(default).
*
* note: <i>option</i>'s default value is CvMat::FIND_FUNDAMENTAL_MAT_OPTION.
*/
VALUE
rb_find_fundamental_mat_8point(int argc, VALUE *argv, VALUE klass)
{
VALUE points1, points2, option, fundamental_matrix, status;
int num = 0;
rb_scan_args(argc, argv, "21", &points1, &points2, &option);
option = FIND_FUNDAMENTAL_MAT_OPTION(option);
fundamental_matrix = cCvMat::new_object(3, 3, CV_32FC1);
if(FFM_WITH_STATUS(option)){
status = cCvMat::new_object(cvGetSize(CVARR(points1)), CV_8UC1);
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_8POINT, 0, 0, CVMAT(status));
return num == 0 ? Qnil : rb_ary_new3(2, fundamental_matrix, status);
}else{
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_8POINT, 0, 0, NULL);
return num == 0 ? Qnil : fundamental_matrix;
}
}
/*
* call-seq:
* CvMat.find_fundamental_mat_ransac(<i>points1, points2[,options = {}]</i>) -> fundamental_matrix(cvmat) or nil
*
* Calculates fundamental matrix from corresponding points, use for RANSAC algorism.
* <i>points1</i> and <i>points2</i> should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix.
* <i>option</i> should be Hash include these keys.
* :with_status (true or false)
* If set true, return fundamental_matrix and status. [fundamental_matrix, status]
* Otherwise return fundamental matrix only(default).
* :maximum_distance
* The maximum distance from point to epipolar line in pixels, beyond which the point is considered an outlier
* and is not used for computing the final fundamental matrix. Usually it is set to 0.5 or 1.0.
* :desirable_level
* It denotes the desirable level of confidence that the matrix is correct.
*
* note: <i>option</i>'s default value is CvMat::FIND_FUNDAMENTAL_MAT_OPTION.
*/
VALUE
rb_find_fundamental_mat_ransac(int argc, VALUE *argv, VALUE klass)
{
VALUE points1, points2, option, fundamental_matrix, status;
int num = 0;
rb_scan_args(argc, argv, "21", &points1, &points2, &option);
option = FIND_FUNDAMENTAL_MAT_OPTION(option);
fundamental_matrix = cCvMat::new_object(3, 3, CV_32FC1);
if(FFM_WITH_STATUS(option)){
status = cCvMat::new_object(cvGetSize(CVARR(points1)), CV_8UC1);
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_RANSAC, FFM_MAXIMUM_DISTANCE(option), FFM_DESIRABLE_LEVEL(option), CVMAT(status));
return num == 0 ? Qnil : rb_ary_new3(2, fundamental_matrix, status);
}else{
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_RANSAC, FFM_MAXIMUM_DISTANCE(option), FFM_DESIRABLE_LEVEL(option), NULL);
return num == 0 ? Qnil : fundamental_matrix;
}
}
/*
* call-seq:
* CvMat.find_fundamental_mat_lmeds(<i>points1, points2[,options = {}]</i>) -> fundamental_matrix(cvmat) or nil
*
* Calculates fundamental matrix from corresponding points, use for LMedS algorism.
* <i>points1</i> and <i>points2</i> should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix.
* <i>option</i> should be Hash include these keys.
* :with_status (true or false)
* If set true, return fundamental_matrix and status. [fundamental_matrix, status]
* Otherwise return fundamental matrix only(default).
* :maximum_distance
* The maximum distance from point to epipolar line in pixels, beyond which the point is considered an outlier
* and is not used for computing the final fundamental matrix. Usually it is set to 0.5 or 1.0.
* :desirable_level
* It denotes the desirable level of confidence that the matrix is correct.
*
* note: <i>option</i>'s default value is CvMat::FIND_FUNDAMENTAL_MAT_OPTION.
*/
VALUE
rb_find_fundamental_mat_lmeds(int argc, VALUE *argv, VALUE klass)
{
VALUE points1, points2, option, fundamental_matrix, status;
int num = 0;
rb_scan_args(argc, argv, "21", &points1, &points2, &option);
option = FIND_FUNDAMENTAL_MAT_OPTION(option);
fundamental_matrix = cCvMat::new_object(3, 3, CV_32FC1);
if(FFM_WITH_STATUS(option)){
status = cCvMat::new_object(cvGetSize(CVARR(points1)), CV_8UC1);
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_LMEDS, FFM_MAXIMUM_DISTANCE(option), FFM_DESIRABLE_LEVEL(option), CVMAT(status));
return num == 0 ? Qnil : rb_ary_new3(2, fundamental_matrix, status);
}else{
num = cvFindFundamentalMat(CVMAT(points1), CVMAT(points2), CVMAT(fundamental_matrix), CV_FM_LMEDS, FFM_MAXIMUM_DISTANCE(option), FFM_DESIRABLE_LEVEL(option), NULL);
return num == 0 ? Qnil : fundamental_matrix;
}
}
/*
* call-seq:
* CvMat.compute_correspond_epilines(<i>points, which_image, fundamental_matrix</i>) -> correspondent_lines(cvmat)
*
* For points in one image of stereo pair computes the corresponding epilines in the other image.
* Finds equation of a line that contains the corresponding point (i.e. projection of the same 3D point)
* in the other image. Each line is encoded by a vector of 3 elements l=[a,b,c]T, so that:
* lT*[x, y, 1]T=0,
* or
* a*x + b*y + c = 0
* From the fundamental matrix definition (see cvFindFundamentalMatrix discussion), line l2 for a point p1 in the first image (which_image=1) can be computed as:
* l2=F*p1
* and the line l1 for a point p2 in the second image (which_image=1) can be computed as:
* l1=FT*p2
* Line coefficients are defined up to a scale. They are normalized (a2+b2=1) are stored into correspondent_lines.
*/
VALUE
rb_compute_correspond_epilines(VALUE klass, VALUE points, VALUE which_image, VALUE fundamental_matrix)
{
VALUE correspondent_lines;
CvSize size = cvGetSize(CVARR(points));
int n;
if(size.width <= 3 && size.height >= 7)
n = size.height;
else if(size.height <= 3 && size.width >= 7)
n = size.width;
else
rb_raise(rb_eTypeError, "input points should 2xN, Nx2 or 3xN, Nx3 matrix(N >= 7).");
correspondent_lines = cCvMat::new_object(n, 3, CV_32F);
cvComputeCorrespondEpilines(CVMAT(points), FIX2INT(which_image), CVMAT(fundamental_matrix), CVMAT(correspondent_lines));
return correspondent_lines;
}
VALUE
new_object(int rows, int cols, int type)
{
return OPENCV_OBJECT(rb_klass, cvCreateMat(rows, cols, type));
}
VALUE
new_object(CvSize size, int type)
{
return OPENCV_OBJECT(rb_klass, cvCreateMat(size.height, size.width, type));
}
__NAMESPACE_END_OPENCV
__NAMESPACE_END_CVMAT