/************************************************************ iplimage.cpp - $Author: lsxi $ Copyright (C) 2005-2006 Masakazu Yonekura ************************************************************/ #include "iplimage.h" /* * Document-class: OpenCV::IplImage * * IPL(Intel Image Processing Library) Image class. * * IplImage is subclass of CvMat. IplImage support ROI(region of interest) and COI(color of interest). * Most of CvMat method support ROI, and some of CvMat method support COI. * * =What is ROI? * region of interest. * * =What is COI? * color of interest. */ __NAMESPACE_BEGIN_OPENCV __NAMESPACE_BEGIN_IPLIMAGE VALUE rb_klass; VALUE rb_class() { return rb_klass; } void define_ruby_class() { if (rb_klass) return; /* * opencv = rb_define_module("OpenCV"); * cvmat = rb_define_class_under(opencv, "CvMat", rb_cObject); * * note: this comment is used by rdoc. */ VALUE opencv = rb_module_opencv(); VALUE cvmat = cCvMat::rb_class(); rb_klass = rb_define_class_under(opencv, "IplImage", cvmat); rb_define_alloc_func(rb_klass, rb_allocate); rb_define_singleton_method(rb_klass, "load", RUBY_METHOD_FUNC(rb_load_image), -1); rb_define_private_method(rb_klass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1); rb_define_method(rb_klass, "get_roi", RUBY_METHOD_FUNC(rb_get_roi), 0); rb_define_alias(rb_klass, "roi", "get_roi"); rb_define_method(rb_klass, "set_roi", RUBY_METHOD_FUNC(rb_set_roi), 1); rb_define_alias(rb_klass, "roi=", "set_roi"); rb_define_method(rb_klass, "reset_roi", RUBY_METHOD_FUNC(rb_reset_roi), 0); rb_define_method(rb_klass, "get_coi", RUBY_METHOD_FUNC(rb_get_coi), 0); rb_define_alias(rb_klass, "coi", "get_coi"); rb_define_method(rb_klass, "set_coi", RUBY_METHOD_FUNC(rb_set_coi), 1); rb_define_alias(rb_klass, "coi=", "set_coi"); rb_define_method(rb_klass, "reset_coi", RUBY_METHOD_FUNC(rb_reset_coi), 0); rb_define_method(rb_klass, "smoothness", RUBY_METHOD_FUNC(rb_smoothness), -1); } VALUE rb_allocate(VALUE klass) { return OPENCV_OBJECT(rb_klass, 0); } /* * call-seq: * new(width, height[, depth = CV_8U][, channel = 3]) * * Create width * height image. Each element-value 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. * * note: width = col, height = row, on CvMat. It is noted not to make a mistake * because the order of argument is differenct to CvMat. */ VALUE rb_initialize(int argc, VALUE *argv, VALUE self) { VALUE width, height, depth, channel; rb_scan_args(argc, argv, "22", &width, &height, &depth, &channel); int _depth = argc < 3 ? CV_8U : FIX2INT(depth); int _channel = argc < 4 ? 3 : FIX2INT(channel); DATA_PTR(self) = cvCreateImage(cvSize(FIX2INT(width), FIX2INT(height)), cvIplDepth(_depth), _channel); return self; } /* * call-seq: * IplImage::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_image(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); } IplImage *image; if ((image = cvLoadImage(StringValueCStr(filename), _iscolor)) == NULL) { rb_raise(rb_eStandardError, "file does not exist or invalid format image."); } return OPENCV_OBJECT(rb_klass, image); } /* * Get ROI as CvRect. */ VALUE rb_get_roi(VALUE self) { return cCvRect::new_object(cvGetImageROI(IPLIMAGE(self))); } /* * call-seq: * set_roi(rect) * set_roi(rect){|image| ...} * * Set ROI. rect should be CvRect or compatible object. * Return self. */ VALUE rb_set_roi(VALUE self, VALUE roi) { VALUE block = rb_block_given_p() ? rb_block_proc() : 0; if (block) { CvRect prev_roi = cvGetImageROI(IPLIMAGE(self)); cvSetImageROI(IPLIMAGE(self), VALUE_TO_CVRECT(roi)); rb_yield_values(1, self); cvSetImageROI(IPLIMAGE(self), prev_roi); } else { cvSetImageROI(IPLIMAGE(self), VALUE_TO_CVRECT(roi)); } return self; } /* * Reset ROI setting. Same as IplImage#roi = nil. Return self. */ VALUE rb_reset_roi(VALUE self) { cvResetImageROI(IPLIMAGE(self)); return self; } /* * Return COI as Fixnum. */ VALUE rb_get_coi(VALUE self) { return INT2FIX(cvGetImageCOI(IPLIMAGE(self))); } /* * call-seq: * set_coi(coi) * set_coi(coi){|image| ...} * * Set COI. coi should be Fixnum. * Return self. */ VALUE rb_set_coi(VALUE self, VALUE coi) { VALUE block = rb_block_given_p() ? rb_block_proc() : 0; if (block) { int prev_coi = cvGetImageCOI(IPLIMAGE(self)); cvSetImageCOI(IPLIMAGE(self), FIX2INT(coi)); rb_yield_values(1, self); cvSetImageCOI(IPLIMAGE(self), prev_coi); } else { cvSetImageCOI(IPLIMAGE(self), FIX2INT(coi)); } return self; } /* * Reset COI setting. Same as IplImage#coi = 0. Return self. */ VALUE rb_reset_coi(VALUE self) { cvSetImageCOI(IPLIMAGE(self), 0); return self; } /* * call-seq: * IplImage.smoothness(lowFreqRatio, blankDensity, messyDensity, highFreqRatio) -> [ symbol, float, float ] * * Determines if the image's smoothness is either, :smooth, :messy, or :blank. */ VALUE rb_smoothness(int argc, VALUE *argv, VALUE self) { VALUE lowFreqRatio, blankDensity, messyDensity, highFreqRatio; rb_scan_args(argc, argv, "04", &lowFreqRatio, &blankDensity, &messyDensity, &highFreqRatio); double f_lowFreqRatio, f_blankDensity, f_messyDensity, f_highFreqRatio; double outLowDensity, outHighDensity; if (TYPE(lowFreqRatio) == T_NIL) { f_lowFreqRatio = 10 / 128.0f; } else { Check_Type(lowFreqRatio, T_FLOAT); f_lowFreqRatio = NUM2DBL(lowFreqRatio); } if (TYPE(blankDensity) == T_NIL) { f_blankDensity = 1.2f; } else { Check_Type(blankDensity, T_FLOAT); f_blankDensity = NUM2DBL(blankDensity); } if (TYPE(messyDensity) == T_NIL) { f_messyDensity = 0.151f; } else { Check_Type(messyDensity, T_FLOAT); f_messyDensity = NUM2DBL(messyDensity); } if (TYPE(highFreqRatio) == T_NIL) { f_highFreqRatio = 5 / 128.0f; } else { Check_Type(highFreqRatio, T_FLOAT); f_highFreqRatio = NUM2DBL(highFreqRatio); } IplImage *pFourierImage; IplImage *p64DepthImage; // the image is required to be in depth of 64 if (IPLIMAGE(self)->depth == 64) { p64DepthImage = NULL; pFourierImage = create_fourier_image(IPLIMAGE(self)); } else { p64DepthImage = cvCreateImage(cvGetSize(IPLIMAGE(self)), IPL_DEPTH_64F, 1); cvConvertScale(CVARR(self), p64DepthImage, 1.0, 0.0); pFourierImage = create_fourier_image(p64DepthImage); } Smoothness result = compute_smoothness(pFourierImage, f_lowFreqRatio, f_blankDensity, f_messyDensity, f_highFreqRatio, outLowDensity, outHighDensity); cvReleaseImage(&pFourierImage); if (p64DepthImage != NULL) cvReleaseImage(&p64DepthImage); switch(result) { case SMOOTH: return rb_ary_new3(3, ID2SYM(rb_intern("smooth")), rb_float_new(outLowDensity), rb_float_new(outHighDensity)); case MESSY: return rb_ary_new3(3, ID2SYM(rb_intern("messy")), rb_float_new(outLowDensity), rb_float_new(outHighDensity)); case BLANK: return rb_ary_new3(3, ID2SYM(rb_intern("blank")), rb_float_new(outLowDensity), rb_float_new(outHighDensity)); default: return rb_ary_new3(3, NULL, rb_float_new(outLowDensity), rb_float_new(outHighDensity)); } } /** * Note: if lowDensity < blankDensityThreshold -> blank; * else if highDensity > messyDensityThreshold -> messy; * else -> good; */ Smoothness compute_smoothness(const IplImage *pFourierImage, const double lowFreqRatio, const double blankDensity, const double messyDensity, const double highFreqRatio, double &outLowDensity, double &outHighDensity) { int low, high; IplImage *filteredFourierImage; int totalIntensity; double sum, den, totalArea; CvScalar scalar; if(! (pFourierImage->nChannels == 1 && pFourierImage->depth == 64) ) { cvError( CV_StsUnmatchedSizes, "compute_smoothness", "input image must contain only 1 channel and a depth of 64", __FILE__, __LINE__ ); } high_pass_range(pFourierImage, lowFreqRatio, low, high ); totalArea = M_PI * (high * high - low * low); filteredFourierImage = create_frequency_filtered_image(pFourierImage, low, high); scalar = cvSum(filteredFourierImage); totalIntensity = scalar.val[0]; cvReleaseImage(&filteredFourierImage); outLowDensity = den = totalIntensity / totalArea; if(den <= blankDensity) { return BLANK; } low = (int) (high * (1.0 - highFreqRatio)); filteredFourierImage = create_frequency_filtered_image(pFourierImage, low, high); scalar = cvSum(filteredFourierImage); totalIntensity = scalar.val[0]; cvReleaseImage(&filteredFourierImage); outHighDensity = den = totalIntensity / totalArea; if(den >= messyDensity) { return MESSY; } return SMOOTH; } // Rearrange the quadrants of Fourier image so that the origin is at // the image center // src & dst arrays of equal size & type void cvShiftDFT(CvArr *src_arr, CvArr *dst_arr ) { CvMat *tmp = NULL; CvMat q1stub, q2stub; CvMat q3stub, q4stub; CvMat d1stub, d2stub; CvMat d3stub, d4stub; CvMat *q1, *q2, *q3, *q4; CvMat *d1, *d2, *d3, *d4; CvSize size = cvGetSize(src_arr); CvSize dst_size = cvGetSize(dst_arr); int cx, cy; if(dst_size.width != size.width || dst_size.height != size.height){ cvError( CV_StsUnmatchedSizes, "cvShiftDFT", "Source and Destination arrays must have equal sizes", __FILE__, __LINE__ ); } if(src_arr==dst_arr){ tmp = cvCreateMat(size.height/2, size.width/2, cvGetElemType(src_arr)); } cx = size.width/2; cy = size.height/2; // image center q1 = cvGetSubRect( src_arr, &q1stub, cvRect(0,0,cx, cy) ); q2 = cvGetSubRect( src_arr, &q2stub, cvRect(cx,0,cx,cy) ); q3 = cvGetSubRect( src_arr, &q3stub, cvRect(cx,cy,cx,cy) ); q4 = cvGetSubRect( src_arr, &q4stub, cvRect(0,cy,cx,cy) ); d1 = cvGetSubRect( src_arr, &d1stub, cvRect(0,0,cx,cy) ); d2 = cvGetSubRect( src_arr, &d2stub, cvRect(cx,0,cx,cy) ); d3 = cvGetSubRect( src_arr, &d3stub, cvRect(cx,cy,cx,cy) ); d4 = cvGetSubRect( src_arr, &d4stub, cvRect(0,cy,cx,cy) ); if(src_arr!=dst_arr){ if( !CV_ARE_TYPES_EQ( q1, d1 )){ cvError( CV_StsUnmatchedFormats, "cvShiftDFT", "Source and Destination arrays must have the same format", __FILE__, __LINE__ ); } cvCopy(q3, d1, 0); cvCopy(q4, d2, 0); cvCopy(q1, d3, 0); cvCopy(q2, d4, 0); } else{ cvCopy(q3, tmp, 0); cvCopy(q1, q3, 0); cvCopy(tmp, q1, 0); cvCopy(q4, tmp, 0); cvCopy(q2, q4, 0); cvCopy(tmp, q2, 0); } if (tmp != NULL) { cvReleaseMat(&tmp); } } IplImage* create_fourier_image(const IplImage *im) { IplImage *realInput; IplImage *imaginaryInput; IplImage *complexInput; int dft_M, dft_N; CvMat *dft_A, tmp; IplImage *image_Re; IplImage *image_Im; realInput = cvCreateImage( cvGetSize(im), IPL_DEPTH_64F, 1); imaginaryInput = cvCreateImage( cvGetSize(im), IPL_DEPTH_64F, 1); complexInput = cvCreateImage( cvGetSize(im), IPL_DEPTH_64F, 2); cvScale(im, realInput, 1.0, 0.0); cvZero(imaginaryInput); cvMerge(realInput, imaginaryInput, NULL, NULL, complexInput); dft_M = cvGetOptimalDFTSize( im->height - 1 ); dft_N = cvGetOptimalDFTSize( im->width - 1 ); dft_A = cvCreateMat( dft_M, dft_N, CV_64FC2 ); image_Re = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1); image_Im = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1); // copy A to dft_A and pad dft_A with zeros cvGetSubRect( dft_A, &tmp, cvRect(0,0, im->width, im->height)); cvCopy( complexInput, &tmp, NULL ); if( dft_A->cols > im->width ) { cvGetSubRect( dft_A, &tmp, cvRect(im->width,0, dft_A->cols - im->width, im->height)); cvZero( &tmp ); } // no need to pad bottom part of dft_A with zeros because of // use nonzero_rows parameter in cvDFT() call below cvDFT( dft_A, dft_A, CV_DXT_FORWARD, complexInput->height ); // Split Fourier in real and imaginary parts cvSplit( dft_A, image_Re, image_Im, 0, 0 ); // Compute the magnitude of the spectrum Mag = sqrt(Re^2 + Im^2) cvPow( image_Re, image_Re, 2.0); cvPow( image_Im, image_Im, 2.0); cvAdd( image_Re, image_Im, image_Re, NULL); cvPow( image_Re, image_Re, 0.5 ); // Compute log(1 + Mag) cvAddS( image_Re, cvScalarAll(1.0), image_Re, NULL ); // 1 + Mag cvLog( image_Re, image_Re ); // log(1 + Mag) // Rearrange the quadrants of Fourier image so that the origin is at // the image center cvShiftDFT( image_Re, image_Re ); cvReleaseImage(&realInput); cvReleaseImage(&imaginaryInput); cvReleaseImage(&complexInput); cvReleaseImage(&image_Im); cvReleaseMat(&dft_A); return image_Re; } IplImage* create_frequency_filtered_image(const IplImage *pImage, int low, int high) { CvPoint2D32f center; center.x = pImage->width / 2; center.y = pImage->height / 2; CvBox2D box; box.center = center; box.size.width = high; box.size.height = high; IplImage *pFilterMask = cvCreateImage( cvGetSize(pImage), IPL_DEPTH_64F, 1 ); IplImage *pFiltered = cvCreateImage( cvGetSize(pImage), IPL_DEPTH_64F, 1 ); cvZero(pFilterMask); cvZero(pFiltered); if(high > 0) cvEllipseBox(pFilterMask, box, cvScalar(255, 255, 255, 255), CV_FILLED, 8, 0); box.size.width = low; box.size.height = low; if(low > 0) cvEllipseBox(pFilterMask, box, cvScalar(0, 0, 0, 0), CV_FILLED, 8, 0); cvAnd(pImage, pFilterMask, pFiltered, NULL); cvReleaseImage(&pFilterMask); return pFiltered; } void high_pass_range(const IplImage *pImage, float lostPercentage, int &outLow, int &outHigh) { if(lostPercentage > 1.0f) { lostPercentage = 1; } else if(lostPercentage < 0.0f ) { lostPercentage = 0; } outHigh = (int) min( pImage->width, pImage->height ); outLow = (int) (lostPercentage * outHigh); } VALUE new_object(int width, int height, int type) { return OPENCV_OBJECT(rb_klass, cvCreateImage(cvSize(width, height), cvIplDepth(type), CV_MAT_CN(type))); } VALUE new_object(CvSize size, int type) { return OPENCV_OBJECT(rb_klass, cvCreateImage(size, cvIplDepth(type), CV_MAT_CN(type))); } __NAMESPACE_END_IPLIMAGE __NAMESPACE_END_OPENCV