#include "ImageApplyAutoCrop.h" #include "ImageProcess_Public.h" CImageApplyAutoCrop::CImageApplyAutoCrop() : m_isCrop(false) , m_isDesaskew(false) , m_isFillBlank(false) , m_isConvexHull(true) , m_isFillColor(false) , m_threshold(40) , m_noise(8) , m_indent(5) , m_normalCrop(false) { } CImageApplyAutoCrop::CImageApplyAutoCrop(bool isCrop, bool isDesaskew, bool isFillBlank, const cv::Size& fixedSize, bool isConvex, bool isFillColor, double threshold, int noise, int indent, bool normalCrop) : m_isCrop(isCrop) , m_isDesaskew(isDesaskew) , m_isFillBlank(isFillBlank) , m_isConvexHull(isConvex) , m_isFillColor(isFillColor) , m_threshold(threshold) , m_noise(noise) , m_indent(indent) , m_fixedSize(fixedSize) , m_normalCrop(normalCrop) { } CImageApplyAutoCrop::~CImageApplyAutoCrop() { } void matmul(double* mul1, double* mul2, double* dst) { dst[0] = mul1[0] * mul2[0] + mul1[1] * mul2[3] + mul1[2] * mul2[6]; dst[1] = mul1[0] * mul2[1] + mul1[1] * mul2[4] + mul1[2] * mul2[7]; dst[2] = mul1[0] * mul2[2] + mul1[1] * mul2[5] + mul1[2] * mul2[8]; dst[3] = mul1[3] * mul2[0] + mul1[4] * mul2[3] + mul1[5] * mul2[6]; dst[4] = mul1[3] * mul2[1] + mul1[4] * mul2[4] + mul1[5] * mul2[7]; dst[5] = mul1[3] * mul2[2] + mul1[4] * mul2[5] + mul1[5] * mul2[8]; dst[6] = mul1[6] * mul2[0] + mul1[7] * mul2[3] + mul1[8] * mul2[6]; dst[7] = mul1[6] * mul2[1] + mul1[7] * mul2[4] + mul1[8] * mul2[7]; dst[8] = mul1[6] * mul2[2] + mul1[7] * mul2[5] + mul1[8] * mul2[8]; } cv::Mat concatenateMatrix(const cv::Mat& first, const cv::Mat& second) { double buffer1[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1} ; double buffer2[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1} ; cv::Mat mul1(3, 3, CV_64FC1, buffer1); //cv::Mat::eye(3, 3, CV_64F); cv::Mat mul2(3, 3, CV_64FC1, buffer2); //cv::Mat::eye(3, 3, CV_64F); cv::Mat mul_r; first.convertTo(mul_r, CV_64F); mul_r.row(0).copyTo(mul1.row(0)); mul_r.row(1).copyTo(mul1.row(1)); second.convertTo(mul_r, CV_64F); mul_r.row(0).copyTo(mul2.row(0)); mul_r.row(1).copyTo(mul2.row(1)); //mul1 = mul2 * mul1; cv::Mat temp(3, 3, CV_64FC1); matmul(buffer2, buffer1, (double*)temp.data); mul1 = temp; mul_r = first.clone(); mul1.row(0).copyTo(mul_r.row(0)); mul1.row(1).copyTo(mul_r.row(1)); return mul_r; } std::vector comMat() { std::vector mats; cv::Point2f srcTri[3]; srcTri[0] = cv::Point2f(1, 1); srcTri[1] = cv::Point2f(1, 0); srcTri[2] = cv::Point2f(0, 1); const float fact = 0.33f; float pos[] = { 0, 2 * fact, fact }; cv::Point2f dstTri[3]; dstTri[0] = cv::Point2f(1, 1); dstTri[1] = cv::Point2f(1, 0.5); dstTri[2] = cv::Point2f(0, 1); for (int i = 0; i < 3; i++) { dstTri[0] = cv::Point2f(1, 1 + pos[i]); dstTri[1] = cv::Point2f(1, pos[i]); dstTri[2] = cv::Point2f(0, 1 + pos[i]); mats.push_back(cv::getAffineTransform(srcTri, dstTri)); } return mats; } void brightSharp(cv::Mat& src) { const float a = -0.49f; const float b = 3.0f; //float kernel_data[] = { // a, 0, 0, 0, a, // 0, 0, a, 0, 0, // 0, a, b, a, 0, // 0, 0, a, 0, 0, // a, 0, 0, 0, a }; float kernel_data[] = { 0, a, 0, a, b, a, 0, a, 0 }; cv::Mat kernel(3, 3, CV_32FC1, kernel_data); cv::filter2D(src, src, src.depth(), kernel); } void CImageApplyAutoCrop::apply(cv::Mat& pDib, int side) { (void)side; if (pDib.empty()) return; if (m_normalCrop) { cv::Rect roi = cv::Rect((pDib.cols - m_fixedSize.width) / 2, side == 0 ? 75 : 145, m_fixedSize.width, m_fixedSize.height) & cv::Rect(0, 0, pDib.cols, pDib.rows); pDib = pDib(roi).clone(); m_rect = cv::RotatedRect(cv::Point2f(roi.x + roi.width / 2, roi.y + roi.height / 2), cv::Size2f(roi.width, roi.height), 0.0f); return; } if (!m_isCrop && !m_isDesaskew && !m_isFillBlank && m_fixedSize.empty()) return; cv::Mat src = pDib; cv::Mat thre; cv::Mat dst; hg::threshold_Mat(src, thre, m_threshold); if (m_noise > 0) { cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(m_noise, 1)); cv::morphologyEx(thre, thre, cv::MORPH_OPEN, element); } if (m_indent > 0) { cv::Mat element = getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(m_indent, m_indent)); cv::morphologyEx(thre, thre, cv::MORPH_ERODE, element); } std::vector hierarchy; std::vector> contours; hg::findContours(thre, contours, hierarchy, cv::RETR_EXTERNAL); m_maxContour = hg::getMaxContour(contours, hierarchy); if (m_maxContour.size() == 0) { thre.release(); // if (!m_isCrop) pDib = pDib(cv::Rect((pDib.cols - m_fixedSize.width) / 2, (pDib.rows - m_fixedSize.height) / 2, m_fixedSize.width, m_fixedSize.height) & cv::Rect(0, 0, pDib.cols, pDib.rows)).clone(); #ifdef LOG FileTools::write_log("imgprc.txt", "exit CImageApplyAutoCrop apply"); #endif // LOG return; } thre.release(); dst.release(); cv::RotatedRect rect = hg::getBoundingRect(m_maxContour); m_rect = rect; cv::Rect boudingRect = cv::boundingRect(m_maxContour); boudingRect.x -= 1; boudingRect.y -= 1; boudingRect.width += 2; boudingRect.height += 2; if (m_isDesaskew && rect.angle != 0) { cv::Point2f srcTri[4], srcTri_temp[3], dstTri[3]; rect.points(srcTri); dstTri[0] = cv::Point2f(0, rect.size.height - 1); dstTri[1] = cv::Point2f(0, 0); dstTri[2] = cv::Point2f(rect.size.width - 1, 0); srcTri_temp[0] = dstTri[0]; srcTri_temp[1] = dstTri[1]; srcTri_temp[2] = dstTri[2]; cv::Mat warp_mat; warp_mat = cv::getAffineTransform(srcTri, dstTri); if (src.channels() == 1) { cv::warpAffine(src, dst, warp_mat, rect.size, cv::INTER_LINEAR); } else { cv::Mat bgr[3]; cv::split(src, bgr); auto mats = comMat(); warp_mat = cv::getAffineTransform(srcTri, dstTri); warp_mat = concatenateMatrix(mats[0], warp_mat); cv::warpAffine(bgr[0], bgr[0], warp_mat, rect.size, cv::INTER_LINEAR); warp_mat = cv::getAffineTransform(srcTri, dstTri); warp_mat = concatenateMatrix(mats[1], warp_mat); cv::warpAffine(bgr[1], bgr[1], warp_mat, rect.size, cv::INTER_LINEAR); warp_mat = cv::getAffineTransform(srcTri, dstTri); warp_mat = concatenateMatrix(mats[2], warp_mat); cv::warpAffine(bgr[2], bgr[2], warp_mat, rect.size, cv::INTER_LINEAR); cv::merge(bgr, 3, dst); } double* ptr_m = reinterpret_cast(warp_mat.data); double a = ptr_m[0]; double b = ptr_m[1]; double c = ptr_m[2]; double d = ptr_m[3]; double e = ptr_m[4]; double f = ptr_m[5]; for (cv::Point& p : m_maxContour) { p.x = static_cast(a * p.x + b * p.y + c); p.y = static_cast(d * p.x + e * p.y + f); } for (std::vector& sub : contours) for (cv::Point& p : sub) { p.x = static_cast(a * p.x + b * p.y + c); p.y = static_cast(d * p.x + e * p.y + f); } } else { auto t_rect = boudingRect & cv::Rect(0, 0, src.cols, src.rows); dst = src(t_rect); if (dst.channels() == 3) { cv::Mat bgr[3]; cv::split(dst, bgr); auto mats = comMat(); for (int i = 0; i < 3; i++) cv::warpAffine(bgr[i], bgr[i], mats[i], t_rect.size(), cv::INTER_LINEAR); cv::merge(bgr, 3, dst); } } cv::Scalar autoBGColor; if (m_isFillBlank) { if (m_isConvexHull) { if (m_maxContour.size() == 0) { thre.release(); //����ǹ̶����棬�뷵�ز��к�ijߴ� if (!m_isCrop) pDib = pDib(cv::Rect((pDib.cols - m_fixedSize.width) / 2, (pDib.rows - m_fixedSize.height) / 2, m_fixedSize.width, m_fixedSize.height) & cv::Rect(0, 0, pDib.cols, pDib.rows)).clone(); return; } hg::convexHull(m_maxContour, m_maxContour); contours.clear(); contours.push_back(m_maxContour); } contours.push_back(std::vector()); contours[contours.size() - 1].push_back(cv::Point(-1, dst.rows - 1)); contours[contours.size() - 1].push_back(cv::Point(-1, -1)); contours[contours.size() - 1].push_back(cv::Point(dst.cols, -1)); contours[contours.size() - 1].push_back(cv::Point(dst.cols, dst.rows)); autoBGColor = m_isFillColor ? getBackGroudColor(pDib, rect.size.area()) : cv::Scalar(255, 255, 255); hg::fillPolys(dst, contours, autoBGColor); } else { m_maxContour.clear(); m_maxContour.push_back(cv::Point(-1, dst.rows)); m_maxContour.push_back(cv::Point(-1, -1)); m_maxContour.push_back(cv::Point(dst.cols, -1)); m_maxContour.push_back(cv::Point(dst.cols, dst.rows)); } pDib.release(); if (/*(m_isCrop && side == 0) || (side == 1 && m_fixedSize.width * m_fixedSize.height == 0)*/ m_isCrop) pDib = dst.clone(); else { pDib = cv::Mat(m_fixedSize, dst.type(), m_isFillBlank ? autoBGColor : cv::Scalar(0, 0, 0)); cv::Rect roi; roi.x = dst.cols > pDib.cols ? (dst.cols - pDib.cols) / 2 : 0; roi.width = cv::min(pDib.cols, dst.cols); roi.y = dst.rows > pDib.rows ? (dst.rows - pDib.rows) / 2 : 0; roi.height = cv::min(pDib.rows, dst.rows); cv::Rect rect((pDib.cols - roi.width) / 2, (pDib.rows - roi.height) / 2, roi.width, roi.height); for (cv::Point& p : m_maxContour) p += roi.tl(); dst(roi).copyTo(pDib(rect)); } #ifdef LOG FileTools::write_log("imgprc.txt", "exit CImageApplyAutoCrop apply8"); #endif // LOG } void CImageApplyAutoCrop::apply(std::vector& mats, bool isTwoSide) { if (mats.empty()) return; if (!mats[0].empty()) { apply(mats[0], 0); m_rects.push_back(m_rect); //brightSharp(mats[0]); } if (isTwoSide && mats.size() > 1) { cv::Size dSize = m_fixedSize; if (!mats[0].empty()) m_fixedSize = mats[0].size(); if (!mats[1].empty()) { apply(mats[1], 1); m_rects.push_back(m_rect); //brightSharp(mats[1]); } if (!mats[0].empty()) m_fixedSize = dSize; } } cv::Scalar CImageApplyAutoCrop::getBackGroudColor(const cv::Mat& image, int total) { if (image.channels() == 3) { cv::Mat image_bgr[3]; cv::split(image, image_bgr); uchar bgr[3]; for (size_t i = 0; i < 3; i++) bgr[i] = getBackGroudChannelMean(image_bgr[i], total); return cv::Scalar(bgr[0], bgr[1], bgr[2]); } else return cv::Scalar::all(getBackGroudChannelMean(image, total)); } uchar CImageApplyAutoCrop::getBackGroudChannelMean(const cv::Mat& gray, int total) { cv::Mat image_clone; cv::resize(gray, image_clone, cv::Size(), 0.25, 0.25); int threnshold = total / 32; int channels[] = { 0 }; int nHistSize[] = { 256 }; float range[] = { 0, 256 }; const float* fHistRanges[] = { range }; cv::Mat hist; cv::calcHist(&image_clone, 1, channels, cv::Mat(), hist, 1, nHistSize, fHistRanges, true, false); int hist_array[256]; for (int i = 0; i < 256; i++) hist_array[i] = hist.at(i, 0); int length = 1; const int length_max = 255 - m_threshold; while (length < length_max) { for (size_t i = m_threshold + 1; i < 256 - length; i++) { int count = 0; uint pixSum = 0; for (size_t j = 0; j < length; j++) { count += hist_array[j + i]; pixSum += hist_array[j + i] * (i + j); } if (count >= threnshold) return pixSum / count; } length++; } return 255; }