[Wavelet-commit] Wavelet Image.cc, 1.24, 1.25 ImageResizer.cc, 1.1, 1.2
Status: Beta
Brought to you by:
herbert
From: Herbert M. D. <he...@us...> - 2007-08-10 17:55:54
|
Update of /cvsroot/wavelet/Wavelet In directory sc8-pr-cvs9.sourceforge.net:/tmp/cvs-serv6309 Modified Files: Image.cc ImageResizer.cc Log Message: Added a smart option to the ImageResizer, so that it can now do fully smart cropping. Index: ImageResizer.cc =================================================================== RCS file: /cvsroot/wavelet/Wavelet/ImageResizer.cc,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** ImageResizer.cc 7 Aug 2007 17:00:58 -0000 1.1 --- ImageResizer.cc 10 Aug 2007 17:55:45 -0000 1.2 *************** *** 25,33 **** ImageResizer::ImageResizer(const ColorImage &img, FilterSet &flt, ! int *fill, coeff threshold, int scalingStrategy) ! : m_image (img), m_resized (NULL), m_filter (flt), m_transform (NULL), ! m_qRows (0), m_qCols (0), m_factor (0), m_useRows (0), m_useCols (0), m_scalingStrategy (scalingStrategy), m_innerAvgPerSize (0), ! m_innerSDeviation (0), m_threshold (threshold) { if (fill == NULL) --- 25,36 ---- ImageResizer::ImageResizer(const ColorImage &img, FilterSet &flt, ! int *fill, coeff threshold, bool optimizeImage, ! int scalingStrategy) ! : m_image (img), m_maxDetail (NULL), m_filter (flt), ! m_qRows (0), m_qCols (0), m_scalingStrategy (scalingStrategy), m_innerAvgPerSize (0), ! m_innerSDeviation (0), m_innerRegionSize (0), m_threshold (threshold), ! m_optimizeImage (optimizeImage), m_optimizationCalculated (false), ! m_cropMaxRows (0), m_cropMaxCols (0), m_rowsMapping (0), m_colsMapping (0) { if (fill == NULL) *************** *** 48,93 **** ImageResizer::~ImageResizer(void) { - DELETENOTNULL (m_resized); DELETENOTNULLAR (m_fill); ! } ! ! void ! ImageResizer::calcDimensions (int rows, int cols) ! { ! /* find the best fit: we want either the rows or the cols to ! * fit exactly so that the remaining dimension can be cropped */ ! m_useRows = (int)floor (m_image.rows () * m_qCols + 0.5); ! m_useCols = (int)floor (m_image.cols () * m_qRows + 0.5); - if (m_useRows >= rows) - { - m_factor = m_qCols; - m_useCols = cols; - m_cropWhat = CROP_ROWS; - } - else - { - m_factor = m_qRows; - m_useRows = rows; - m_cropWhat = CROP_COLS; - } } ! // TODO: calculate image quality heuristics here! ! Image * ! ImageResizer::prepareImage (int rows, int cols, int steps) { - /* m_factor delivers us an image of same size or larger than - * specified by rows/cols */ - m_resized = m_image.scale (m_factor, m_scalingStrategy); - - assert (m_resized->rows() >= rows); - assert (m_resized->cols() >= cols); - Image *channel; ! ColorImage *tmp = m_resized->clone (); ! Image *maxDetail = NULL; int oldY = tmp->rows(); int oldX = tmp->cols(); /* perform a wavelet transform on all channels and from this calculate the --- 51,67 ---- ImageResizer::~ImageResizer(void) { DELETENOTNULLAR (m_fill); ! DELETENOTNULL (m_maxDetail); } ! void ! ImageResizer::genMaxDetail (int steps) { Image *channel; ! ColorImage *tmp = m_image.clone (); int oldY = tmp->rows(); int oldX = tmp->cols(); + WaveletTransform *transform = NULL; /* perform a wavelet transform on all channels and from this calculate the *************** *** 95,106 **** for (int i = 0; i < tmp->colors (); i++) { ! DELETENOTNULL (m_transform); ! m_transform = NEW (PyramidTransform (tmp->channel (i), m_filter)); ! m_transform->expandImage (); ! m_transform->analysis (steps); ! channel = m_transform->highMax (steps); if (i == 0) { ! maxDetail = channel->clone (); } else --- 69,80 ---- for (int i = 0; i < tmp->colors (); i++) { ! DELETENOTNULL (transform); ! transform = NEW (PyramidTransform (tmp->channel (i), m_filter)); ! transform->expandImage (); ! transform->analysis (steps); ! channel = transform->highMax (steps); if (i == 0) { ! m_maxDetail = channel->clone (); } else *************** *** 108,114 **** for (int j = 0; j < channel->size(); j++) { ! if (channel->at (j) > maxDetail->at (j)) { ! maxDetail->to (j, channel->at (j)); } } --- 82,88 ---- for (int j = 0; j < channel->size(); j++) { ! if (channel->at (j) > m_maxDetail->at (j)) { ! m_maxDetail->to (j, channel->at (j)); } } *************** *** 119,307 **** /* now restore the old image's ratio for the one produced from the max * coefficients in the detail areas */ ! maxDetail->resize(maxDetail->rows() * oldY / tmp->rows(), ! maxDetail->cols() * oldX / tmp->cols()); ! ! DELETE(tmp); ! DELETE (m_transform); ! return maxDetail; } void ! ImageResizer::doResizeImage(int rows, int cols, int nDiscardEach) { ! ColorImage *tmp = NULL; ! if (nDiscardEach > 0) { ! int fromY = m_cropWhat == CROP_COLS? 0: nDiscardEach; ! int fromX = m_cropWhat == CROP_ROWS? 0: nDiscardEach; ! int sizeY = m_cropWhat == CROP_COLS? ! m_resized->rows (): m_resized->rows () - 2 * nDiscardEach; ! int sizeX = m_cropWhat == CROP_ROWS? ! m_resized->cols (): m_resized->cols () - 2 * nDiscardEach; ! // TOOD this is far from optimal, but let's just see how it works first ! tmp = m_resized->crop (fromY, fromX, sizeY, sizeX); ! DELETE (m_resized); } else { ! tmp = m_resized; } - m_resized = tmp->fitInto (rows, cols, m_fill, m_scalingStrategy); - DELETE (tmp); } ! static coeff ! absVariance (int fromY, int fromX, int toY, int toX, Image &img, coeff avg) { ! int nVals = 0; ! int lower = (toY < 0)? img.rows (): toY; ! int right = (toX < 0)? img.cols (): toX; ! coeff ret = 0.0; ! coeff tmp; ! for (int y = (fromY < 0? 0: fromY); y < lower && y < img.rows (); y++) { ! for (int x = (fromX < 0? 0: fromX); x < right && x < img.cols (); x++) ! { ! ++nVals; ! tmp = fabs (img.at (y, x)); ! tmp -= avg; ! ret += SQUARE (tmp); ! } } ! return ret / nVals; } void ! ImageResizer::calcStats(Image &img, int nVecs, coeff &outerAvg, ! coeff &outerSDev, coeff &innerAvg, coeff &innerSDev) { ! // we're not calculating the *REAL* standard deviation here since we have ! // independent regions on which we calculate the averages which are used ! // for calculating the standard deviation, but what we get here will ! // normally come close enough to the real thing ! // OPTIMIZE ME: the calculation of the average is duplicate here ! coeff outerVariance; ! coeff innerVariance; ! if (m_cropWhat == CROP_COLS) ! { ! int top = 0; ! int bottom = -1; ! int firstLeft = 0; ! int firstRight = nVecs; ! int secondLeft = img.cols () - nVecs; ! int secondRight = -1; ! outerAvg = (img.aaverage (top, firstLeft, bottom, firstRight) ! + img.aaverage (top, secondLeft, bottom, secondRight)) / 2.0; ! outerVariance = (absVariance (top, firstLeft, bottom, firstRight, ! img, outerAvg) ! + absVariance (top, secondLeft, bottom, secondRight, ! img, outerAvg)) / 2; ! innerAvg = img.aaverage (top, firstRight, bottom, secondLeft); ! innerVariance = absVariance (top, firstRight, bottom, secondLeft, ! img, innerAvg); } ! else { ! int left = 0; ! int right = -1; ! int firstTop = 0; ! int firstBottom = nVecs; ! int secondTop = img.rows () - nVecs; ! int secondBottom = -1; ! outerAvg = (img.aaverage (firstTop, left, firstBottom, right) ! + img.aaverage (secondTop, left, secondBottom, right)) / 2.0; ! outerVariance = (absVariance (firstTop, left, firstBottom, right, ! img, outerAvg) ! + absVariance (secondTop, left, secondBottom, right, ! img, outerAvg)) / 2.0; ! innerAvg = img.aaverage (firstBottom, left, secondTop, right); ! innerVariance = absVariance (firstBottom, left, secondTop, right, ! img, innerAvg); } - outerSDev = sqrt (outerVariance); - innerSDev = sqrt (innerVariance); } ! ColorImage* ! ImageResizer::resize(int rows, int cols, int steps) { ! if (rows <= 0 || cols <= 0) { ! throw invalid_argument ("rows or columns arg is <= zero"); } ! m_qRows = (float)rows / m_image.rows(); ! m_qCols = (float)cols / m_image.cols(); ! m_factor = m_qRows; ! ! /* no dimension change necessary? do the job and go away */ ! if (fabs (m_qRows - m_qCols) < COEFF_EPSILON) { ! return m_image.scale (m_factor, m_scalingStrategy); } ! calcDimensions (rows, cols); ! ! Image *maxDetail = prepareImage (rows, cols, steps); coeff outerAvg; coeff outerSDev; coeff innerAvg; coeff innerSDev; - // maxDetail->rows () / maxDetali->cols () == ratio; double ratio = (double)rows / (double)cols; ! double mapping; int maxI = 0; if (m_cropWhat == CROP_COLS) { ! //std::cout << "cropping columns..." << std::endl; ! // ratio = maxDetail->rows () / (maxDetail->cols () - 2 * maxI) ! // ratio * (maxDetail->cols () - 2 * maxI) = maxDetail->rows () ! // maxDetail->cols () - 2 * maxI = maxDetail->rows () / ratio ! maxI = - (int)(maxDetail->rows () / ratio - maxDetail->cols () + 0.5); ! mapping = m_resized->cols () / maxDetail->cols (); ! assert (maxI <= maxDetail->cols ()); } else { ! //std::cout << "cropping rows..." << std::endl; ! // ratio = (maxDetail->rows () - 2 * maxI) / maxDetail->cols () ! // ratio * maxDetail->cols () = maxDetail->rows () - 2 * maxI ! maxI = - (int)(maxDetail->cols () * ratio - maxDetail->rows () + 0.5); ! mapping = m_resized->rows () / maxDetail->rows (); ! assert (maxI <= maxDetail->rows ()); } - maxI /= 2; - //std::cout << "maxI: " << maxI << ", ratio: " << ratio << std::endl; - - int nDiscardEach = 0; double outerByInner; - cout.setf (ios::fixed, ios::floatfield); - for (int i = 1; i < maxI && nDiscardEach == 0; i++) { ! calcStats(*maxDetail, i, outerAvg, outerSDev, innerAvg, innerSDev); outerByInner = outerAvg / innerAvg; ! cout.setf (ios::fixed, ios::floatfield); ! /*cout << "i: " << std::setw (2) << i << std::setw (2) << ", oa: " << outerAvg << std::setw (2) << ", os: " << outerSDev --- 93,344 ---- /* now restore the old image's ratio for the one produced from the max * coefficients in the detail areas */ ! m_maxDetail->resize(m_maxDetail->rows() * oldY / tmp->rows(), ! m_maxDetail->cols() * oldX / tmp->cols()); ! m_colsMapping = m_image.cols () / m_maxDetail->cols (); ! m_rowsMapping = m_image.rows () / m_maxDetail->rows (); ! DELETE (tmp); ! DELETENOTNULL (transform); } void ! ImageResizer::calcDimensions (int rows, int cols) { ! /* we determine the optimal size from the image, no factors are needed */ ! if (rows == 0 && cols == 0 && m_optimizeImage) { ! m_cropWhat = CROP_AUTOMATICALLY; ! return; ! } ! m_qRows = (float)rows / m_image.rows(); ! m_qCols = (float)cols / m_image.cols(); ! /* find the best fit: we want either the rows or the cols to ! * fit exactly so that the remaining dimension can be cropped */ ! if ((int)floor (m_image.rows () * m_qCols + 0.5) >= rows) ! { ! m_cropWhat = CROP_ROWS; ! } ! else if ((int)floor (m_image.cols () * m_qRows + 0.5) > cols) ! { ! m_cropWhat = CROP_COLS; } else { ! m_cropWhat = CROP_BOTH_OR_NONE; } } ! ColorImage* ! ImageResizer::doResizeImage(int rows, int cols, int discardRows, int discardCols) { ! ColorImage *tmp = NULL; ! if (discardRows > 0 || discardCols > 0) ! { ! int fromY = discardRows; ! int fromX = discardCols; ! int sizeY = m_image.rows () - 2 * discardRows; ! int sizeX = m_image.cols () - 2 * discardCols; ! ! /* TOOD this is far from optimal, but let's just see how it works first */ ! tmp = m_image.crop (fromY, fromX, sizeY, sizeX); ! } ! else { ! tmp = m_image.clone (); } ! ! if (m_cropWhat == CROP_AUTOMATICALLY) ! { ! return tmp; ! } ! ! ColorImage *result = tmp->fitInto (rows, cols, m_fill, m_scalingStrategy); ! DELETE (tmp); ! return result; } void ! ImageResizer::calcOuterStats (coeff &avg, coeff &variance, ! int firstTop, int firstLeft, ! int firstBottom, int firstRight, ! int secondTop, int secondLeft, ! int secondBottom, int secondRight) { ! avg = (m_maxDetail->aaverage (firstTop, firstLeft, ! firstBottom, firstRight) ! + m_maxDetail->aaverage (secondTop, secondLeft, ! secondBottom, secondRight)) / 2.0; ! variance = (m_maxDetail->variance (firstTop, firstLeft, ! firstBottom, firstRight, ! avg, true) ! + m_maxDetail->variance (secondTop, secondLeft, ! secondBottom, secondRight, ! avg, true)) / 2; ! } ! void ! ImageResizer::calcInnerStats (coeff &avg, coeff &sDev, ! int top, int left, int bottom, int right) ! { ! avg = m_maxDetail->aaverage (top, left, bottom, right); ! sDev = sqrt (m_maxDetail->variance (top, left, bottom, right, avg, true)); ! m_innerRegionSize = (m_maxDetail->rows () - 2 * top) ! * (m_maxDetail->cols () - 2 * left); ! } ! void ! ImageResizer::calcStats(int nRows, int nCols, coeff &outerAvg, ! coeff &outerSDev, coeff &innerAvg, coeff &innerSDev) ! { ! coeff outerVariance = 0; ! int valsOuterCols = 0; ! int secondTop = m_maxDetail->rows () - nRows; ! int secondLeft = m_maxDetail->cols () - nCols; ! int secondBottom = m_maxDetail->rows (); ! int secondRight = m_maxDetail->cols (); ! if (nCols > 0) ! { ! calcOuterStats (outerAvg, outerVariance, nRows, 0, secondTop, nCols, ! nRows, secondLeft, secondTop, secondRight); ! valsOuterCols = (secondTop - nRows) * nCols; } ! else if (nRows > 0) { ! calcOuterStats (outerAvg, outerVariance, 0, nCols, nRows, secondLeft, ! secondTop, nCols, secondBottom, secondRight); ! valsOuterCols = (nRows - secondTop) * (secondLeft - nCols); ! } ! else ! { ! throw invalid_argument ("can operate only one one out of rows/cols"); ! } ! calcInnerStats (innerAvg, innerSDev, nRows, nCols, secondTop, secondLeft); ! outerSDev = sqrt (outerVariance); ! /* we calculate this at the end of the image optimization if this is set */ ! if (!m_optimizeImage) ! { ! m_innerSDeviation = innerSDev; ! m_innerAvgPerSize = innerAvg / m_innerRegionSize; } } ! void ! ImageResizer::calcOptimization (void) { ! coeff outerAvg; ! coeff outerSDev; ! coeff innerAvg; ! coeff innerSDev; ! double outerByInner; ! int max = m_image.rows () / 2; ! ! for (int row = 1; row < max && m_cropMaxRows == 0; row++) { ! calcStats(row, 0, outerAvg, outerSDev, innerAvg, innerSDev); ! outerByInner = outerAvg / innerAvg; ! /*cout.setf (ios::fixed, ios::floatfield); ! cout << "row: " << std::setw (2) << row ! << std::setw (2) << ", oa: " << outerAvg ! << std::setw (2) << ", os: " << outerSDev ! << std::setw (2) << ", ia: " << innerAvg ! << std::setw (2) << ", is: " << innerSDev ! << std::setw (2) << ", o / row: " << outerByInner ! << endl;*/ ! if (outerByInner > m_threshold) ! { ! m_cropMaxRows = row - 1; ! } } ! max = m_image.cols () / 2; ! for (int col = 1; col < max && m_cropMaxCols == 0; col++) { ! calcStats(0, col, outerAvg, outerSDev, innerAvg, innerSDev); ! outerByInner = outerAvg / innerAvg; ! /*cout.setf (ios::fixed, ios::floatfield); ! cout << "col: " << std::setw (2) << col ! << std::setw (2) << ", oa: " << outerAvg ! << std::setw (2) << ", os: " << outerSDev ! << std::setw (2) << ", ia: " << innerAvg ! << std::setw (2) << ", is: " << innerSDev ! << std::setw (2) << ", o / col: " << outerByInner ! << endl;*/ ! if (outerByInner > m_threshold) ! { ! m_cropMaxCols = col - 1; ! } } + //cout << "calcOptimization: discard " << m_cropMaxRows + // << " rows and " << m_cropMaxCols << " cols." << endl; + calcInnerStats (innerAvg, innerSDev, m_cropMaxRows, m_cropMaxCols, + m_maxDetail->rows () - m_cropMaxRows, + m_maxDetail->cols () - m_cropMaxCols); ! m_innerSDeviation = innerSDev; ! m_innerAvgPerSize = innerAvg / m_innerRegionSize; ! } + int + ImageResizer::fixDimensions (int rows, int cols) + { coeff outerAvg; coeff outerSDev; coeff innerAvg; coeff innerSDev; + int nDiscardEach = 0; double ratio = (double)rows / (double)cols; ! double mapping = 0; int maxI = 0; if (m_cropWhat == CROP_COLS) { ! maxI = - (int)(m_maxDetail->rows () / ratio - m_maxDetail->cols () + 0.5) / 2; ! mapping = m_colsMapping; ! if (maxI < m_cropMaxCols) ! { ! return (int)(mapping * maxI); ! } ! maxI -= m_cropMaxCols; ! assert (maxI <= m_maxDetail->cols ()); ! } ! else if (m_cropWhat == CROP_ROWS) ! { ! maxI = - (int)(m_maxDetail->cols () * ratio - m_maxDetail->rows () + 0.5) / 2; ! mapping = m_rowsMapping; ! if (maxI < m_cropMaxRows) ! { ! return (int)(mapping * maxI); ! } ! maxI -= m_cropMaxRows; ! assert (maxI <= m_maxDetail->rows ()); } else { ! /* dimensions already match, nothing to do */ ! return 0; } double outerByInner; for (int i = 1; i < maxI && nDiscardEach == 0; i++) { ! calcStats (m_cropWhat == CROP_ROWS? i: 0, m_cropWhat == CROP_COLS? i: 0, ! outerAvg, outerSDev, innerAvg, innerSDev); outerByInner = outerAvg / innerAvg; ! /*cout.setf (ios::fixed, ios::floatfield); ! cout << "i: " << std::setw (2) << i << std::setw (2) << ", oa: " << outerAvg << std::setw (2) << ", os: " << outerSDev *************** *** 310,327 **** << std::setw (2) << ", o / i: " << outerByInner << endl;*/ - m_innerAvgPerSize = innerAvg / maxDetail->size (); - m_innerSDeviation = innerSDev; if (outerByInner > m_threshold) { ! nDiscardEach = (i > 0)? (int)(mapping * (i - 1)) : 0; } } ! doResizeImage(rows, cols, nDiscardEach); ! DELETE (maxDetail); - ColorImage *ret = m_resized; - m_resized = NULL; return ret; } --- 347,415 ---- << std::setw (2) << ", o / i: " << outerByInner << endl;*/ if (outerByInner > m_threshold) { ! nDiscardEach = (i > 1)? (int)(mapping * (i - 1)) : 0; } } + return nDiscardEach; + } ! ColorImage* ! ImageResizer::resize(int rows, int cols, int steps) ! { ! if ((rows <= 0 || cols <= 0) && !(rows == 0 && cols == 0 && m_optimizeImage)) ! { ! throw invalid_argument ("rows or columns arg is <= zero"); ! } ! calcDimensions (rows, cols); ! ! /* no dimension change necessary? do the job and go away */ ! if (m_cropWhat == CROP_BOTH_OR_NONE && !m_optimizeImage) ! { ! return m_image.scale (m_qRows, m_scalingStrategy); ! } ! ! /* We call genMaxDetail only once to save time */ ! if (m_maxDetail == NULL) ! { ! genMaxDetail (steps); ! } ! ! if (m_optimizeImage && !m_optimizationCalculated) ! { ! calcOptimization (); ! m_optimizationCalculated = true; ! } ! ! int nDiscard = fixDimensions (rows, cols); ! int nDiscardRows = (int)(m_cropMaxRows * m_rowsMapping + 0.5); ! int nDiscardCols = (int)(m_cropMaxCols * m_colsMapping + 0.5); ! ! if (m_cropWhat != CROP_AUTOMATICALLY) ! { ! /* can we crop even more than needed? */ ! if (m_image.rows () > rows && 2 * nDiscardRows > m_image.rows () - rows) ! { ! nDiscardRows = (m_image.rows () - rows) / 2; ! } ! if (m_image.cols () > cols && 2 * nDiscardCols > m_image.cols () - cols) ! { ! nDiscardCols = (m_image.cols () - cols) / 2; ! } ! ! /* have we gained some more to crop with differing dimensions? */ ! if (m_cropWhat == CROP_ROWS && nDiscard > nDiscardRows) ! { ! nDiscardRows = nDiscard; ! } ! else if (m_cropWhat == CROP_COLS && nDiscard > nDiscardCols) ! { ! nDiscardCols = nDiscard; ! } ! } ! ! ColorImage *ret = doResizeImage (rows, cols, nDiscardRows, nDiscardCols); return ret; } Index: Image.cc =================================================================== RCS file: /cvsroot/wavelet/Wavelet/Image.cc,v retrieving revision 1.24 retrieving revision 1.25 diff -C2 -d -r1.24 -r1.25 *** Image.cc 7 Aug 2007 17:00:58 -0000 1.24 --- Image.cc 10 Aug 2007 17:55:45 -0000 1.25 *************** *** 157,165 **** Image::variance (int fromY, int fromX, int toY, int toX, bool abs) const { - int nVals = 0; int lower = (toY < 0)? m_ysize: toY; int right = (toX < 0)? m_xsize: toX; coeff avg = abs? this->aaverage (fromY, fromX, lower, right) : this->saverage (fromY, fromX, lower, right); coeff ret = 0.0; coeff tmp; --- 157,174 ---- Image::variance (int fromY, int fromX, int toY, int toX, bool abs) const { int lower = (toY < 0)? m_ysize: toY; int right = (toX < 0)? m_xsize: toX; coeff avg = abs? this->aaverage (fromY, fromX, lower, right) : this->saverage (fromY, fromX, lower, right); + return variance (fromY, fromX, toY, toX, abs, avg); + } + + coeff + Image::variance (int fromY, int fromX, int toY, int toX, + coeff avg, bool abs) const + { + int nVals = 0; + int lower = (toY < 0)? m_ysize: toY; + int right = (toX < 0)? m_xsize: toX; coeff ret = 0.0; coeff tmp; |