#include "ImageApplyAutoCrop.h" #include "ImageProcess_Public.h" #include #include #include "ImageApplyDispersion.h" #define COLOR_BACKGROUND_THRE 20 #define FRONT_TOP 70 #define FX_FY 0.5f 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) , m_isDispersion(true) , m_fx(1.0) , m_fy(1.0) { } CImageApplyAutoCrop::CImageApplyAutoCrop(bool isCrop, bool isDesaskew, bool isFillBlank, const cv::Size& fixedSize, bool isConvex, bool isFillColor, double threshold, int noise, int indent, bool normalCrop, bool dispersion, double fx, double fy) : 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) , m_isDispersion(dispersion) , m_fx(fx) , m_fy(fy) { } CImageApplyAutoCrop::~CImageApplyAutoCrop() { } void CImageApplyAutoCrop::apply(cv::Mat& pDib, int side) { cv::Mat dst; autoCrop_desaskew_fillBlank(pDib, dst, m_isCrop, m_isDesaskew, m_isFillBlank, m_fixedSize.width, m_fixedSize.height, m_isConvexHull, m_isFillColor, m_threshold, m_noise, m_indent, m_normalCrop, m_isDispersion, m_fx, m_fy); pDib = dst; } void CImageApplyAutoCrop::apply(std::vector& mats, bool isTwoSide) { (void)isTwoSide; int i = 0; for (cv::Mat& var : mats) { if (i != 0 && isTwoSide == false) break; if (!var.empty()) apply(var, 0); i++; } } void CImageApplyAutoCrop::myWarpAffine(cv::InputArray _src, cv::OutputArray _dst, cv::InputArray _M0, cv::Size dsize, int flags, int borderType, const cv::Scalar& borderValue) { int interpolation = flags; cv::Mat src = _src.getMat(), M0 = _M0.getMat(); cv::Mat dst = _dst.getMat(); if (dst.data == src.data) src = src.clone(); double M[6] = { 0 }; cv::Mat matM(2, 3, CV_64F, M); if (interpolation == cv::INTER_AREA) interpolation = cv::INTER_LINEAR; M0.convertTo(matM, matM.type()); if (!(flags & cv::WARP_INVERSE_MAP)) { double D = M[0] * M[4] - M[1] * M[3]; D = D != 0 ? 1. / D : 0; double A11 = M[4] * D, A22 = M[0] * D; M[0] = A11; M[1] *= -D; M[3] *= -D; M[4] = A22; double b1 = -M[0] * M[2] - M[1] * M[5]; double b2 = -M[3] * M[2] - M[4] * M[5]; M[2] = b1; M[5] = b2; } cv::hal::warpAffine(src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, M, interpolation, borderType, borderValue.val); } cv::Scalar CImageApplyAutoCrop::getBackGroudColor(const cv::Mat& image, int threshold) { if (image.channels() == 3) { uchar table[768] = { 0 }; int hist_bgr[3][256] = { 0 }; int width = image.cols, height = image.rows, bytesPerLine = image.step; memset(table + threshold * 3, 255, 768 - threshold * 3); unsigned char* ptr_data = image.data; unsigned char b = 0; for (uint i = 0; i < height; i++, ptr_data += bytesPerLine) for (uint j = 0, x = 0; j < width; j++, x += 3) { b = table[ptr_data[x] + ptr_data[x + 1] + ptr_data[x + 2]]; for (uint k = 0; k < 3; k++) hist_bgr[k][ptr_data[x + k] & b]++; } int max_vals[3] = { 0 }; cv::Scalar max_indexes(0, 0, 0); for (uint i = 5; i < 256; i++) for (uint j = 0; j < 3; j++) if (hist_bgr[j][i] > max_vals[j]) { max_vals[j] = hist_bgr[j][i]; max_indexes[j] = i; } return max_indexes; } else { uchar table[256] = { 0 }; int hist_bgr[256] = { 0 }; int width = image.cols, height = image.rows, bytesPerLine = image.step; memset(table + threshold, 255, 256 - threshold); unsigned char* ptr_data = image.data; unsigned char b = 0; for (uint i = 0; i < height; i++, ptr_data += bytesPerLine) for (uint j = 0, x = 0; j < width; j++, x ++) hist_bgr[ptr_data[x] & table[ptr_data[x]]]++; int max_vals = 5; for (uint i = 5; i < 256; i++) if (hist_bgr[i] > hist_bgr[max_vals]) max_vals = i; return cv::Scalar(max_vals); } } void CImageApplyAutoCrop::autoCrop_desaskew_fillBlank(cv::Mat& src, cv::Mat& dst, bool isAutoCrop, bool isDesaskew, bool isFillBlank, int dWidth, int dHeight, bool isConvex, bool isColorBlank, double threshold, int noise, int indent, bool isNormalCrop, bool dispersion, double fx, double fy) { if (src.empty()) return; if (fx < 0.999999 || fx > 1.000001 || fy < 0.999999 || fy > 1.000001) cv::resize(src, src, cv::Size(), fx, fy, cv::INTER_LINEAR); if (!isAutoCrop && !isDesaskew && !isFillBlank && dWidth == 0 && dHeight == 0) { dst = src; return; } if (isNormalCrop) { cv::Rect roi = cv::Rect((src.cols - dWidth) / 2, FRONT_TOP, dWidth, dHeight) & cv::Rect(0, 0, src.cols, src.rows); dst = src(roi).clone(); return; } if (!isAutoCrop && !isDesaskew && !isFillBlank && (dWidth <= 0 || dHeight <= 0)) { dst = src.clone(); return; } cv::Mat resizeMat; cv::Mat thre; cv::resize(src, resizeMat, cv::Size(), FX_FY, FX_FY, cv::INTER_NEAREST); hg::threshold_Mat(resizeMat, thre, threshold); if (noise > 0) cv::morphologyEx(thre, thre, cv::MORPH_OPEN, getStructuringElement(cv::MORPH_RECT, cv::Size(noise * FX_FY, 1)), cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0)); std::vector hierarchy; std::vector> contours; hg::findContours(thre, contours, hierarchy, cv::RETR_EXTERNAL); for (std::vector& sub : contours) for (cv::Point& p : sub) p /= FX_FY; std::vector maxContour = hg::getMaxContour(contours, hierarchy); if (maxContour.empty()) { if (isAutoCrop) dst = src.clone(); else { cv::Rect roi = cv::Rect((src.cols - dWidth) / 2, FRONT_TOP, dWidth, dHeight) & cv::Rect(0, 0, src.cols, src.rows); dst = src(roi).clone(); } return; } cv::RotatedRect rect = hg::getBoundingRect(maxContour); if (dispersion) { CImageApplyDispersion m_dispersion_apply; cv::Mat mat_dispersion = src(cv::boundingRect(maxContour)); m_dispersion_apply.apply(mat_dispersion, 0); } cv::Scalar blankColor; if (isFillBlank) if (isColorBlank) blankColor = getBackGroudColor(resizeMat, COLOR_BACKGROUND_THRE); else blankColor = cv::Scalar::all(255); else blankColor = cv::Scalar::all(0); if (isAutoCrop) if (isDesaskew) dst = cv::Mat(cv::Size(rect.size), src.type(), blankColor); else dst = cv::Mat(rect.boundingRect().size(), src.type(), blankColor); else dst = cv::Mat(dHeight, dWidth, src.type(), blankColor); cv::Mat dstROI; if (isDesaskew && rect.angle != 0) { cv::Point2f srcTri[4], dstTri[3]; rect.points(srcTri); srcTri[0].x -= 1; srcTri[1].x -= 1; srcTri[2].x -= 1; int w = rect.size.width; int h = rect.size.height; int x = (dst.cols - w) / 2; int y = (dst.rows - h) / 2; dstTri[0] = cv::Point2f(0, h); dstTri[1] = cv::Point2f(0, 0); dstTri[2] = cv::Point2f(w, 0); dstROI = dst(cv::Rect(x, y, w, h) & cv::Rect(0, 0, dst.cols, dst.rows)); myWarpAffine(src, dstROI, cv::getAffineTransform(srcTri, dstTri), dstROI.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar::all(0)); } else { cv::Rect bounding = cv::boundingRect(maxContour); if (bounding.width > dst.cols) { bounding.x += (bounding.width - dst.cols) / 2; bounding.width = dst.cols; } if (bounding.height > dst.rows) { bounding.y += (bounding.height - dst.rows) / 2; bounding.height = dst.rows; } dstROI = dst(cv::Rect((dst.cols - bounding.width) / 2, (dst.rows - bounding.height) / 2, bounding.width, bounding.height)); src(bounding).copyTo(dstROI); } if (isFillBlank) { if (isConvex) { hg::convexHull(maxContour, maxContour); contours.clear(); contours.push_back(maxContour); } cv::Point2f srcTri[4], dstTri[3]; int w, h; if (isDesaskew && rect.angle != 0) { rect.points(srcTri); srcTri[0].x -= 1; srcTri[1].x -= 1; srcTri[2].x -= 1; w = rect.size.width; h = rect.size.height; } else { cv::Rect bounding = rect.boundingRect(); srcTri[0] = cv::Point(bounding.x, bounding.br().y - 1); srcTri[1] = cv::Point(bounding.x, bounding.y); srcTri[2] = cv::Point(bounding.br().x - 1, bounding.y); w = bounding.width; h = bounding.height; } dstTri[0] = cv::Point2f((dstROI.cols - w) / 2 + indent, (dstROI.rows - h) / 2 + h - indent); dstTri[1] = cv::Point2f((dstROI.cols - w) / 2 + indent, (dstROI.rows - h) / 2 + indent); dstTri[2] = cv::Point2f((dstROI.cols - w) / 2 - indent + w, (dstROI.rows - h) / 2 + indent); cv::Mat warp_mat = cv::getAffineTransform(srcTri, dstTri); 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]; int x, y; for (std::vector& sub : contours) for (cv::Point& p : sub) { x = p.x; y = p.y; p.x = static_cast(a * x + b * y + c); p.y = static_cast(d * x + e * y + f); } contours.push_back(std::vector()); contours[contours.size() - 1].push_back(cv::Point(-1, dstROI.rows - 1)); contours[contours.size() - 1].push_back(cv::Point(-1, -1)); contours[contours.size() - 1].push_back(cv::Point(dstROI.cols, -1)); contours[contours.size() - 1].push_back(cv::Point(dstROI.cols, dst.rows)); hg::fillPolys(dstROI, contours, blankColor); } }