2022-05-12 03:11:22 +00:00
|
|
|
|
#include "ImageApplyAutoCrop.h"
|
|
|
|
|
#include "ImageProcess_Public.h"
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <opencv2/imgproc/hal/hal.hpp>
|
|
|
|
|
#include "ImageApplyDispersion.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)
|
|
|
|
|
, m_isDispersion(true)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
: 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)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
pDib = dst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CImageApplyAutoCrop::apply(std::vector<cv::Mat>& 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++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define FRONT_TOP 70
|
|
|
|
|
#define FX_FY 0.5f
|
|
|
|
|
|
|
|
|
|
void 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uchar getBackGroudChannelMean(const cv::Mat& gray, int total, int threshold)
|
|
|
|
|
{
|
|
|
|
|
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<float>(i, 0);
|
|
|
|
|
|
|
|
|
|
int length = 1;
|
|
|
|
|
const int length_max = 255 - threshold;
|
|
|
|
|
while (length < length_max)
|
|
|
|
|
{
|
|
|
|
|
for (int i = threshold + 1; i < 256 - length; i++)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
uint pixSum = 0;
|
|
|
|
|
for (int 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv::Scalar getBackGroudColor(const cv::Mat& image, int total, int threshold)
|
|
|
|
|
{
|
|
|
|
|
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, threshold);
|
|
|
|
|
return cv::Scalar(bgr[0], bgr[1], bgr[2]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return cv::Scalar::all(getBackGroudChannelMean(image, total, threshold));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CImageApplyDispersion dispersion_apply;
|
2022-06-17 10:01:34 +00:00
|
|
|
|
#define COLOR_SCALE_THRE 0.5
|
2022-05-12 03:11:22 +00:00
|
|
|
|
void autoCrop_desaskew_fillBlank(const 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)
|
|
|
|
|
{
|
|
|
|
|
if (src.empty()) 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<cv::Vec4i> hierarchy;
|
|
|
|
|
std::vector<std::vector<cv::Point>> contours;
|
|
|
|
|
|
|
|
|
|
hg::findContours(thre, contours, hierarchy, cv::RETR_EXTERNAL);
|
|
|
|
|
|
|
|
|
|
for (std::vector<cv::Point>& sub : contours)
|
|
|
|
|
for (cv::Point& p : sub)
|
|
|
|
|
p /= FX_FY;
|
|
|
|
|
|
|
|
|
|
std::vector<cv::Point> 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)
|
|
|
|
|
{
|
|
|
|
|
cv::Mat mat_dispersion = src(cv::boundingRect(maxContour));
|
|
|
|
|
dispersion_apply.apply(mat_dispersion, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv::Scalar blankColor;
|
|
|
|
|
if (isFillBlank)
|
|
|
|
|
if (isColorBlank)
|
2022-06-17 10:01:34 +00:00
|
|
|
|
blankColor = getBackGroudColor(resizeMat, rect.size.area() * FX_FY * FX_FY, COLOR_SCALE_THRE);
|
2022-05-12 03:11:22 +00:00
|
|
|
|
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<double*>(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<cv::Point>& sub : contours)
|
|
|
|
|
for (cv::Point& p : sub)
|
|
|
|
|
{
|
|
|
|
|
x = p.x;
|
|
|
|
|
y = p.y;
|
|
|
|
|
p.x = static_cast<int>(a * x + b * y + c);
|
|
|
|
|
p.y = static_cast<int>(d * x + e * y + f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contours.push_back(std::vector<cv::Point>());
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|