#include "ruby_vips.h" /* * call-seq: * im.dilate(mask) -> image * * Dilates *self*, according to mask. The output image is the same size * as the input. Sets pixels in the output if *any* part of the mask matches. * * *self* must be a one channel binary image ie. with pixels that are either 0 * (black) or 255 (white). This method assume that *self* contains white * objects against a black background. * * mask can be a two-dimensional array or a Mask object. All mask values * must be integers or this method will raise an exception. * * Mask coefficients can be either 0 (for object) or 255 (for background) or * 128 (for do not care). The mask should have odd length sides and the origin * of the mask is at location (mask_columns/2, mask_rows/2) integer division. * * Based on the book "Fundamentals of Digital Image Processing" by A. Jain, * pp 384-388, Prentice-Hall, 1989. */ VALUE img_dilate(VALUE obj, VALUE mask) { INTMASK *imask; GetImg(obj, data, im); OutImg2(obj, mask, new, data_new, im_new); mask_arg2mask(mask, &imask, NULL); if (im_dilate(im, im_new, imask)) vips_lib_error(); return new; } /* * call-seq: * im.erode(mask) -> image * * Erodes *self*, according to mask. The output image is the same size * as the input. Sets pixels in the output if *all* part of the mask matches. * * *self* must be a one channel binary image ie. with pixels that are either 0 * (black) or 255 (white). This method assume that *self* contains white * objects against a black background. * * mask can be a two-dimensional array or a Mask object. All mask values * must be integers or this method will raise an exception. * * Mask coefficients can be either 0 (for object) or 255 (for background) or * 128 (for do not care). The mask should have odd length sides and the origin * of the mask is at location (mask_columns/2, mask_rows/2) integer division. * * Based on the book "Fundamentals of Digital Image Processing" by A. Jain, * pp 384-388, Prentice-Hall, 1989. */ VALUE img_erode(VALUE obj, VALUE mask) { INTMASK *imask; GetImg(obj, data, im); OutImg2(obj, mask, new, data_new, im_new); mask_arg2mask(mask, &imask, NULL); if (im_erode(im, im_new, imask)) vips_lib_error(); return new; } /* * call-seq: * im.rank(xsize, ysize, n) -> image * * Does rank filtering on an image. A window of size xsize by * ysize is passed over the image. At each position, the pixels inside * the window are sorted into ascending order and the pixel at the nth * position is output. n numbers from 0. * * It works for any non-complex image type, with any number of bands. The input * is expanded by copying edge pixels before performing the operation so that * the output image has the same size as *self*. Edge pixels in the output * image are therefore only approximate. */ VALUE img_rank(VALUE obj, VALUE xsize, VALUE ysize, VALUE order) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_rank(im, im_new, NUM2INT(xsize), NUM2INT(ysize), NUM2INT(order))) vips_lib_error(); return new; } VALUE img_rank_image_internal(int argc, VALUE *argv, VALUE obj, int index) { vipsImg *im_t; IMAGE **ins; int i; GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); ins = IM_ARRAY(im_new, argc + 1, IMAGE*); ins[0] = im; for (i = 0; i < argc; i++) { img_add_dep(data_new, argv[i]); Data_Get_Struct(argv[i], vipsImg, im_t); ins[i + 1] = im_t->in; } if (im_rank_image(ins, im_new, argc + 1, index)) vips_lib_error(); return new; } /* * call-seq: * im.rank_image(index, other_image, ...) * * Sorts the input images pixel-wise, then outputs an image in which each pixel * is selected from the sorted list by index parameter. For example, if * index is zero, then each output pixel will be the minimum of all the * corresponding input pixels. * * It works for any uncoded, non-complex image type. All input images must * match in size, format, and number of bands. */ VALUE img_rank_image(int argc, VALUE *argv, VALUE obj) { VALUE index, *images; if (argc < 2) rb_raise(rb_eArgError, "Need an index and at least one image"); index = argv[0]; images = RARRAY_PTR(rb_ary_new4(argc - 1, argv + 1)); return img_rank_image_internal(argc - 1, images, obj, NUM2INT(index)); } /* * call-seq: * im.maxvalue(other_image, ...) -> image * * Sorts the input images pixel-wise, then outputs an image in which each pixel * is the maximum from the input pixels. * * It works for any uncoded, non-complex image type. All input images must * match in size, format, and number of bands. */ VALUE img_maxvalue(int argc, VALUE *argv, VALUE obj) { return img_rank_image_internal(argc, argv, obj, argc - 1); } static VALUE img_cntlines(VALUE obj, int flag) { double nolines; GetImg(obj, data, im); if (im_cntlines(im, &nolines, flag)) vips_lib_error(); return DBL2NUM(nolines); } /* * call-seq: * im.cntlines_h -> number * * Calculates the number of transitions between black and white for the * horizontal direction of an image. black is < 128, and white is >= 128. * * Returns the mean of the result. Input should be binary one channel. */ VALUE img_cntlines_h(VALUE obj) { return img_cntlines(obj, 0); } /* * call-seq: * im.cntlines_v -> number * * Calculates the number of transitions between black and white for the * vertical direction of an image. black is < 128, and white is >= 128. * * Returns the mean of the result. Input should be binary one channel. */ VALUE img_cntlines_v(VALUE obj) { return img_cntlines(obj, 1); } static VALUE img_zerox(VALUE obj, int flag) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_zerox(im, im_new, flag)) vips_lib_error(); return new; } /* * call-seq: * im.zerox_pos -> image * * Detects the +ve edges of zero crossings of *self*. Works on integer images. * The output image is byte with zero crossing set to 255 and all other values * set to zero. */ VALUE img_zerox_pos(VALUE obj) { return img_zerox(obj, 1); } /* * call-seq: * im.zerox_neg -> image * * Detects the -ve edges of zero crossings of *self*. Works on integer images. * The output image is byte with zero crossing set to 255 and all other values * set to zero. */ VALUE img_zerox_neg(VALUE obj) { return img_zerox(obj, -1); } static VALUE img_profile(VALUE obj, int dir) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_profile(im, im_new, dir)) vips_lib_error(); return new; } /* * call-seq: * im.profile_h -> image * * For each horizontal line, find the position of the first non-zero pixel from * the left. Output is USHORT with width = 1 and height = input height. */ VALUE img_profile_h(VALUE obj) { return img_profile(obj, 1); } /* * call-seq: * im.profile_v -> image * * For each vertical line, find the position of the first non-zero pixel from * the top. Output is USHORT with width = input width and height = 1. */ VALUE img_profile_v(VALUE obj) { return img_profile(obj, 0); } /* * call-seq: * im.label_regions -> image, segments * * *self* is repeatedly scanned and regions of 4-connected pixels with the same * pixel value found. Every time a region is discovered, those pixels are * marked in the output image with a unique serial number. Once all pixels have * been labelled, the operation returns, returning an an image and * segments, the number of discrete regions which were detected. * * The output image is always a 1-band image with band format :UINT, and of the * same dimensions as *self*. * * This operation is useful for, for example, blob counting. You can use the * morphological operators to detect and isolate a series of objects, then use * this method to number them all. * * Use Image#histindexed to (for example) find blob coordinates. */ VALUE img_label_regions(VALUE obj) { int segments; GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_label_regions(im, im_new, &segments)) vips_lib_error(); return rb_ary_new3(2, new, segments); }