/************************************************************ 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", RUBY_METHOD_FUNC(rb_rotation), 3); rb_define_method(rb_klass, "warp_perspective", RUBY_METHOD_FUNC(rb_warp_perspective), -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_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_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_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), 0); 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(row, col[, depth = CV_8U][, channel = 3]) -> cvmat * * Create col * row matrix. Each element set 0. * * Each element possigle range is set by depth. Default is unsigned 8bit. * * Number of channel is set by channel. channel 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(filename[,iscolor = CV_LOAD_IMAGE_COLOR]) * * 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 # => */ 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 true if this matrix has parent object, otherwise false. */ 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(mat) -> mat * copy(val) -> 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(rect) -> cvmat * sub_rect(topleft, size) -> cvmat * sub_rect(x, y, width, height) -> cvmat * * Return parts of self as CvMat. * * p or x,y mean top-left coordinate. * size or width,height 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(n) * * The matrix is divided into n 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(n) * * The matrix is divided into n 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(n) -> Return row * row(n1, n2, ...) -> 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(n) -> Return column * col(n1, n2, ...) -> 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([val = 0]) -> cvmat * * Return one of array diagonals. * val 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(index) -> int * * Return number of dimension. * almost same as CvMat#dims[index]. * 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: * [idx1[,idx2]...] * * 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: * [idx1[,idx2]...] = value * * Set value of the particular array element to value. * value 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(value[, mask]) -> 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!(value[, mask]) -> 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(filename) -> 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([val = [1]]) -> cvmat * * Return initializes scaled identity matrix. * val 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!([val = [1]]) -> self * * Initialize scaled identity matrix. * val 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([:rows => num][, :channel => cn]) -> 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(mat) -> cvmat * * Tiled mat 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(mat1[,mat2][,mat3][,mat4]) -> 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(lookup_table) -> cvmat * * Return new matrix performed lookup-table transform. * * lookup_table should be CvMat that have 256 element (e.g. 1x256 matrix). * Otherwise, raise CvStatusBadArgument error. * * And lookup_table 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(:depth => nil, :scale => 1.0, :shift => 0.0) * * 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(:scale => 1.0, :shift => 0.0) * * 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(val[,mask]) -> cvmat * * Return new matrix computed per-element sum. * val should be CvMat or CvScalar. * If val is CvMat, it must have same type (depth and channel). * mask 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(val[,mask]) -> cvmat * * Return new matrix computed per-element difference. * val should be CvMat or CvScalar. * If val is CvMat, it must have same type (depth and channel). * mask 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(val[,scale = 1.0]) -> cvmat * * Return new matrix computed per-element product. * val should be CvMat or CvScalar. * If val 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(val[,shiftvec]) -> cvmat * Performs matrix multiplication * dst = src1 * src2 + shiftvec * val and shiftvec 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(val[,scale = 1.0]) -> cvmat * * Return new matrix computed per-element division. * val should be CvMat or CvScalar. * If val 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(val[,mask]) -> cvmat * * Return new matrix computed per-element bit-wise conjunction. * val should be CvMat or CvScalar. * If val 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(val[,mask]) -> cvmat * * Return new matrix computed per-element bit-wise disjunction. * val should be CvMat or CvScalar. * If val 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(val[,mask]) -> cvmat * * Return new matrix computed per-element bit-wise "exclusive or" operation. * val should be CvMat or CvScalar. * If val 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(val) -> 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(val) -> 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(val) -> 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(val) -> 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(val) -> 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(val) -> 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(min, max) -> cvmat * * Check that element lie between two object. * min and max 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(val) -> cvmat * * Calculate absolute difference between two. * val 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([mask]) -> 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(mask) -> [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([mask]) -> 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([mask]) -> [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(mat) -> float * * Calculates dot product of two arrays in Euclidian metrics. * mat 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(mat) -> cvmat * * Calculate cross product of two 3D vectors. * mat 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(transmat[,shiftvec]) -> 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(mat) -> cvmat * * Return performed perspective matrix transform of vector array. * mat 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(:order => :default or :inverse, :delta => nil or cvmat) * * 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. * * rectangular matrix only (CvMat#square? = true). */ VALUE rb_transpose_bang(VALUE self) { cvTranspose(CVARR(self), CVARR(self)); return self; } /* * call-seq: * det -> float * * Return determinant of matrix. * self should be single-channel and floating-point depth. */ VALUE rb_det(VALUE self) { return rb_float_new(cvDet(CVARR(self))); } /* * call-seq: * invert(inversion_method=:lu[,delta]) -> float * * Finds inverse or pseudo-inverse of matrix. * inversion_method 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. * * self 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(mat, inversion_method=:lu) * * Solves linear system or least-squares problem (the latter is possible with SVD method). * * inversion_method 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) * * 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!([eps = 0.0]) -> [eigen_vectors(cvmat), eigen_values(cvmat)] * * Computes eigenvalues and eigenvectors of symmetric matrix. * self should be symmetric square matrix. self 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 self 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 self, * 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(anyflags...) -> 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(anyflags...) -> 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(p1, p2[, drawing_option]) -> mat * * Return image is drawn a line segment connecting two points. * * drawing_option 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: drawing_option'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!(p1, p2[, drawing_option]) -> 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(p1, p2[, drawing_option]) -> mat * * Return image is drawn a rectangle with two opposite corners p1 and p2. * * drawing_options 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: drawing_option'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!(p1, p2[, drawing_option]) -> 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(center, radius[,drawing_option]) -> cvmat * * Return image is drawn a simple or filled circle with given center and radius. * * drawing_options 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: drawing_option'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!(center, radius[,drawing_option]) -> 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", ¢er, &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(center, axis, angle, start_angle, end_angle[,drawing_option]) -> mat * * Return image is drawn a simple or thick elliptic arc or fills an ellipse sector. * * drawing_options 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: drawing_option'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!(center, axis, angle, start_angle, end_angle[,drawing_option]) -> 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", ¢er, &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(box[, drawing_option]) -> 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. * * drawing_options 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: drawing_option'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!(box[, drawing_option]) -> 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(points[,drawing_option]) -> 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!(points[,drawing_option]) -> 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: drawing_option'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(points[,drawing_option]) -> 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. * * 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: drawing_option'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!(points[,drawing_option]) -> 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(points[,drawing_option]) -> mat * * Return image drawed a single or multiple polygonal curves. * * drawing_option 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: drawing_option'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!(points[,drawing_option]) -> 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(str, point, font[,color]) -> cvmat * * Return image is drawn text string. * font 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!(str, point ,font[,color]) -> 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(xorder,yorder[,aperture_size=3]) -> cvmat * * Calculates first, second, third or mixed image derivatives using extended Sobel operator. * self 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([aperture_size = 3]) -> cvmat * * Calculates Laplacian of the image. * self 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(thresh1,thresh2[,aperture_size = 3]) -> 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([aperture_size = 3]) -> cvmat * * Calculates feature map for corner detection. * aperture_size 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(block_size[,aperture_size]) -> cvmat * * For every pixel considers block_size x block_size 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(block_size[,aperture_size = 3]) -> 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(block_size[,aperture_size = 3][,k = 0.04]) -> 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() * * 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(quality_level, min_distance[, good_features_to_track_option]) * -> 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 * good_features_to_track_option 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: good_features_to_track_option'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 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(center[, size = self.size]) -> cvmat * * Retrieves pixel rectangle from image with sub-pixel accuracy. * Extracts pixels from self. * 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", ¢er, &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(map_matrix[, size = self.size]) -> cvmat * * Retrives pixel quadrangle from image with sub-pixel accuracy. * Extracts pixel from self 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(size[,interpolation = :linear]) -> cvmat * * Resize image. * interpolation 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 self resized image that it fits exactly to size. 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(map_matrix[,interpolation = :linear][,option = :fill_outliers][,fillval = 0]) -> 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.rotation(center,angle,scale) -> cvmat * * Create new affine matrix of 2D rotation (2x3 32bit floating-point matrix). * center is center of rotation (x, y). * angle is rotation angle in degrees. * Positive values mean counter-clockwise rotation * (the coordinate origin is assumed at top-left corner). * scale 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(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(map_matrix[,interpolation=:linear][,option =:fill_outliers][,fillval=0])) -> 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(mapx,mapy[,interpolation=:linear][,option=:fill_outliers][,fillval=0]) -> 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(center, magnitude, ) * * 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, ¢er, &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([element = nil, iteration = 1]) -> cvmat * * Create erodes image by using arbitrary structuring element. * element is structuring element used for erosion. * element should be IplConvKernel. If it is nil, a 3x3 rectangular structuring element is used. * iterations 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!([element = nil][,iteration = 1]) -> 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([element = nil][,iteration = 1]) -> cvmat * * Create dilates image by using arbitrary structuring element. * element is structuring element used for erosion. * element should be IplConvKernel. If it is nil, a 3x3 rectangular structuring element is used. * iterations 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!([element = nil][,iteration = 1]) -> 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; } /* * call-seq: * morpholohy_open([element = nil][,iteration = 1]) -> cvmat * * Performs advanced morphological transformations "Opening". * dilate(erode(src,element),element) */ VALUE rb_morphology_open(int argc, VALUE *argv, VALUE self) { VALUE element, iteration, dest; rb_scan_args(argc, argv, "02", &element, &iteration); dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvMorphologyEx(CVARR(self), CVARR(dest), 0, IPLCONVKERNEL(element), CV_MOP_OPEN, IF_INT(iteration, 1)); return dest; } /* * call-seq: * morpholohy_close([element = nil][,iteration = 1]) -> cvmat * * Performs advanced morphological transformations "Closing". * erode(dilate(src,element),element) */ VALUE rb_morphology_close(int argc, VALUE *argv, VALUE self) { VALUE element, iteration, dest; rb_scan_args(argc, argv, "02", &element, &iteration); dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvMorphologyEx(CVARR(self), CVARR(dest), 0, IPLCONVKERNEL(element), CV_MOP_CLOSE, IF_INT(iteration, 1)); return dest; } /* * call-seq: * morpholohy_gradient([element = nil][,iteration = 1]) -> 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, temp, dest; rb_scan_args(argc, argv, "02", &element, &iteration); temp = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvMorphologyEx(CVARR(self), CVARR(dest), CVARR(temp), IPLCONVKERNEL(element), CV_MOP_GRADIENT, IF_INT(iteration, 1)); return dest; } /* * call-seq: * morpholohy_tophat([element = nil][,iteration = 1]) -> cvmat * * Performs advanced morphological transformations "tophat". * src-open(src,element) */ VALUE rb_morphology_tophat(int argc, VALUE *argv, VALUE self) { VALUE element, iteration, dest; rb_scan_args(argc, argv, "02", &element, &iteration); dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvMorphologyEx(CVARR(self), CVARR(dest), 0, IPLCONVKERNEL(element), CV_MOP_TOPHAT, IF_INT(iteration, 1)); return dest; } /* * call-seq: * morpholohy_blackhat([element = nil][,iteration = 1]) -> 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); dest = new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvMorphologyEx(CVARR(self), CVARR(dest), 0, IPLCONVKERNEL(element), CV_MOP_BLACKHAT, IF_INT(iteration, 1)); return dest; } /* * call-seq: * smooth_blur_no_scale([p1 = 3, p2 = 3]) -> cvmat * * Smooths the image by simple blur with no scaling. * * 8bit unsigned -> return 16bit unsigned * * 8bit signed -> return 16bit signed * * 32bit floating point -> return 32bit floating point * support single-channel image only. */ 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_8S: dest_type = CV_16S; 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, 0)); return dest; } /* * call-seq: * smooth_blur([p1 = 3, p2 = 3]) -> cvmat * * Smooths the image by simple blur. * Summation over a pixel p1 x p2 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, 0)); return dest; } /* * call-seq: * smooth_gaussian([p1 = 3, p2 = 3, p3 = 0.0, p4 = 0.0]) -> cvmat * * Smooths the image by gaussian blur. * Convolving image with p1 x p2 Gaussian kernel. * * p3 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. * * p4 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, 0), IF_DBL(p3, 0.0), IF_DBL(p4, 0.0)); return dest; } /* * call-seq: * smooth_median([p1 = 3]) -> cvmat * * Smooths the image by median blur. * Finding median of p1 x p1 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([p1 = 3][p2 = 3]) -> cvmat * * Smooths the image by bilateral filter. * Applying bilateral 3x3 filtering with color sigma=p1 and space sigma=p2. */ 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, 0)); return dest; } /* * call-seq: * filter2d(kernel[,anchor]) -> 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(size, offset[,value = CvScalar.new(0)]) * * 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(size, offset) * * 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(need_sqsum = false, need_tilted_sum = false) -> [cvmat, cvmat or nil, cvmat or nil] * * Calculates integral images. * If need_sqsum = true, calculate the integral image for squared pixel values. * If need_tilted_sum = true, calculate the integral for the image rotated by 45 degrees. * * sum(X,Y)=sumxthreshold, max_value[,use_otsu = false]) * * 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, dest; rb_scan_args(argc, argv, "21", &threshold, &max_value, &use_otsu); dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvThreshold(CVARR(self), CVARR(dest), NUM2DBL(threshold), NUM2DBL(max_value), CV_THRESH_BINARY | (use_otsu == Qtrue ? CV_THRESH_OTSU : 0)); return dest; } /* * call-seq: * threshold_binary_inverse(threshold, max_value[,use_otsu = false]) * * 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, dest; rb_scan_args(argc, argv, "21", &threshold, &max_value, &use_otsu); dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvThreshold(CVARR(self), CVARR(dest), NUM2DBL(threshold), NUM2DBL(max_value), CV_THRESH_BINARY_INV | (use_otsu == Qtrue ? CV_THRESH_OTSU : 0)); return dest; } /* * call-seq: * threshold_trunc(threshold[,use_otsu = false]) * * 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, dest; rb_scan_args(argc, argv, "11", &threshold, &use_otsu); dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvThreshold(CVARR(self), CVARR(dest), NUM2DBL(threshold), 0, CV_THRESH_BINARY_INV | (use_otsu == Qtrue ? CV_THRESH_OTSU : 0)); return dest; } /* * call-seq: * threshold_to_zero(threshold[,use_otsu = false]) * * 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, dest; rb_scan_args(argc, argv, "11", &threshold, &use_otsu); dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvThreshold(CVARR(self), CVARR(dest), NUM2DBL(threshold), 0, CV_THRESH_TOZERO | (use_otsu == Qtrue ? CV_THRESH_OTSU : 0)); return dest; } /* * call-seq: * threshold_to_zero_inverse(threshold[,use_otsu = false]) * * 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, dest; rb_scan_args(argc, argv, "11", &threshold, &use_otsu); dest = cCvMat::new_object(cvGetSize(CVARR(self)), cvGetElemType(CVARR(self))); cvThreshold(CVARR(self), CVARR(dest), NUM2DBL(threshold), 0, CV_THRESH_TOZERO_INV | (use_otsu == Qtrue ? CV_THRESH_OTSU : 0)); return dest; } /* * call-seq: * pyr_down([filter = :gaussian_5x5]) -> 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([filter = :gaussian_5x5]) -> 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(seed_point, new_val, lo_diff, up_diff[,flood_fill_option]) -> [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: flood_fill_option'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!(seed_point, new_val, lo_diff, up_diff[,flood_fill_option]) -> [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(lo_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 chcontour or nil * * Finds contours in binary image, and return contours as CvContour or CvChain. * If contours not found, return nil. * * flood_fill_option 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: find_contours_option's default value is CvMat::FIND_CONTOURS_OPTION. * * support single-channel 8bit unsigned image only. * * 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 * * support single-channel 8bit unsigned image only. */ 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 = 0; 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, header_size, element_size; if (method == CV_CHAIN_CODE) { klass = cCvChain::rb_class(); element_klass = cCvChainCode::rb_class(); header = CV_SEQ_CHAIN_CONTOUR; header_size = sizeof(CvChain); element_size = sizeof(CvChainCode); } else { klass = cCvContour::rb_class(); element_klass = cCvPoint::rb_class(); header = CV_SEQ_CONTOUR; header_size = sizeof(CvContour); element_size = sizeof(CvPoint); } storage = cCvMemStorage::new_object(); if(cvFindContours(CVARR(self), CVMEMSTORAGE(storage), &contour, header, mode, method, FC_OFFSET(find_contours_option)) == 0) return Qnil; if(!contour) contour = cvCreateSeq(header, header_size, element_size, CVMEMSTORAGE(storage)); return cCvSeq::new_sequence(klass, contour, element_klass, storage); } /* * call-seq: * pyr_segmentation(level, threshold1, threshold2) -> [cvmat, cvseq(include cvconnectedcomp)] * * Does image segmentation by pyramids. * The pyramid builds up to the level level. * The links between any pixel a on leveli 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. * support single-channel or 3-channel 8bit unsigned image only */ 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); if (!(l >0)) rb_raise(rb_eArgError, "argument 1 (level) should be > 0."); if(((src->width | src->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(sp, sr[,max_level = 1][termcrit = CvTermCriteria.new(5,1)]) -> 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 = cCvMat::new_object(cvGetSize(CVARR(self)), CV_32SC1); cvZero(CVARR(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(rho, theta, threshold) -> 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(rho, theta, threshold, min_length, max_gap) -> 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(rho, theta, threshold, div_rho, div_theta) -> 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(dp, min_dist, threshold_canny, threshold_accumulate, min_radius = 0, max_radius = max(width,height)) -> 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(mask, radius) -> 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(mask, radius) -> 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. * * support single-channel 8bit image (grayscale) only. */ 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(template[,method = :sqdiff]) -> cvmat(result) * * Compares template against overlapped image regions. * method is specifies the way the template must be compared with image regions. * method 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(object) -> float * * Compares two shapes(self and object). object 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(object) -> float * * Compares two shapes(self and object). object 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(object) -> float * * Compares two shapes(self and object). object 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(points, alpha, beta, gamma, window, criteria[, calc_gradient = true]) -> 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(prev[,velx = nil][,vely = nil][,options]) -> [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. * prev is previous image * velx is previous velocity field of x-axis, and vely 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: option'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(prev, win_size) -> [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. * * win_size 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(prev[,velx = nil][,vely = nil][,option]) -> [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. * prev is previous image. * velx is previous velocity field of x-axis, and vely 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: option'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(points1, points2[,options = {}]) -> fundamental_matrix(cvmat) or nil * * Calculates fundamental matrix from corresponding points, use for 7-point algorism. Return fundamental matrix(9x3). * points1 and points2 should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix. * option 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: option'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(points1, points2[,options = {}]) -> fundamental_matrix(cvmat) or nil * * Calculates fundamental matrix from corresponding points, use for 8-point algorism. * points1 and points2 should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix. * option 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: option'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(points1, points2[,options = {}]) -> fundamental_matrix(cvmat) or nil * * Calculates fundamental matrix from corresponding points, use for RANSAC algorism. * points1 and points2 should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix. * option 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: option'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(points1, points2[,options = {}]) -> fundamental_matrix(cvmat) or nil * * Calculates fundamental matrix from corresponding points, use for LMedS algorism. * points1 and points2 should be 2x7 or 3x7 single-channel, or 1x7 multi-channel matrix. * option 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: option'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(points, which_image, fundamental_matrix) -> 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