PageRenderTime 94ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/src/libsead/seadlib.cpp

https://github.com/brigr/InFeRno
C++ | 720 lines | 477 code | 135 blank | 108 comment | 116 complexity | f7aa6cfc35bae4e91a1c77a49fadada5 MD5 | raw file
  1. /*
  2. * This file is part of InFeRno.
  3. *
  4. * Copyright (C) 2011:
  5. * Nikos Ntarmos <ntarmos@cs.uoi.gr>,
  6. * Sotirios Karavarsamis <s.karavarsamis@gmail.com>
  7. *
  8. * InFeRno is free software: you can redistribute it and/or modify it under
  9. * the terms of the GNU Lesser General Public License as published by the Free
  10. * Software Foundation, either version 3 of the License, or (at your option)
  11. * any later version.
  12. *
  13. * InFeRno is distributed in the hope that it will be useful, but WITHOUT ANY
  14. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15. * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
  16. * more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public License
  19. * along with InFeRno. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <cerrno>
  22. #include <cstdio>
  23. #include <stack>
  24. #include <stdexcept>
  25. #define CV_NO_BACKWARD_COMPATIBILITY
  26. #include <cv.h>
  27. #include <highgui.h>
  28. #include <ml.h>
  29. #include <gdk-pixbuf/gdk-pixbuf.h>
  30. #include <glib/gthread.h>
  31. #include "sead.h"
  32. #include "logger.h"
  33. #ifndef M_PI
  34. #define M_PI ((double)3.14159265358979323846)
  35. #endif
  36. using namespace std;
  37. typedef struct {
  38. int r;
  39. int g;
  40. int b;
  41. } Pixel_t;
  42. const int Sead::PARTITIONS_X = 4;
  43. const int Sead::PARTITIONS_Y = 4;
  44. const double Sead::alpha = 0.005;
  45. const double Sead::beta = 0.1;
  46. const double Sead::c = 0.3;
  47. const double Sead::rho = 0.1;
  48. Sead::Sead() : img(NULL) {}
  49. Sead::~Sead() { if (img) { cvReleaseImage(&img); img = NULL; } }
  50. IplImage* Sead::loadGIF(const string& file) {
  51. GdkPixbuf *pb;
  52. GError *gerror = NULL;
  53. IplImage *image;
  54. int width;
  55. int height;
  56. int rowstride;
  57. int chans;
  58. int x;
  59. int y;
  60. guchar *pixels;
  61. guchar *p;
  62. // check path
  63. if(file.empty()) {
  64. errno = EINVAL;
  65. return NULL;
  66. }
  67. // load gdk pixbuf from file
  68. pb = gdk_pixbuf_new_from_file(file.c_str(), &gerror);
  69. if(pb == NULL) {
  70. Logger::debug("GDK cannot load file %s: %s", file.c_str(), gerror->message);
  71. return NULL;
  72. }
  73. // get necessary file information
  74. width = gdk_pixbuf_get_width(pb);
  75. height = gdk_pixbuf_get_height(pb);
  76. rowstride = gdk_pixbuf_get_rowstride(pb);
  77. chans = gdk_pixbuf_get_n_channels(pb);
  78. pixels = gdk_pixbuf_get_pixels(pb);
  79. // create opencv image structure
  80. image = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, chans);
  81. if(image == NULL) {
  82. Logger::debug("cvCreateImage");
  83. g_object_unref(pb);
  84. return NULL;
  85. }
  86. for(x = 0; x < width; x++) {
  87. for(y = 0; y < height; y++) {
  88. // read channel values from pixbuf
  89. p = pixels + y * rowstride + x * chans;
  90. // copy RGB channel values to the IplImage structure
  91. cvSet2D(image, y, x, CV_RGB((int)p[0], (int)p[1], (int)p[2]));
  92. }
  93. }
  94. // free memory associated with image
  95. g_object_unref(pb);
  96. // return structure
  97. return(image);
  98. }
  99. int Sead::init(const string& path) {
  100. if (path.empty()) return -1;
  101. this->path = path;
  102. if (img)
  103. cvReleaseImage(&img);
  104. Logger::debug("Loading client-requested resource from %s...", path.c_str());
  105. if (
  106. !(img = cvLoadImage(path.c_str(), CV_LOAD_IMAGE_COLOR)) &&
  107. !(img = loadGIF(path))) {
  108. Logger::debug("Sead could not open/locate image %s on storage media", path.c_str());
  109. return -1;
  110. }
  111. return 0;
  112. }
  113. int Sead::doAdaptiveGammaCorrection(IplImage *img) {
  114. static const double chiM = 128.0; /* intensity midpoint */
  115. uchar *data = (uchar *)img->imageData;
  116. // invert the image
  117. for(int i = 0; i < img->height; i++) {
  118. for(int j = 0; j < img->width; j++) {
  119. for(int idx = 0; idx < 3; idx++) {
  120. double intensity = (double)data[i * img->widthStep + 3 * j + idx];
  121. double f1 = alpha * cos((M_PI * intensity) / (2.0 * chiM));
  122. double f2 = (rho * sin((4 * M_PI * intensity) / 255.0) + beta) * cos(alpha) + intensity * sin(alpha);
  123. double f3 = c * fabs((intensity / chiM) - 1.0) * cos((3.0 * M_PI * intensity) / 255.0);
  124. double gammax = 1.0 + f1 + f2 + f3;
  125. if(gammax <= 0)
  126. return -1;
  127. // update gamma corrected values
  128. data[i * img->widthStep + 3 * j + idx] = (uchar)255 * pow(intensity / 255.0, 1.0 / gammax);
  129. }
  130. }
  131. }
  132. return 0;
  133. }
  134. // image: binary skin filtered image, 3 channel image!
  135. // x, y: coordinates of point in binary image
  136. int Sead::Psi(const IplImage *image, int x, int y) {
  137. if(image == NULL)
  138. return -1;
  139. /* pixel pixel values of 4-neighborhood around pixel (x,y) */
  140. CvScalar p[] = {
  141. cvGet2D(image, y + 1, x + 0),
  142. cvGet2D(image, y + 0, x + 1),
  143. cvGet2D(image, y - 1, x + 0),
  144. cvGet2D(image, y + 0, x - 1)
  145. };
  146. // find skin pixels around 4-neighborhood
  147. int ksi = 0;
  148. for(int i = 0; i < 4; i++)
  149. ksi += (is_skin((int)p[i].val[2], (int)p[i].val[1], (int)p[i].val[0])); /* check if pixel is a skin pixel (white) */
  150. /* decide edge operator return value */
  151. return (ksi != 4);
  152. }
  153. // image: canny filtered image
  154. // x, y: coordinates
  155. int Sead::CAN(const IplImage *image, int x, int y) {
  156. if(image == NULL)
  157. return -1;
  158. /* get intensity value at pixel (x,y) */
  159. CvScalar sc = cvGet2D(image, y, x);
  160. /* check skiness in 4-neighborhood; check if pixel is white */
  161. return (sc.val[0] == 255);
  162. }
  163. int Sead::Edge(const IplImage *skin_binary_img, const IplImage *canny_edge_img, int x, int y) {
  164. return (Psi(skin_binary_img, x, y) + CAN(canny_edge_img, x, y) == 2);
  165. }
  166. void Sead::cvGetAreaPixels(IplImage *image, IplImage *blackboard, CvPoint init_point, CvScalar boundary_color, CvScalar fill_color, CvSeq *points, double& contour_nonskin_mean_r, double& contour_nonskin_mean_g, double& contour_nonskin_mean_b, double& skin_to_nonskin_ratio, unsigned long& contour_nonskin_total_pixels) {
  167. unsigned long contour_skin_total_pixels = 0;
  168. stack<CvPoint> neighbors;
  169. CvPoint pt;
  170. // mean R, G, B values for non-skin pixels
  171. contour_nonskin_mean_r = contour_nonskin_mean_g = contour_nonskin_mean_b = 0;
  172. contour_nonskin_total_pixels = 0;
  173. // push mean pixel to stack
  174. pt.x = init_point.x;
  175. pt.y = init_point.y;
  176. neighbors.push(pt);
  177. while (!neighbors.empty()) {
  178. // pop neighboring pixel
  179. CvPoint n = neighbors.top();
  180. neighbors.pop();
  181. // check if we are out of the image matrix bounds
  182. if(n.x < 0 || n.x >= image->width || n.y < 0 || n.y >= image->height)
  183. continue;
  184. // get color vector of this pixel
  185. CvScalar tmp = cvGet2D(blackboard, n.y - 1, n.x - 1);
  186. if ((tmp.val[0] != boundary_color.val[0] && tmp.val[1] != boundary_color.val[1] && tmp.val[2] != boundary_color.val[2]) ||
  187. (tmp.val[0] == fill_color.val[0] || tmp.val[1] == fill_color.val[1] || tmp.val[2] == fill_color.val[2]))
  188. continue;
  189. cvSet2D(blackboard, n.y - 1, n.x - 1, CV_RGB(255, 255, 255));
  190. // update statistics
  191. tmp = cvGet2D(image, n.y, n.x);
  192. if(is_skin((int) tmp.val[2], (int) tmp.val[1], (int) tmp.val[0])) {
  193. contour_skin_total_pixels++;
  194. } else {
  195. contour_nonskin_total_pixels++;
  196. /* update non-skin r, g, b mean */
  197. contour_nonskin_mean_r += (double)tmp.val[2];
  198. contour_nonskin_mean_g += (double)tmp.val[1];
  199. contour_nonskin_mean_b += (double)tmp.val[0];
  200. /* add non-skin pixel color components to the list of non-skin in-contour pixels */
  201. Pixel_t pix;
  202. pix.r = (int)tmp.val[2];
  203. pix.g = (int)tmp.val[1];
  204. pix.b = (int)tmp.val[0];
  205. cvSeqPush(points, &pix);
  206. }
  207. // push west
  208. pt.x = n.x - 1;
  209. pt.y = n.y;
  210. neighbors.push(pt);
  211. // push east
  212. pt.x = n.x + 1;
  213. pt.y = n.y;
  214. neighbors.push(pt);
  215. // push north
  216. pt.x = n.x;
  217. pt.y = n.y - 1;
  218. neighbors.push(pt);
  219. // push south
  220. pt.x = n.x;
  221. pt.y = n.y + 1;
  222. neighbors.push(pt);
  223. }
  224. /* compute mean of non skin pixels in contour area */
  225. contour_nonskin_mean_r /= (double) contour_nonskin_total_pixels;
  226. contour_nonskin_mean_g /= (double) contour_nonskin_total_pixels;
  227. contour_nonskin_mean_b /= (double) contour_nonskin_total_pixels;
  228. skin_to_nonskin_ratio = contour_skin_total_pixels / (double)contour_nonskin_total_pixels;
  229. }
  230. struct Sead::IplImageFeature *Sead::process() {
  231. IplImage *splitColorImage = NULL, *featureimg = NULL, *blackboard = NULL, *hu_img = NULL;
  232. CvMemStorage *mem_storage = NULL, *nsp_storage = NULL;
  233. CvSeq *hull = NULL, *mem_seq = NULL, *nsp_seq = NULL;
  234. bool returnNull = false;
  235. struct IplImageFeature *feature = NULL;
  236. struct IplImagePartition *partition = NULL;
  237. unsigned long contour_nonskin_total_pixels;
  238. double mean_x, mean_y;
  239. CvPoint mean_pt, main_pt;
  240. CvMoments moments;
  241. if (!img) {
  242. Logger::debug("Called process() on an uninitialized Sead instance");
  243. return NULL;
  244. }
  245. if (
  246. !(feature = new struct IplImageFeature) || // alloc + zero-out
  247. !(mem_storage = cvCreateMemStorage(0)) ||
  248. !(nsp_storage = cvCreateMemStorage(0)) ||
  249. !(mem_seq = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, sizeof(CvContour), sizeof(CvPoint), mem_storage)) ||
  250. !(nsp_seq = cvCreateSeq(CV_SEQ_KIND_GENERIC, sizeof(CvContour), sizeof(Pixel_t), nsp_storage)) ||
  251. !(featureimg = cvCloneImage(img)) ||
  252. !(splitColorImage = cvCloneImage(img)) ||
  253. !(hu_img = cvCreateImage(cvGetSize(img), img->depth, 1)) ||
  254. !(blackboard = cvCreateImage(cvGetSize(img), IPL_DEPTH_32F, 3))
  255. ) {
  256. returnNull = true;
  257. goto dealloc_memory;
  258. }
  259. memset(feature, 0, sizeof(struct IplImageFeature));
  260. /* compute Canny edge detection on image */
  261. cvCvtColor(img, hu_img, CV_BGR2GRAY);
  262. /* perform adaptive Gamma correction on image */
  263. //doAdaptiveGammaCorrection(img);
  264. //doAdaptiveGammaCorrection(splitColorImage);
  265. // draw a black filled rectangle over the whole image
  266. cvRectangle(blackboard, cvPoint(0, 0), cvPoint(img->width, img->height), CV_RGB(0, 0, 0), CV_FILLED, 8, 0);
  267. /* recursively partition the color image and locate skin-like regions */
  268. if (cvAdaptiveGrids(splitColorImage, 4, 4, 3, NULL, mem_seq, partition)) {
  269. Logger::error("There was an error in performing the recursive splitting or no skin regions in the image!");
  270. returnNull = true;
  271. goto dealloc_memory;
  272. }
  273. if(cvAdaptiveGridsHasFoundCells(partition) == 0) {
  274. returnNull = true;
  275. Logger::info("No connected skin regions found in image: image is benign!");
  276. goto dealloc_memory;
  277. }
  278. /* compute convex hull of detected skin-like regions */
  279. hull = cvConvexHull2(mem_seq, 0, CV_CLOCKWISE, 0 );
  280. if(hull == NULL) {
  281. returnNull = true;
  282. Logger::error("cvConvexHull2: returned NULL");
  283. goto dealloc_memory;
  284. }
  285. /* render perimeter of skin-region convex hull */
  286. main_pt = **CV_GET_SEQ_ELEM( CvPoint*, hull, hull->total - 1 );
  287. for (int i = 0; i < hull->total; i++) {
  288. /* get adjacent point in convex hull */
  289. CvPoint pt = **CV_GET_SEQ_ELEM( CvPoint*, hull, i );
  290. /* draw contour line on feature image */
  291. cvLine(featureimg, main_pt, pt, CV_RGB( 0, 255, 0), 3, 8, 0);
  292. /* draw contour line on blackboard image */
  293. cvLine(blackboard, main_pt, pt, CV_RGB( 0, 255, 0), 3, 8, 0);
  294. /* keep last point */
  295. main_pt = pt;
  296. }
  297. /* COMPUTE MEAN CONTROL POINT ON THE REFINED CONVEX HULL */
  298. /* ...first initialize coordinates of the mean point */
  299. mean_x = 0;
  300. mean_y = 0;
  301. /* ...compute nominator, also dividing by the total number of points
  302. * on the convex hull to get mean point */
  303. for (int i = 0; i < hull->total; i++) {
  304. CvPoint pt1 = **CV_GET_SEQ_ELEM(CvPoint *, hull, i);
  305. mean_x += pt1.x / (double)hull->total;
  306. mean_y += pt1.y / (double)hull->total;
  307. }
  308. mean_pt.x = (int)floor(mean_x);
  309. mean_pt.y = (int)floor(mean_y);
  310. /* get all internal points with respect to the focus-of-attention contour */
  311. cvGetAreaPixels(featureimg, blackboard, mean_pt, CV_RGB(0, 255, 0), CV_RGB(255, 255, 255), nsp_seq, feature->mean_r, feature->mean_g, feature->mean_b, feature->skin_to_non_skin_area, contour_nonskin_total_pixels);
  312. if(contour_nonskin_total_pixels == 0) {
  313. Logger::info("No nonskin pixels found!");
  314. returnNull = true;
  315. goto dealloc_memory;
  316. }
  317. cvMoments(hu_img, &moments);
  318. cvGetHuMoments(&moments, &feature->hu);
  319. /* compute variance of non-skin pixels inside the contour convex hull for R, G, B channels */
  320. for (int i = 0; i < nsp_seq->total; i++) {
  321. Pixel_t pix = *CV_GET_SEQ_ELEM(Pixel_t, nsp_seq, i);
  322. feature->cov_r += (pix.r - feature->mean_r) * (pix.r - feature->mean_r) / (double)nsp_seq->total;
  323. feature->cov_g += (pix.g - feature->mean_g) * (pix.g - feature->mean_g) / (double)nsp_seq->total;
  324. feature->cov_b += (pix.b - feature->mean_b) * (pix.b - feature->mean_b) / (double)nsp_seq->total;
  325. }
  326. dealloc_memory:
  327. /* release allocated space for images */
  328. if (hu_img)
  329. cvReleaseImage(&hu_img);
  330. if (blackboard)
  331. cvReleaseImage(&blackboard);
  332. if (featureimg)
  333. cvReleaseImage(&featureimg);
  334. if (splitColorImage)
  335. cvReleaseImage(&splitColorImage);
  336. /* release memory of storage trunks */
  337. if (mem_storage)
  338. cvReleaseMemStorage(&mem_storage);
  339. if (nsp_storage)
  340. cvReleaseMemStorage(&nsp_storage);
  341. if (feature && returnNull) {
  342. delete feature;
  343. feature = NULL;
  344. }
  345. return feature;
  346. }
  347. int Sead::blur() {
  348. if (!img)
  349. return -1;
  350. string dest = path + ".jpg";
  351. Logger::debug("Blurring image '%s' out!", path.c_str());
  352. cvSmooth(img, img, CV_BLUR, 20, 20);
  353. if (!cvSaveImage(dest.c_str(), img)) {
  354. Logger::error("cvSaveImage(%s)", dest.c_str());
  355. return -1;
  356. }
  357. if (rename(dest.c_str(), path.c_str())) {
  358. Logger::error("rename");
  359. return -1;
  360. }
  361. return 0;
  362. }
  363. int Sead::_min(int a, int b, int c) {
  364. int tmp = (a < b) ? a : b;
  365. return (tmp < c) ? tmp : c;
  366. }
  367. int Sead::_max(int a, int b, int c) {
  368. int tmp = (a > b) ? a : b;
  369. return (tmp > c) ? tmp : c;
  370. }
  371. int Sead::is_skin(int r, int g, int b) {
  372. // P. Peer, J. Kovac, and F. Solina. Human skin colour clustering
  373. // for face detection. In Proc. EUROCON - International Conference
  374. // on Computer as a Tool (2003).
  375. return ((r > 95 && g > 40 && b > 20) &&
  376. (_max(r, g, b) - _min(r, g, b) > 15) &&
  377. (abs(r-g) > 15) &&
  378. (r > g && r > b));
  379. }
  380. int Sead::cvAdaptiveGridsHasFoundCells(struct IplImagePartition *partition) {
  381. /* scan all cells and find potentially non-split region */
  382. for (int i = 0; i < partition->partitions_x; i++)
  383. for (int j = 0; j < partition->partitions_y; j++)
  384. if(partition->blocks[i][j].partitions_x > 0 || partition->blocks[i][j].partitions_y > 0)
  385. return 1;
  386. return 0;
  387. }
  388. int Sead::cvAdaptiveGrids(IplImage *image, int partitions_x, int partitions_y, int maxSplitLevel, struct IplImagePartition *cell, CvSeq *ctrl_points, struct IplImagePartition*& partition) {
  389. bool returnNull = false;
  390. int block_width; /* width in pixels for examined cell */
  391. int block_height; /* height in poixels for examined cell */
  392. unsigned long region_total_area; /* total area of cell */
  393. double region_mean_intensity; /* mean intensity of examined cell */
  394. double region_mean_skin_intensity; /* mean skin pixel intensity of examined cell */
  395. double region_variance_intensity; /* variance of intensity in examined cell */
  396. double region_variance_skin_intensity; /* variance of skin pixel intensity in examined cell */
  397. double region_kurtosis_total_area; /* 4-th order kurtosis of examined cell */
  398. double region_kurtosis_skin_area; /* 4-th order kurtosis of skin area in examined cell */
  399. double zetta1; /* skin area to total area ratio; decision factor for splitting criterion */
  400. double zetta2; /* kurtosis of overal image to kurtosis of skin area in cell; constraint in splitting criterion */
  401. /* if image is uninitialized return NULL pointer to partitioning structure */
  402. /* maxSplitLevel must be a positive integer */
  403. if (!image || !maxSplitLevel)
  404. return 0;
  405. /* allocate memory for partition table */
  406. if (!(partition = new struct IplImagePartition)) {
  407. Logger::warn("cvAdaptiveGrids: not enough memory for partition table allocation!");
  408. return -1;
  409. }
  410. memset(partition, 0, sizeof(struct IplImagePartition));
  411. /* initialize structure parameters */
  412. if (!cell) {
  413. partition->is_base_image = 1;
  414. partition->ref_x = partition->ref_y = 0;
  415. partition->height = image->height;
  416. partition->width = image->width;
  417. } else {
  418. partition->is_base_image = 0;
  419. partition->ref_x = cell->ref_x;
  420. partition->ref_y = cell->ref_y;
  421. partition->height = cell->height;
  422. partition->width = cell->width;
  423. }
  424. // if user wants 0 partitions over x (y), we be default split x (y)
  425. // in 4 partitions
  426. partition->partitions_x = (partitions_x) ? partitions_x : PARTITIONS_X;
  427. partition->partitions_y = (partitions_y) ? partitions_y : PARTITIONS_Y;
  428. // default: split level is set to 2
  429. partition->maxSplitLevel = (maxSplitLevel) ? maxSplitLevel : 2;
  430. partition->blocks = new struct IplImagePartition *[partition->partitions_x];
  431. if(partition->blocks == NULL) {
  432. Logger::warn("cvAdaptiveGrids: allocation error while trying to allocate image partition structures!");
  433. returnNull = true;
  434. goto cleanup_memory;
  435. }
  436. for (int i = 0; i < partition->partitions_x; i++) {
  437. partition->blocks[i] = new struct IplImagePartition[partition->partitions_y];
  438. if(partition->blocks[i] == NULL) {
  439. Logger::warn("cvAdaptiveGrids: allocation error while trying to allocate image partition structures!");
  440. returnNull = true;
  441. goto cleanup_memory;
  442. }
  443. memset(partition->blocks[i], 0, partition->partitions_y * sizeof(struct IplImagePartition));
  444. }
  445. /* allocate space for intensity histograms */
  446. partition->intensity_histograms = new struct IntensityHistogram *[partition->partitions_x];
  447. if(partition->intensity_histograms == NULL) {
  448. Logger::warn("cvAdaptiveGrids: allocation failed for region intensity histograms!");
  449. returnNull = true;
  450. goto cleanup_memory;
  451. }
  452. for (int i = 0; i < partition->partitions_x; i++) {
  453. partition->intensity_histograms[i] = new struct IntensityHistogram[partition->partitions_y];
  454. if(partition->intensity_histograms[i] == NULL) {
  455. Logger::warn("cvAdaptiveGrids: allocation failed for region intensity histograms!");
  456. returnNull = true;
  457. goto cleanup_memory;
  458. }
  459. memset(partition->intensity_histograms[i], 0, partition->partitions_y * sizeof(struct IntensityHistogram));
  460. }
  461. /* obtain width and height of local subregion */
  462. block_width = (int)floor(partition->width / (double)partition->partitions_x);
  463. block_height = (int)floor(partition->height / (double)partition->partitions_y);
  464. /* compute total area based on width and height of cell */
  465. region_total_area = block_width * block_height;
  466. /* do the partitioning */
  467. for (int i = 0; i < partition->partitions_x; i++) {
  468. /* obtain x-axis top left coordinate of sub-region to scan */
  469. int start_x = partition->ref_x + i * block_width;
  470. for (int j = 0; j < partition->partitions_y; j++) {
  471. /* obtain y-axis top left coordinate of sub-region to scan */
  472. int start_y = partition->ref_y + j * block_height;
  473. unsigned long region_skin_area = 0;
  474. /* scan all pixels in region and compute metrics */
  475. for (int coord_x = start_x; coord_x < start_x + block_width; coord_x++) {
  476. for (int coord_y = start_y; coord_y < start_y + block_height; coord_y++) {
  477. CvScalar tmp = cvGet2D(image, coord_y, coord_x);
  478. /* obtain color constraints at that point */
  479. int r = (int) tmp.val[2];
  480. int g = (int) tmp.val[1];
  481. int b = (int) tmp.val[0];
  482. int intensity = (int)floor(((double)(r + g + b)) / 3.0);
  483. /* increment intensity count at intensity I */
  484. partition->intensity_histograms[i][j].bins[intensity]++;
  485. region_skin_area += is_skin(r, g, b);
  486. }
  487. }
  488. if (!region_skin_area) {
  489. continue;
  490. }
  491. /* compute skin proportion decision factor */
  492. zetta1 = region_skin_area / (double)region_total_area;
  493. /* compute intensity mean */
  494. region_mean_intensity = region_mean_skin_intensity = 0.0;
  495. for (int k = 0; k < 256; k++) {
  496. region_mean_intensity += (double)k * (partition->intensity_histograms[i][j].bins[k] / (double)region_total_area);
  497. region_mean_skin_intensity += (double)k * (partition->intensity_histograms[i][j].bins[k] / (double)region_skin_area);
  498. }
  499. /* compute intensity variance and kurtosis */
  500. region_variance_intensity = region_variance_skin_intensity = 0.0;
  501. region_kurtosis_total_area = region_kurtosis_skin_area = 0.0;
  502. for (int k = 0; k < 256; k++) {
  503. region_variance_intensity += (k - region_mean_intensity) * (k - region_mean_intensity) * (partition->intensity_histograms[i][j].bins[k] / (double)region_total_area);
  504. region_variance_skin_intensity += (double)(k - region_mean_skin_intensity) * (k - region_mean_skin_intensity) * (partition->intensity_histograms[i][j].bins[k] / (double)region_skin_area);
  505. }
  506. if (!region_variance_intensity || !region_variance_skin_intensity) {
  507. // Zero variance means all values are in a single cell
  508. // (i.e., an impulse) so set the skin region kurtosis
  509. // would be infinite, and thus zetta2 also in. Thus, we
  510. // symbolically set it to 1.6, which is the uppper bound
  511. // for the selection criterion.
  512. zetta2 = 1.6;
  513. } else {
  514. double div_var = region_variance_intensity * region_variance_intensity, div_svar = region_variance_skin_intensity * region_variance_skin_intensity;
  515. for (int k = 0; k < 256; k++) {
  516. region_kurtosis_total_area += pow(k - region_mean_intensity, 4.0) * (partition->intensity_histograms[i][j].bins[k] / (double)region_total_area) / div_var;
  517. region_kurtosis_skin_area += pow(k - region_mean_skin_intensity, 4.0) * (partition->intensity_histograms[i][j].bins[k] / (double)region_skin_area) / div_svar;
  518. }
  519. /* compute skin to total kurtosis decision factor */
  520. zetta2 = (region_kurtosis_skin_area - 3.0) / (region_kurtosis_total_area - 3.0);
  521. }
  522. /* decide if region needs to be split further */
  523. if (zetta1 >= 0.35 && zetta2 >= 1.00) {
  524. /* draw green rectange signifying ROI */
  525. if(cell != NULL && !cell->is_base_image && cell->maxSplitLevel == 1) {
  526. CvPoint pt1, pt2;
  527. pt1.x = start_x;
  528. pt1.y = start_y;
  529. pt2.x = start_x + block_width;
  530. pt2.y = start_y + block_height;
  531. cvRectangle(image, pt1, pt2, CV_RGB(255, 0, 0), 1, 8, 0);
  532. cvSeqPush(ctrl_points, &pt1);
  533. cvSeqPush(ctrl_points, &pt2);
  534. pt2.x = pt1.x + block_width;
  535. pt2.y = pt1.y;
  536. cvSeqPush(ctrl_points, &pt2);
  537. pt2.x = pt1.x;
  538. pt2.y = pt1.y + block_height;
  539. cvSeqPush(ctrl_points, &pt2);
  540. }
  541. /* prepare terrain for next recursive splitting */
  542. partition->blocks[i][j].is_base_image = 0;
  543. partition->blocks[i][j].maxSplitLevel = maxSplitLevel - 1;
  544. /* number of segments to partition image into along x and y */
  545. partition->blocks[i][j].partitions_x = (partitions_x) ? partitions_x : PARTITIONS_X;
  546. partition->blocks[i][j].partitions_y = (partitions_y) ? partitions_y : PARTITIONS_Y;
  547. /* partitioned cell dimensions */
  548. partition->blocks[i][j].width = block_width;
  549. partition->blocks[i][j].height = block_height;
  550. /* relative start point of partitioned cell */
  551. partition->blocks[i][j].ref_x = start_x;
  552. partition->blocks[i][j].ref_y = start_y;
  553. /* recursively split cell */
  554. struct IplImagePartition* innerPartition = NULL;
  555. if (cvAdaptiveGrids(image, 4, 4, maxSplitLevel - 1, &partition->blocks[i][j], ctrl_points, innerPartition)) {
  556. returnNull = true;
  557. goto cleanup_memory;
  558. }
  559. if (innerPartition) {
  560. cvAdaptiveGridsRelease(innerPartition);
  561. }
  562. }
  563. }
  564. }
  565. cleanup_memory:
  566. if (returnNull && partition)
  567. cvAdaptiveGridsRelease(partition);
  568. return (returnNull) ? -1 : 0;
  569. }
  570. void Sead::cvAdaptiveGridsRelease(struct IplImagePartition*& partition) {
  571. if (!partition)
  572. return;
  573. if (partition->blocks) {
  574. for (int i = 0; i < partition->partitions_x && partition->blocks[i]; i++)
  575. cvAdaptiveGridsRelease(partition->blocks[i]);
  576. delete[] partition->blocks;
  577. }
  578. if (partition->intensity_histograms) {
  579. for (int i = 0; i < partition->partitions_x && partition->intensity_histograms[i]; i++)
  580. delete[] partition->intensity_histograms[i];
  581. delete[] partition->intensity_histograms;
  582. }
  583. delete partition;
  584. partition = NULL;
  585. }