#include "ruby_vips.h" /* * call-seq: * im.histgr([band]) -> image * * Find the histogram of *self*. If band is given, find the histogram * for that band (producing a one-band histogram). If band is not given, * find the histogram for all bands (producing an n-band histogram). * * *self* must be u8 or u16. The output image is always u32. */ VALUE img_histgr(int argc, VALUE *argv, VALUE obj) { VALUE v_bandno; int bandno; GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); rb_scan_args(argc, argv, "01", &v_bandno); bandno = NIL_P(v_bandno) ? -1 : NUM2INT(v_bandno); if (im_histgr(im, im_new, bandno)) vips_lib_error(); return new; } /* * call-seq: * im.histnd(bins) -> image * * Make a one, two or three dimensional histogram of a 1, 2 or 3 band image. * Divide each axis into a certain number of bins .. ie. output is 1 x bins, * bins x bins, or bins x bins x bins bands. uchar and ushort only. */ VALUE img_histnd(VALUE obj, VALUE bins) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_histnD(im, im_new, NUM2INT(bins))) vips_lib_error(); return new; } /* * call-seq: * im.hist_indexed(other_image) -> image * * Make a histogram of other_image, but use *self* to pick the bins. In * other words, element zero in the output image contains the sum of all the * pixels in other_image whose corresponding pixel in *self* is zero. * * *self* must have just one band and be u8 or u16. other_image must be * non-complex. The output image always has the same size and format as * other_image. * * This operation is useful in conjunction with Image#label_regions. You can * use it to find the centre of gravity of blobs in an image, for example. */ VALUE img_hist_indexed(VALUE obj, VALUE obj2) { RUBY_VIPS_BINARY(im_hist_indexed); } /* * call-seq: * Image.identity(bands) -> image * * Creates a image file with Xsize=256, Ysize=1, Bands=bands, * BandFmt= :UCHAR, Type=:HISTOGRAM. * * The created image consist of a bands-bands linear lut and is the * basis for building up look-up tables. */ VALUE img_s_identity(VALUE obj, VALUE bands) { OutPartial(new, data, im); if (im_identity(im, NUM2INT(bands))) vips_lib_error(); return new; } /* * call-seq: * Image.identity_ushort(size) -> image * * As Image.identity, but make a ushort LUT. ushort LUTs can be up to 65536 * elements - size is the number of elements required. * * The created image consist of a bands-bands linear lut and is the * basis for building up look-up tables. */ VALUE img_s_identity_ushort(VALUE obj, VALUE bands, VALUE sz) { OutPartial(new, data, im); if (im_identity_ushort(im, NUM2INT(bands), NUM2INT(sz))) vips_lib_error(); return new; } /* * call-seq: * Image.invertlut(input, lut_size) -> image * * Given a input of target values and real values, generate a LUT which * will map reals to targets. Handy for linearising images from measurements of * a colour chart. All values in [0,1]. Piecewise linear interpolation, * extrapolate head and tail to 0 and 1. * * Eg. input like this: * * input = [ * [0.2, 0.2, 0.3, 0.1], * [0.2, 0.4, 0.4, 0.2], * [0.7, 0.5, 0.6, 0.3] * ] * * Means a patch with 10% reflectance produces an image with 20% in channel 1, * 30% in channel 2, and 10% in channel 3, and so on. * * Inputs don't need to be sorted (we do that). Generate any precision LUT, * typically you might ask for 256 elements. * * It won't work too well for non-monotonic camera responses. * * input can be an array or a Mask object. */ VALUE img_s_invertlut(VALUE obj, VALUE input, VALUE lut_size) { DOUBLEMASK *dmask; OutPartial(new, data, im); mask_arg2mask(input, NULL, &dmask); if (im_invertlut(dmask, im, NUM2INT(lut_size))) vips_lib_error(); return new; } /* * call-seq: * Image.buildlut(input) -> image * * This operation builds a lookup table from a set of points. Intermediate * values are generated by piecewise linear interpolation. * * For example, consider this 2 x 2 matrix of (x, y) coordinates: * * input = [ * [ 0, 0], * [255, 100] * ] * im = Image.invertlut(input) * * We then generate an image with the following pixel values: * * im[0, 0] # => 0 * im[0, 1] # => 0.4 * # ... * im[0, 255] # => 100 * * This is then written as the output image, with the left column giving the * index in the image to place the value. * * The (x, y) points don't need to be sorted: we do that. You can have several * Ys, each becomes a band in the output LUT. You don't need to start at zero, * any integer will do, including negatives. */ VALUE img_s_buildlut(VALUE obj, VALUE input) { DOUBLEMASK *dmask; OutPartial(new, data, im); mask_arg2mask(input, NULL, &dmask); if (im_buildlut(dmask, im)) vips_lib_error(); return new; } /* * call-seq: * im.project -> image * * Find the horizontal and vertical projections of an image, ie. the sum * of every row of pixels, and the sum of every column of pixels. The output * format is uint, int or double, depending on the input format. * * Non-complex images only. */ VALUE img_project(VALUE obj) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); OutImg(obj, new2, data_new2, im_new2); if (im_project(im, im_new, im_new2)) vips_lib_error(); return rb_ary_new3(2, new, new2); } /* * call-seq: * im.histnorm -> image * * Normalise histogram ... normalise range to make it square (ie. max == * number of elements). Normalise each band separately. */ VALUE img_histnorm(VALUE obj) { RUBY_VIPS_UNARY(im_histnorm); } /* * call-seq: * im.histcum -> image * * Form cumulative histogram. */ VALUE img_histcum(VALUE obj) { RUBY_VIPS_UNARY(im_histcum); } /* * call-seq: * im.histeq -> image * * Histogram equalisation: normalised cumulative histogram. */ VALUE img_histeq(VALUE obj) { RUBY_VIPS_UNARY(im_histeq); } /* * call-seq: * im.histspec(other_image) -> image * * Creates a lut which, when applied to the image from which histogram *self* * was formed, will produce an image whose PDF matches that of the image from * which other_image was formed. */ VALUE img_histspec(VALUE obj, VALUE obj2) { RUBY_VIPS_BINARY(im_histspec); } /* * call-seq: * im.maplut(lut) -> image * * Map an image through another image acting as a LUT (Look Up Table). * The lut may have any type, and the output image will be that type. * * The input image will be cast to one of the unsigned integer types, that is, * band format :UCHAR, :USHORT or :UINT. * * If lut is too small for the input type (for example, if *self* is * band format :UCHAR but lut only has 100 elements), the lut is padded * out by copying the last element. Overflows are reported at the end of * computation. * * If lut is too large, extra values are ignored. * * If lut has one band, then all bands of *self* pass through it. If * lut has same number of bands as *self*, then each band is mapped * separately. If *self* has one band, then @lut may have many bands and the * output will have the same number of bands as lut. */ VALUE img_maplut(VALUE obj, VALUE obj2) { GetImg(obj, data, im); GetImg(obj2, data2, im2); OutImg2(obj, obj2, new, data_new, im_new); if (im_maplut(im, im_new, im2)) vips_lib_error(); return new; } /* * call-seq: * im.histplot -> image * * Plot a 1 by any or any by 1 image as a max by any or any by max image using * these rules: * * * unsigned char max is always 256 * * other unsigned integer types output 0 - maxium value of *self*. * * signed int types - min moved to 0, max moved to max + min. * * float types - min moved to 0, max moved to any (square output). */ VALUE img_histplot(VALUE obj) { RUBY_VIPS_UNARY(im_histplot); } /* * call-seq: * im.monotonic? -> true or false * * Test *self* for monotonicity. Returns true if *self* is monotonic. */ VALUE img_monotonic_p(VALUE obj) { int ret; GetImg(obj, data, im); if( im_ismonotonic(im, &ret) ) vips_lib_error(); return( ret == 0 ? Qfalse : Qtrue ); } /* * call-seq: * im.hist([band]) * * Find and plot the histogram of *self*. If band is not given, plot all * bands. Otherwise plot the specified band. */ VALUE img_hist(int argc, VALUE *argv, VALUE obj) { VALUE v_bandno; int bandno; GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); rb_scan_args(argc, argv, "01", &v_bandno); bandno = NIL_P(v_bandno) ? -1 : NUM2INT(v_bandno); if (im_hist(im, im_new, bandno)) vips_lib_error(); return new; } /* * call-seq: * im.hsp(other_image) -> image * * Maps *self* to the output image,, adjusting the histogram to match image * other_image. * * Both images should have the same number of bands. */ VALUE img_hsp(VALUE obj, VALUE obj2) { RUBY_VIPS_BINARY(im_hsp); } /* * call-seq: * im.gammacorrect(exponent) -> image * * Gamma-correct an 8- or 16-bit unsigned image with a lookup table. The * output format is the same as the input format. */ VALUE img_gammacorrect(VALUE obj, VALUE exponent) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_gammacorrect(im, im_new, NUM2DBL(exponent))) vips_lib_error(); return new; } /* * call-seq: * im.mpercent_hist(percent) -> number * * Just like Image#mpercent, except it works on an image histogram. Handy if * you want to run Image#mpercent several times without having to recompute the * histogram each time. */ VALUE img_mpercent_hist(VALUE obj, VALUE percent) { #if ATLEAST_VIPS( 7, 22 ) int ret; GetImg(obj, data, im); if (im_mpercent_hist(im, NUM2DBL(percent), &ret)) vips_lib_error(); return INT2NUM(ret); #else rb_raise(eVIPSError, "This operation is not supported by your version of VIPS"); #endif } /* * call-seq: * im.mpercent(percent) -> number * * Returns the threshold above which there are percent values of *self*. * If for example percent=.1, the number of pels of the input image with values * greater than the returned int will correspond to 10% of all pels of the * image. * * The function works for uchar and ushort images only. It can be used to * threshold the scaled result of a filtering operation. */ VALUE img_mpercent(VALUE obj, VALUE percent) { int ret; GetImg(obj, data, im); if (im_mpercent(im, NUM2DBL(percent), &ret)) vips_lib_error(); return INT2NUM(ret); } /* * call-seq: * im.heq([band]) -> image * * Histogram-equalise *self*. Equalise using band band, or if not given, * equalise all bands. */ VALUE img_heq(int argc, VALUE *argv, VALUE obj) { VALUE v_bandno; int bandno; GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); rb_scan_args(argc, argv, "01", &v_bandno); bandno = NIL_P(v_bandno) ? -1 : NUM2INT(v_bandno); if (im_heq(im, im_new, bandno)) vips_lib_error(); return new; } /* * call-seq: * im.lhisteq(xwin, ywin) -> image * * Performs local histogram equalisation on *self* using a window of size * xwin by ywin centered on the input pixel. Works only on * monochrome images. * * The output image is the same size as the input image. The edge pixels are * created by copy edge pixels of the input image outwards. */ VALUE img_lhisteq(VALUE obj, VALUE xwin, VALUE ywin) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_lhisteq(im, im_new, NUM2INT(xwin), NUM2INT(ywin))) vips_lib_error(); return new; } /* * call-seq: * im.stdif(a, m0, b, s, xwin, ywin) -> image * * Preforms statistical differencing according to the formula given in page 45 * of the book "An Introduction to Digital Image Processing" by Wayne Niblack. * This transformation emphasises the way in which a pel differs statistically * from its neighbours. It is useful for enhancing low-contrast images with * lots of detail, such as X-ray plates. * * At point (i,j) the output is given by the equation: * * vout(i,j) = a * m0 + (1 - a) * meanv + * (vin(i,j) - meanv) * (b * s0) / (s0 + b * stdv) * * Values a, m0, b and s0 are entered, while meanv * and stdv are the values calculated over a moving window of size xwin, * ywin centred on pixel (i,j). * * m0 is the new mean, a is the weight given to it. s0 is * the new standard deviation, b is the weight given to it. * * Try: * * im.stdif(0.5, 128, 0.5, 50, 11, 11) * * The operation works on one-band uchar images only, and writes a one-band * uchar image as its result. The output image has the same size as the * input. */ VALUE img_stdif(VALUE obj, VALUE a, VALUE m0, VALUE b, VALUE s0, VALUE xwin, VALUE ywin) { GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_stdif(im, im_new, NUM2DBL(a), NUM2DBL(m0), NUM2DBL(b), NUM2DBL(s0), NUM2INT(xwin), NUM2INT(ywin))) vips_lib_error(); return new; } /* * call-seq: * Image.tone_build_range(in_max, out_max, lb, lw, ps, pm, ph, s, m, h) -> image * * Generates a tone curve for the adjustment of image levels. It is mostly * designed for adjusting the L* part of a LAB image in way suitable for print * work, but you can use it for other things too. * * The curve is an unsigned 16-bit image with (in_max + 1) entries, each * in the range [0, out_max]. * * lb, lw are expressed as 0-100, as in LAB colour space. You * specify the scaling for the input and output images with the in_max * and out_max parameters. */ VALUE img_s_tone_build_range(VALUE obj, VALUE in_max, VALUE out_max, VALUE lb, VALUE lw, VALUE ps, VALUE pm, VALUE ph, VALUE s, VALUE m, VALUE h) { OutPartial(new, data, im); if (im_tone_build_range(im, NUM2DBL(in_max), NUM2DBL(out_max), NUM2DBL(lb), NUM2DBL(lw), NUM2DBL(ps), NUM2DBL(pm), NUM2DBL(ph), NUM2DBL(s), NUM2DBL(m), NUM2DBL(h))) vips_lib_error(); return new; } /* * call-seq: * Image.tone_build(lb, lw, ps, pm, ph, s, m, h) -> image * * As Image#tone_build_range, but set 32767 and 32767 as values for * in_max and out_max. This makes a curve suitable for correcting * LABS images, the most common case. */ VALUE img_s_tone_build(VALUE obj, VALUE lb, VALUE lw, VALUE ps, VALUE pm, VALUE ph, VALUE s, VALUE m, VALUE h) { OutPartial(new, data, im); if (im_tone_build(im, NUM2DBL(lb), NUM2DBL(lw), NUM2DBL(ps), NUM2DBL(pm), NUM2DBL(ph), NUM2DBL(s), NUM2DBL(m), NUM2DBL(h))) vips_lib_error(); return new; } /* * call-seq: * im.tone_analyse(ps, pm, ph, s, m, h) -> image * * As Image#tone_build, but analyse the histogram of *self* and use it to pick * the 0.1% and 99.9% points for lb and lw. */ VALUE img_tone_analyse(VALUE obj, VALUE ps, VALUE pm, VALUE ph, VALUE s, VALUE m, VALUE h) { #if ATLEAST_VIPS( 7, 23 ) GetImg(obj, data, im); OutImg(obj, new, data_new, im_new); if (im_tone_analyse(im, im_new, NUM2DBL(ps), NUM2DBL(pm), NUM2DBL(ph), NUM2DBL(s), NUM2DBL(m), NUM2DBL(h)) ) vips_lib_error(); return new; #else rb_raise(eVIPSError, "This operation is not supported by your version of VIPS"); #endif }