PageRenderTime 90ms CodeModel.GetById 43ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/opencv/iplimage.cpp

https://github.com/jordoh/ruby-opencv
C++ | 576 lines | 394 code | 71 blank | 111 comment | 50 complexity | 5d5cb7b275aea4b3dcd283820d199bad MD5 | raw file
  1. /************************************************************
  2. iplimage.cpp -
  3. $Author: lsxi $
  4. Copyright (C) 2005-2006 Masakazu Yonekura
  5. ************************************************************/
  6. #include "iplimage.h"
  7. /*
  8. * Document-class: OpenCV::IplImage
  9. *
  10. * IPL(Intel Image Processing Library) Image class.
  11. *
  12. * IplImage is subclass of CvMat. IplImage support ROI(region of interest) and COI(color of interest).
  13. * Most of CvMat method support ROI, and some of CvMat method support COI.
  14. *
  15. * =What is ROI?
  16. * region of interest.
  17. *
  18. * =What is COI?
  19. * color of interest.
  20. */
  21. __NAMESPACE_BEGIN_OPENCV
  22. __NAMESPACE_BEGIN_IPLIMAGE
  23. VALUE rb_klass;
  24. VALUE
  25. rb_class()
  26. {
  27. return rb_klass;
  28. }
  29. void
  30. define_ruby_class()
  31. {
  32. if (rb_klass)
  33. return;
  34. /*
  35. * opencv = rb_define_module("OpenCV");
  36. * cvmat = rb_define_class_under(opencv, "CvMat", rb_cObject);
  37. *
  38. * note: this comment is used by rdoc.
  39. */
  40. VALUE opencv = rb_module_opencv();
  41. VALUE cvmat = cCvMat::rb_class();
  42. rb_klass = rb_define_class_under(opencv, "IplImage", cvmat);
  43. rb_define_alloc_func(rb_klass, rb_allocate);
  44. rb_define_singleton_method(rb_klass, "load", RUBY_METHOD_FUNC(rb_load_image), -1);
  45. rb_define_private_method(rb_klass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1);
  46. rb_define_method(rb_klass, "get_roi", RUBY_METHOD_FUNC(rb_get_roi), 0);
  47. rb_define_alias(rb_klass, "roi", "get_roi");
  48. rb_define_method(rb_klass, "set_roi", RUBY_METHOD_FUNC(rb_set_roi), 1);
  49. rb_define_alias(rb_klass, "roi=", "set_roi");
  50. rb_define_method(rb_klass, "reset_roi", RUBY_METHOD_FUNC(rb_reset_roi), 0);
  51. rb_define_method(rb_klass, "get_coi", RUBY_METHOD_FUNC(rb_get_coi), 0);
  52. rb_define_alias(rb_klass, "coi", "get_coi");
  53. rb_define_method(rb_klass, "set_coi", RUBY_METHOD_FUNC(rb_set_coi), 1);
  54. rb_define_alias(rb_klass, "coi=", "set_coi");
  55. rb_define_method(rb_klass, "reset_coi", RUBY_METHOD_FUNC(rb_reset_coi), 0);
  56. rb_define_method(rb_klass, "smoothness", RUBY_METHOD_FUNC(rb_smoothness), -1);
  57. }
  58. VALUE
  59. rb_allocate(VALUE klass)
  60. {
  61. return OPENCV_OBJECT(rb_klass, 0);
  62. }
  63. /*
  64. * call-seq:
  65. * new(<i>width, height[, depth = CV_8U][, channel = 3]</i>)
  66. *
  67. * Create width * height image. Each element-value set 0.
  68. *
  69. * Each element possigle range is set by <i>depth</i>. Default is unsigned 8bit.
  70. *
  71. * Number of channel is set by <i>channel</i>. <i>channel</i> should be 1..4.
  72. *
  73. * note: width = col, height = row, on CvMat. It is noted not to make a mistake
  74. * because the order of argument is differenct to CvMat.
  75. */
  76. VALUE
  77. rb_initialize(int argc, VALUE *argv, VALUE self)
  78. {
  79. VALUE width, height, depth, channel;
  80. rb_scan_args(argc, argv, "22", &width, &height, &depth, &channel);
  81. int _depth = CVMETHOD("DEPTH", depth, CV_8U);
  82. int _channel = argc < 4 ? 3 : NUM2INT(channel);
  83. DATA_PTR(self) = rb_cvCreateImage(cvSize(NUM2INT(width), NUM2INT(height)), cvIplDepth(_depth), _channel);
  84. return self;
  85. }
  86. /*
  87. * call-seq:
  88. * IplImage::load(<i>filename[,iscolor = CV_LOAD_IMAGE_COLOR]</i>)
  89. *
  90. * Load an image from file.
  91. * iscolor = CV_LOAD_IMAGE_COLOR, the loaded image is forced to be a 3-channel color image
  92. * iscolor = CV_LOAD_IMAGE_GRAYSCALE, the loaded image is forced to be grayscale
  93. * iscolor = CV_LOAD_IMAGE_UNCHANGED, the loaded image will be loaded as is.
  94. * Currently the following file format are supported.
  95. * * Windows bitmaps - BMP,DIB
  96. * * JPEG files - JPEG,JPG,JPE
  97. * * Portable Network Graphics - PNG
  98. * * Portable image format - PBM,PGM,PPM
  99. * * Sun rasters - SR,RAS
  100. * * TIFF files - TIFF,TIF
  101. */
  102. VALUE
  103. rb_load_image(int argc, VALUE *argv, VALUE self)
  104. {
  105. VALUE filename, iscolor;
  106. rb_scan_args(argc, argv, "11", &filename, &iscolor);
  107. Check_Type(filename, T_STRING);
  108. int _iscolor;
  109. if (TYPE(iscolor) == T_NIL) {
  110. _iscolor = CV_LOAD_IMAGE_COLOR;
  111. }
  112. else {
  113. Check_Type(iscolor, T_FIXNUM);
  114. _iscolor = FIX2INT(iscolor);
  115. }
  116. IplImage *image;
  117. if ((image = cvLoadImage(StringValueCStr(filename), _iscolor)) == NULL) {
  118. rb_raise(rb_eStandardError, "file does not exist or invalid format image.");
  119. }
  120. return OPENCV_OBJECT(rb_klass, image);
  121. }
  122. /*
  123. * Get ROI as CvRect.
  124. */
  125. VALUE
  126. rb_get_roi(VALUE self)
  127. {
  128. CvRect rect;
  129. try {
  130. rect = cvGetImageROI(IPLIMAGE(self));
  131. }
  132. catch (cv::Exception& e) {
  133. raise_cverror(e);
  134. }
  135. return cCvRect::new_object(rect);
  136. }
  137. /*
  138. * call-seq:
  139. * set_roi(<i>rect</i>)
  140. * set_roi(<i>rect</i>){|image| ...}
  141. *
  142. * Set ROI. <i>rect</i> should be CvRect or compatible object.
  143. * Return self.
  144. */
  145. VALUE
  146. rb_set_roi(VALUE self, VALUE roi)
  147. {
  148. VALUE block = rb_block_given_p() ? rb_block_proc() : 0;
  149. try {
  150. if (block) {
  151. CvRect prev_roi = cvGetImageROI(IPLIMAGE(self));
  152. cvSetImageROI(IPLIMAGE(self), VALUE_TO_CVRECT(roi));
  153. rb_yield_values(1, self);
  154. cvSetImageROI(IPLIMAGE(self), prev_roi);
  155. }
  156. else {
  157. cvSetImageROI(IPLIMAGE(self), VALUE_TO_CVRECT(roi));
  158. }
  159. }
  160. catch (cv::Exception& e) {
  161. raise_cverror(e);
  162. }
  163. return self;
  164. }
  165. /*
  166. * Reset ROI setting. Same as IplImage#roi = nil. Return self.
  167. */
  168. VALUE
  169. rb_reset_roi(VALUE self)
  170. {
  171. try {
  172. cvResetImageROI(IPLIMAGE(self));
  173. }
  174. catch (cv::Exception& e) {
  175. raise_cverror(e);
  176. }
  177. return self;
  178. }
  179. /*
  180. * Return COI as Fixnum.
  181. */
  182. VALUE
  183. rb_get_coi(VALUE self)
  184. {
  185. int coi = 0;
  186. try {
  187. coi = cvGetImageCOI(IPLIMAGE(self));
  188. }
  189. catch (cv::Exception& e) {
  190. raise_cverror(e);
  191. }
  192. return INT2FIX(coi);
  193. }
  194. /*
  195. * call-seq:
  196. * set_coi(<i>coi</i>)
  197. * set_coi(<i>coi</i>){|image| ...}
  198. *
  199. * Set COI. <i>coi</i> should be Fixnum.
  200. * Return self.
  201. */
  202. VALUE
  203. rb_set_coi(VALUE self, VALUE coi)
  204. {
  205. VALUE block = rb_block_given_p() ? rb_block_proc() : 0;
  206. try {
  207. if (block) {
  208. int prev_coi = cvGetImageCOI(IPLIMAGE(self));
  209. cvSetImageCOI(IPLIMAGE(self), NUM2INT(coi));
  210. rb_yield_values(1, self);
  211. cvSetImageCOI(IPLIMAGE(self), prev_coi);
  212. }
  213. else {
  214. cvSetImageCOI(IPLIMAGE(self), NUM2INT(coi));
  215. }
  216. }
  217. catch (cv::Exception& e) {
  218. raise_cverror(e);
  219. }
  220. return self;
  221. }
  222. /*
  223. * Reset COI setting. Same as IplImage#coi = 0. Return self.
  224. */
  225. VALUE
  226. rb_reset_coi(VALUE self)
  227. {
  228. try {
  229. cvSetImageCOI(IPLIMAGE(self), 0);
  230. }
  231. catch (cv::Exception& e) {
  232. raise_cverror(e);
  233. }
  234. return self;
  235. }
  236. /*
  237. * call-seq:
  238. * IplImage.smoothness(<i>lowFreqRatio, blankDensity, messyDensity, highFreqRatio</i>) -> [ symbol, float, float ]
  239. *
  240. * Determines if the image's smoothness is either, :smooth, :messy, or :blank.
  241. *
  242. * Original Author: yuhanz@gmail.com
  243. */
  244. VALUE
  245. rb_smoothness(int argc, VALUE *argv, VALUE self)
  246. {
  247. VALUE lowFreqRatio, blankDensity, messyDensity, highFreqRatio;
  248. rb_scan_args(argc, argv, "04", &lowFreqRatio, &blankDensity, &messyDensity, &highFreqRatio);
  249. double f_lowFreqRatio, f_blankDensity, f_messyDensity, f_highFreqRatio;
  250. double outLowDensity, outHighDensity;
  251. if (TYPE(lowFreqRatio) == T_NIL) {
  252. f_lowFreqRatio = 10 / 128.0f;
  253. }
  254. else {
  255. Check_Type(lowFreqRatio, T_FLOAT);
  256. f_lowFreqRatio = NUM2DBL(lowFreqRatio);
  257. }
  258. if (TYPE(blankDensity) == T_NIL) {
  259. f_blankDensity = 1.2f;
  260. }
  261. else {
  262. Check_Type(blankDensity, T_FLOAT);
  263. f_blankDensity = NUM2DBL(blankDensity);
  264. }
  265. if (TYPE(messyDensity) == T_NIL) {
  266. f_messyDensity = 0.151f;
  267. }
  268. else {
  269. Check_Type(messyDensity, T_FLOAT);
  270. f_messyDensity = NUM2DBL(messyDensity);
  271. }
  272. if (TYPE(highFreqRatio) == T_NIL) {
  273. f_highFreqRatio = 5 / 128.0f;
  274. }
  275. else {
  276. Check_Type(highFreqRatio, T_FLOAT);
  277. f_highFreqRatio = NUM2DBL(highFreqRatio);
  278. }
  279. IplImage *pFourierImage;
  280. IplImage *p64DepthImage;
  281. // the image is required to be in depth of 64
  282. if (IPLIMAGE(self)->depth == 64) {
  283. p64DepthImage = NULL;
  284. pFourierImage = create_fourier_image(IPLIMAGE(self));
  285. }
  286. else {
  287. p64DepthImage = rb_cvCreateImage(cvGetSize(IPLIMAGE(self)), IPL_DEPTH_64F, 1);
  288. cvConvertScale(CVARR(self), p64DepthImage, 1.0, 0.0);
  289. pFourierImage = create_fourier_image(p64DepthImage);
  290. }
  291. Smoothness result = compute_smoothness(pFourierImage, f_lowFreqRatio, f_blankDensity, f_messyDensity,
  292. f_highFreqRatio, outLowDensity, outHighDensity);
  293. cvReleaseImage(&pFourierImage);
  294. if (p64DepthImage != NULL)
  295. cvReleaseImage(&p64DepthImage);
  296. switch(result) {
  297. case SMOOTH:
  298. return rb_ary_new3(3, ID2SYM(rb_intern("smooth")), rb_float_new(outLowDensity), rb_float_new(outHighDensity));
  299. case MESSY:
  300. return rb_ary_new3(3, ID2SYM(rb_intern("messy")), rb_float_new(outLowDensity), rb_float_new(outHighDensity));
  301. case BLANK:
  302. return rb_ary_new3(3, ID2SYM(rb_intern("blank")), rb_float_new(outLowDensity), rb_float_new(outHighDensity));
  303. default:
  304. return rb_ary_new3(3, NULL, rb_float_new(outLowDensity), rb_float_new(outHighDensity));
  305. }
  306. }
  307. /**
  308. * Note: if lowDensity < blankDensityThreshold -> blank;
  309. * else if highDensity > messyDensityThreshold -> messy;
  310. * else -> good;
  311. */
  312. Smoothness
  313. compute_smoothness(const IplImage *pFourierImage, const double lowFreqRatio, const double blankDensity,
  314. const double messyDensity, const double highFreqRatio, double &outLowDensity,
  315. double &outHighDensity)
  316. {
  317. int low, high;
  318. IplImage *filteredFourierImage;
  319. int totalIntensity;
  320. double sum, den, totalArea;
  321. CvScalar scalar;
  322. if (!(pFourierImage->nChannels == 1 && pFourierImage->depth == 64) ) {
  323. cvError(CV_StsUnmatchedSizes, "compute_smoothness", "input image must contain only 1 channel and a depth of 64",
  324. __FILE__, __LINE__ );
  325. }
  326. high_pass_range(pFourierImage, lowFreqRatio, low, high );
  327. totalArea = M_PI * (high * high - low * low);
  328. filteredFourierImage = create_frequency_filtered_image(pFourierImage, low, high);
  329. scalar = cvSum(filteredFourierImage);
  330. totalIntensity = scalar.val[0];
  331. cvReleaseImage(&filteredFourierImage);
  332. outLowDensity = den = totalIntensity / totalArea;
  333. if (den <= blankDensity) {
  334. return BLANK;
  335. }
  336. low = (int)(high * (1.0 - highFreqRatio));
  337. filteredFourierImage = create_frequency_filtered_image(pFourierImage, low, high);
  338. scalar = cvSum(filteredFourierImage);
  339. totalIntensity = scalar.val[0];
  340. cvReleaseImage(&filteredFourierImage);
  341. outHighDensity = den = totalIntensity / totalArea;
  342. if (den >= messyDensity) {
  343. return MESSY;
  344. }
  345. return SMOOTH;
  346. }
  347. // Rearrange the quadrants of Fourier image so that the origin is at
  348. // the image center
  349. // src & dst arrays of equal size & type
  350. void
  351. cvShiftDFT(CvArr *src_arr, CvArr *dst_arr )
  352. {
  353. CvMat *tmp = NULL;
  354. CvMat q1stub, q2stub;
  355. CvMat q3stub, q4stub;
  356. CvMat d1stub, d2stub;
  357. CvMat d3stub, d4stub;
  358. CvMat *q1, *q2, *q3, *q4;
  359. CvMat *d1, *d2, *d3, *d4;
  360. CvSize size = cvGetSize(src_arr);
  361. CvSize dst_size = cvGetSize(dst_arr);
  362. int cx, cy;
  363. if (dst_size.width != size.width ||
  364. dst_size.height != size.height) {
  365. cvError( CV_StsUnmatchedSizes, "cvShiftDFT", "Source and Destination arrays must have equal sizes",
  366. __FILE__, __LINE__ );
  367. }
  368. if (src_arr == dst_arr) {
  369. tmp = rb_cvCreateMat(size.height / 2, size.width / 2, cvGetElemType(src_arr));
  370. }
  371. cx = size.width / 2;
  372. cy = size.height / 2; // image center
  373. q1 = cvGetSubRect(src_arr, &q1stub, cvRect(0,0,cx, cy));
  374. q2 = cvGetSubRect(src_arr, &q2stub, cvRect(cx,0,cx,cy));
  375. q3 = cvGetSubRect(src_arr, &q3stub, cvRect(cx,cy,cx,cy));
  376. q4 = cvGetSubRect(src_arr, &q4stub, cvRect(0,cy,cx,cy));
  377. d1 = cvGetSubRect(src_arr, &d1stub, cvRect(0,0,cx,cy));
  378. d2 = cvGetSubRect(src_arr, &d2stub, cvRect(cx,0,cx,cy));
  379. d3 = cvGetSubRect(src_arr, &d3stub, cvRect(cx,cy,cx,cy));
  380. d4 = cvGetSubRect(src_arr, &d4stub, cvRect(0,cy,cx,cy));
  381. if (src_arr != dst_arr) {
  382. if (!CV_ARE_TYPES_EQ(q1, d1)) {
  383. cvError(CV_StsUnmatchedFormats, "cvShiftDFT", "Source and Destination arrays must have the same format",
  384. __FILE__, __LINE__ );
  385. }
  386. cvCopy(q3, d1, 0);
  387. cvCopy(q4, d2, 0);
  388. cvCopy(q1, d3, 0);
  389. cvCopy(q2, d4, 0);
  390. }
  391. else {
  392. cvCopy(q3, tmp, 0);
  393. cvCopy(q1, q3, 0);
  394. cvCopy(tmp, q1, 0);
  395. cvCopy(q4, tmp, 0);
  396. cvCopy(q2, q4, 0);
  397. cvCopy(tmp, q2, 0);
  398. }
  399. if (tmp != NULL) {
  400. cvReleaseMat(&tmp);
  401. }
  402. }
  403. IplImage*
  404. create_fourier_image(const IplImage *im)
  405. {
  406. IplImage *realInput;
  407. IplImage *imaginaryInput;
  408. IplImage *complexInput;
  409. int dft_M, dft_N;
  410. CvMat *dft_A, tmp;
  411. IplImage *image_Re;
  412. IplImage *image_Im;
  413. realInput = rb_cvCreateImage(cvGetSize(im), IPL_DEPTH_64F, 1);
  414. imaginaryInput = rb_cvCreateImage(cvGetSize(im), IPL_DEPTH_64F, 1);
  415. complexInput = rb_cvCreateImage(cvGetSize(im), IPL_DEPTH_64F, 2);
  416. cvScale(im, realInput, 1.0, 0.0);
  417. cvZero(imaginaryInput);
  418. cvMerge(realInput, imaginaryInput, NULL, NULL, complexInput);
  419. dft_M = cvGetOptimalDFTSize(im->height - 1);
  420. dft_N = cvGetOptimalDFTSize(im->width - 1);
  421. dft_A = rb_cvCreateMat(dft_M, dft_N, CV_64FC2);
  422. image_Re = rb_cvCreateImage(cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
  423. image_Im = rb_cvCreateImage(cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
  424. // copy A to dft_A and pad dft_A with zeros
  425. cvGetSubRect(dft_A, &tmp, cvRect(0,0, im->width, im->height));
  426. cvCopy(complexInput, &tmp, NULL);
  427. if (dft_A->cols > im->width) {
  428. cvGetSubRect(dft_A, &tmp, cvRect(im->width,0, dft_A->cols - im->width, im->height));
  429. cvZero(&tmp);
  430. }
  431. // no need to pad bottom part of dft_A with zeros because of
  432. // use nonzero_rows parameter in cvDFT() call below
  433. cvDFT(dft_A, dft_A, CV_DXT_FORWARD, complexInput->height);
  434. // Split Fourier in real and imaginary parts
  435. cvSplit(dft_A, image_Re, image_Im, 0, 0);
  436. // Compute the magnitude of the spectrum Mag = sqrt(Re^2 + Im^2)
  437. cvPow(image_Re, image_Re, 2.0);
  438. cvPow(image_Im, image_Im, 2.0);
  439. cvAdd(image_Re, image_Im, image_Re, NULL);
  440. cvPow(image_Re, image_Re, 0.5);
  441. // Compute log(1 + Mag)
  442. cvAddS(image_Re, cvScalarAll(1.0), image_Re, NULL); // 1 + Mag
  443. cvLog(image_Re, image_Re); // log(1 + Mag)
  444. // Rearrange the quadrants of Fourier image so that the origin is at
  445. // the image center
  446. cvShiftDFT(image_Re, image_Re);
  447. cvReleaseImage(&realInput);
  448. cvReleaseImage(&imaginaryInput);
  449. cvReleaseImage(&complexInput);
  450. cvReleaseImage(&image_Im);
  451. cvReleaseMat(&dft_A);
  452. return image_Re;
  453. }
  454. IplImage*
  455. create_frequency_filtered_image(const IplImage *pImage, int low, int high)
  456. {
  457. CvPoint2D32f center;
  458. center.x = pImage->width / 2;
  459. center.y = pImage->height / 2;
  460. CvBox2D box;
  461. box.center = center;
  462. box.size.width = high;
  463. box.size.height = high;
  464. IplImage *pFilterMask = rb_cvCreateImage(cvGetSize(pImage), IPL_DEPTH_64F, 1);
  465. IplImage *pFiltered = rb_cvCreateImage(cvGetSize(pImage), IPL_DEPTH_64F, 1);
  466. cvZero(pFilterMask);
  467. cvZero(pFiltered);
  468. if (high > 0)
  469. cvEllipseBox(pFilterMask, box, cvScalar(255, 255, 255, 255), CV_FILLED, 8, 0);
  470. box.size.width = low;
  471. box.size.height = low;
  472. if (low > 0)
  473. cvEllipseBox(pFilterMask, box, cvScalar(0, 0, 0, 0), CV_FILLED, 8, 0);
  474. cvAnd(pImage, pFilterMask, pFiltered, NULL);
  475. cvReleaseImage(&pFilterMask);
  476. return pFiltered;
  477. }
  478. void
  479. high_pass_range(const IplImage *pImage, float lostPercentage, int &outLow, int &outHigh)
  480. {
  481. if (lostPercentage > 1.0f) {
  482. lostPercentage = 1;
  483. }
  484. else if (lostPercentage < 0.0f) {
  485. lostPercentage = 0;
  486. }
  487. outHigh = (int)MIN(pImage->width, pImage->height);
  488. outLow = (int)(lostPercentage * outHigh);
  489. }
  490. VALUE
  491. new_object(int width, int height, int type)
  492. {
  493. return OPENCV_OBJECT(rb_klass, rb_cvCreateImage(cvSize(width, height), cvIplDepth(type), CV_MAT_CN(type)));
  494. }
  495. VALUE
  496. new_object(CvSize size, int type)
  497. {
  498. return OPENCV_OBJECT(rb_klass, rb_cvCreateImage(size, cvIplDepth(type), CV_MAT_CN(type)));
  499. }
  500. __NAMESPACE_END_IPLIMAGE
  501. __NAMESPACE_END_OPENCV