code_app/imgproc/hg_imageprocess.cpp

615 lines
18 KiB
C++

#include "hg_imageprocess.hpp"
#include <opencv2/opencv.hpp>
#include <stdlib.h>
#include <malloc.h>
HG_Image::HG_Image()
: m_width(0)
, m_height(0)
, m_depth(0)
, m_isMono(false)
, m_step(0)
, m_data(nullptr)
, m_isConstructor(false)
{
}
HG_Image::HG_Image(const HG_Image &image)
: m_width(image.m_width)
, m_height(image.m_height)
, m_depth(image.m_depth)
, m_isMono(image.m_isMono)
, m_step(image.m_step)
, m_data(image.m_data)
, m_isConstructor(image.m_isConstructor)
{
if (m_isConstructor)
{
uchar* data = m_data - 1;
data[0]++;
}
}
HG_Image::HG_Image(int width, int height, int depth, bool isMono, size_t step)
: m_width(width)
, m_height(height)
, m_depth(depth)
, m_isMono(isMono)
, m_step(step)
, m_isConstructor(true)
{
if (m_step == 0)
m_step = ((width * depth / 8) + 3) / 4 * 4;
uchar* data = new uchar[m_step * m_height + 1];
data[0] = 1;
m_data = data + 1;
}
HG_Image::HG_Image(int width, int height, int depth, uchar* data, bool isMono, size_t step)
: m_width(width)
, m_height(height)
, m_depth(depth)
, m_isMono(isMono)
, m_step(step)
, m_data(data)
, m_isConstructor(false)
{
if (m_step == 0)
m_step = ((width * depth / 8) + 3) / 4 * 4;
}
HG_Image::~HG_Image()
{
if (m_isConstructor)
{
uchar* data = m_data - 1;
if ((data[0] - 1) == 0)
{
data[0]--;
delete[] data;
}
else
data[0]--;
}
}
bool HG_Image::empty()
{
return m_data == nullptr || m_width <= 0 || m_height <= 0;
}
void HG_Image::release()
{
if (m_isConstructor)
{
uchar* data = m_data - 1;
if ((data[0] - 1) == 0)
{
data[0]--;
delete[] data;
}
else
data[0]--;
}
m_width = 0;
m_height = 0;
m_depth = 0;
m_isMono = false;
m_step = 0;
m_data = nullptr;
m_isConstructor = false;
}
cv::Mat transforColor(const cv::Mat& src)
{
if (src.channels() == 1) return src.clone();
std::vector<cv::Mat> channels(3);
cv::split(src, channels);
cv::Mat temp, dst;
bitwise_or(channels[0], channels[1], temp);
bitwise_or(channels[2], temp, dst);
temp.release();
for (cv::Mat& index : channels)
index.release();
return dst;
}
void threshold_Mat(const cv::Mat& src, cv::Mat& dst, double thre)
{
if (src.channels() == 3)
{
#ifdef USE_ONENCL
if (cl_res.context)
transforColor_threshold_opencl(src, dst, static_cast<uchar>(thre));
else
#endif
{
cv::Mat gray = transforColor(src);
cv::threshold(gray, dst, thre, 255, cv::THRESH_BINARY);
gray.release();
}
}
else
cv::threshold(src, dst, thre, 255, cv::THRESH_BINARY);
}
void findContours(const cv::Mat& src, std::vector<std::vector<cv::Point>>& contours, std::vector<cv::Vec4i>& hierarchy,
int retr = cv::RETR_LIST, int method = cv::CHAIN_APPROX_SIMPLE, cv::Point offset = cv::Point(0, 0))
{
#if CV_VERSION_REVISION <= 6
CvMat c_image = src;
#else
CvMat c_image;
c_image = cvMat(src.rows, src.cols, src.type(), src.data);
c_image.step = (int)src.step[0];
c_image.type = (c_image.type & ~cv::Mat::CONTINUOUS_FLAG) | (src.flags & cv::Mat::CONTINUOUS_FLAG);
#endif
cv::MemStorage storage(cvCreateMemStorage());
CvSeq* _ccontours = nullptr;
#if CV_VERSION_REVISION <= 6
cvFindContours(&c_image, storage, &_ccontours, sizeof(CvContour), retr, method, CvPoint(offset));
#else
cvFindContours(&c_image, storage, &_ccontours, sizeof(CvContour), retr, method, CvPoint{ offset.x, offset.y });
#endif
if (!_ccontours)
{
contours.clear();
return;
}
cv::Seq<CvSeq*> all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage));
size_t total = all_contours.size();
contours.resize(total);
cv::SeqIterator<CvSeq*> it = all_contours.begin();
for (size_t i = 0; i < total; i++, ++it)
{
CvSeq* c = *it;
reinterpret_cast<CvContour*>(c)->color = static_cast<int>(i);
int count = c->total;
int* data = new int[static_cast<size_t>(count * 2)];
cvCvtSeqToArray(c, data);
for (int j = 0; j < count; j++)
{
contours[i].push_back(cv::Point(data[j * 2], data[j * 2 + 1]));
}
delete[] data;
}
hierarchy.resize(total);
it = all_contours.begin();
for (size_t i = 0; i < total; i++, ++it)
{
CvSeq* c = *it;
int h_next = c->h_next ? reinterpret_cast<CvContour*>(c->h_next)->color : -1;
int h_prev = c->h_prev ? reinterpret_cast<CvContour*>(c->h_prev)->color : -1;
int v_next = c->v_next ? reinterpret_cast<CvContour*>(c->v_next)->color : -1;
int v_prev = c->v_prev ? reinterpret_cast<CvContour*>(c->v_prev)->color : -1;
hierarchy[i] = cv::Vec4i(h_next, h_prev, v_next, v_prev);
}
storage.release();
}
std::vector<cv::Point> getMaxContour(const std::vector<std::vector<cv::Point>>& contours, const std::vector<cv::Vec4i>& hierarchy)
{
std::vector<cv::Point> maxContour;
if (contours.size() < 1) return {};
for (size_t i = 0, length = hierarchy.size(); i < length; i++)
if (hierarchy[i][3] == -1)
for (const auto &item : contours[i])
maxContour.push_back(item);
return maxContour;
}
cv::RotatedRect getBoundingRect(const std::vector<cv::Point>& contour)
{
if (contour.empty()) return {};
cv::RotatedRect rect = minAreaRect(contour);
if (rect.angle < -45)
{
rect.angle += 90;
float temp = rect.size.width;
rect.size.width = rect.size.height;
rect.size.height = temp;
}
if (rect.angle > 45)
{
rect.angle -= 90;
float temp = rect.size.width;
rect.size.width = rect.size.height;
rect.size.height = temp;
}
return rect;
}
void convexHull_(const std::vector<cv::Point>& src, std::vector<cv::Point>& dst, bool clockwise)
{
CvMemStorage* storage = cvCreateMemStorage(); //
CvSeq* ptseq = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage); //ptseqstorage
//将src的点集填充至ptseq
for (const cv::Point& item : src)
{
CvPoint p;
p.x = item.x;
p.y = item.y;
cvSeqPush(ptseq, &p);
}
//获取轮廓点
CvSeq* hull = cvConvexHull2(ptseq, nullptr, clockwise ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE, 0);
if (hull == nullptr)
{
//释放storage
cvReleaseMemStorage(&storage);
return;
}
//填充dst
dst.clear();
for (int i = 0, hullCount = hull->total; i < hullCount; i++)
dst.push_back(**CV_GET_SEQ_ELEM(CvPoint*, hull, i));
//释放storage
cvReleaseMemStorage(&storage);
}
void fillPolys(cv::Mat& image, const std::vector<std::vector<cv::Point>>& contours, const cv::Scalar& color)
{
if (contours.empty()) return;
size_t count = contours.size();
cv::Point** pointss = new cv::Point*[count];
int* npts = new int[count];
for (size_t i = 0; i < count; i++)
{
size_t length = contours[i].size();
npts[i] = (int)length;
pointss[i] = new cv::Point[length];
for (size_t j = 0; j < length; j++)
pointss[i][j] = contours[i][j];
}
cv::fillPoly(image, const_cast<const cv::Point**>(pointss), npts, (int)count, color);
for (size_t i = 0; i < count; i++)
delete[] pointss[i];
delete[] pointss;
delete[] npts;
}
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] = (int)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));
}
HG_Image HG_Image::autoCrop_desaskew_fillBlank(bool isAutoCrop, bool isDesaskew, bool isFillBlank, int dWidth, int dHeight,
bool isConvex, bool isColorBlank, double threshold, int noise, int indent)
{
if (!isAutoCrop && !isDesaskew && !isFillBlank && (dWidth <= 0 || dHeight <= 0))
return *this;
cv::Mat src(m_height, m_width, CV_8UC(m_depth / 8), m_data, m_step);
cv::Mat thre;
cv::Mat dst;
HG_Image dstImage;
threshold_Mat(src, thre, threshold);
if (noise > 0)
{
cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(noise, 1));
cv::morphologyEx(thre, thre, cv::MORPH_OPEN, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0));
}
if (indent > 0)
{
cv::Mat element = getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(indent, indent));
cv::morphologyEx(thre, thre, cv::MORPH_ERODE, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0));
}
std::vector<cv::Vec4i> hierarchy;
std::vector<std::vector<cv::Point>> contours;
findContours(thre, contours, hierarchy, cv::RETR_EXTERNAL);
std::vector<cv::Point> maxContour;
maxContour = getMaxContour(contours, hierarchy);
if (maxContour.size() == 0)
{
thre.release();
//
if (!isAutoCrop)
{
dstImage = HG_Image(dWidth, dHeight, m_depth, m_isMono);
dst = cv::Mat(dHeight, dWidth, CV_8UC(m_depth / 8), dstImage.data(), dstImage.step());
src(cv::Rect((src.cols - dWidth) / 2, (src.rows - dHeight) / 2, dWidth, dHeight) & cv::Rect(0, 0, src.cols, src.rows)).copyTo(dst);
return dstImage;
}
}
thre.release();
dst.release();
cv::RotatedRect rect = getBoundingRect(maxContour);
cv::Rect boudingRect = cv::boundingRect(maxContour);
if (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);
cv::warpAffine(src, dst, warp_mat, rect.size, cv::INTER_LINEAR);
dstImage = HG_Image((int)(rect.size.width + 0.5), (int)(rect.size.height + 0.5), m_depth, m_isMono);
int step_dst = (int)dst.step;
int step_dstImage = (int)dstImage.step();
uchar* data_dst = dst.data;
uchar* data_dstImage = dstImage.data();
for (int i = 0; i < dstImage.height(); i++)
{
memcpy(data_dstImage, data_dst, step_dst);
data_dst += step_dst;
data_dstImage += step_dstImage;
}
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];
double x, y;
for (cv::Point& p : maxContour)
{
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);
}
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);
}
}
else
{
auto t_rect = boudingRect & cv::Rect(0, 0, src.cols, src.rows);
dstImage = HG_Image(t_rect.width, t_rect.height, m_depth, m_isMono);
dst = cv::Mat(t_rect.height, t_rect.width, CV_8UC(m_depth / 8), dstImage.data(), dstImage.step());
src(t_rect).copyTo(dst);
cv::Point tl = t_rect.tl();
for (cv::Point& p : maxContour)
p -= tl;
contours.clear();
contours.push_back(maxContour);
}
cv::Scalar autoBGColor;
if (isFillBlank)
{
if (isConvex)
{
if (maxContour.size() == 0)
{
thre.release();
if (!isAutoCrop)
{
dstImage = HG_Image(dWidth, dHeight, m_depth, m_isMono);
dst = cv::Mat(dHeight, dWidth, CV_8UC(m_depth / 8), dstImage.data(), dstImage.step());
src(cv::Rect((m_width - dWidth) / 2, (m_height - dHeight) / 2, dWidth, dHeight) & cv::Rect(0, 0, m_width, m_height)).copyTo(dst);
return dstImage;
}
else
return *this;
}
convexHull_(maxContour, maxContour, false);
contours.clear();
contours.push_back(maxContour);
}
contours.push_back(std::vector<cv::Point>());
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 = isColorBlank ? getBackGroudColor(src, (int)rect.size.area(), (int)threshold) : cv::Scalar(255, 255, 255);
fillPolys(dst, contours, autoBGColor);
}
else
{
maxContour.clear();
maxContour.push_back(cv::Point(-1, dst.rows));
maxContour.push_back(cv::Point(-1, -1));
maxContour.push_back(cv::Point(dst.cols, -1));
maxContour.push_back(cv::Point(dst.cols, dst.rows));
}
if (isAutoCrop)
{
return dstImage;
}
else
{
HG_Image dstImageTemp(dWidth, dHeight, m_depth, m_isMono);
cv::Mat dstMat(dHeight, dWidth, CV_8UC(m_depth / 8), dstImageTemp.data(), dstImageTemp.step());
dstMat.setTo(isFillBlank ? autoBGColor : cv::Scalar(0, 0, 0));
cv::Rect roi;
roi.x = dst.cols > dstImage.m_width ? (dst.cols - dstImage.m_width) / 2 : 0;
roi.width = cv::min(dstImage.m_width, dst.cols);
roi.y = dst.rows > dstImage.m_height ? (dst.rows - dstImage.m_height) / 2 : 0;
roi.height = cv::min(dstImage.m_height, dst.rows);
int destX = (dWidth >= roi.width) ? (dWidth - roi.width) / 2 : 0;
int destY = (dHeight >= roi.height) ? (dHeight - roi.height) / 2 : 0;
int destW = (dWidth >= roi.width) ? roi.width : dWidth;
int destH = (dHeight >= roi.height) ? roi.height : dHeight;
int srcX = (dWidth >= roi.width) ? roi.x : roi.x + (roi.width - dWidth) / 2;
int srcY = (dHeight >= roi.height) ? roi.y : roi.y + (roi.height - dHeight) / 2;
int srcW = (dWidth >= roi.width) ? roi.width : dWidth;
int srcH = (dHeight >= roi.height) ? roi.height : dHeight;
cv::Rect destRect(destX, destY, destW, destH);
cv::Rect srcRect(srcX, srcY, srcW, srcH);
dst(srcRect).copyTo(dstMat(destRect));
return dstImageTemp;
}
}
void HG_Image::adjustColors(int brightness, int contrast, float gamma)
{
if (brightness == 0 && contrast == 0 && gamma > 0.999999f && gamma < 1.000001f)
return;
brightness = cv::max(-255, cv::min(brightness, 255));
contrast = cv::max(-127, cv::min(contrast, 127));
gamma = cv::max(0.1f, cv::min(gamma, 5.0f));
cv::Mat lut(1, 256, CV_8UC1);
uchar* ptr = lut.data;
for (int i = 0; i < 256; i++)
{
//update brightness
ptr[i] = static_cast<unsigned char>(cv::max(0, cv::min(i + brightness, 255)));
//update contrast
if (ptr[i] < 128)
ptr[i] = static_cast<unsigned char>(cv::max(0, cv::min(ptr[i] - contrast, 127)));
else
ptr[i] = static_cast<unsigned char>(cv::max(127, cv::min(ptr[i] + contrast, 255)));
}
float g = 1.0f / gamma;
for (int i = 0; i < 256; i++)
ptr[i] = static_cast<unsigned char>(cv::min(255, static_cast<int>(cv::pow(static_cast<float>(ptr[i]) / 255.0f, g) * 255.0f + 0.5f)));
cv::Mat image(m_height, m_width, CV_8UC(m_depth / 8), m_data, m_step);
cv::LUT(image, lut, image);
}
HG_Image HG_Image::resize(int dWidth, int dHeight, int interpolation)
{
cv::Mat src(m_height, m_width, CV_8UC(m_depth >> 3), m_data, m_step);
HG_Image dst_(dWidth, dHeight, m_depth, m_isMono);
cv::Mat dst(dHeight, dWidth, CV_8UC(m_depth >> 3), dst_.data(), dst_.step());
cv::resize(src, dst, cv::Size(dWidth, dHeight), 0, 0, interpolation);
return dst_;
}
HG_Image HG_Image::resize( double fx, double fy, int interpolation)
{
int dWidth = static_cast<int>(m_width * fx);
int dHeight = static_cast<int>(m_height * fy);
return resize(dWidth, dHeight, interpolation);
}
HG_Image& HG_Image::operator=(const HG_Image& image)
{
if (this == &image)
return *this;
release();
m_width = image.m_width;
m_height = image.m_height;
m_depth = image.m_depth;
m_isMono = image.m_isMono;
m_step = image.m_step;
m_data = image.m_data;
m_isConstructor = image.m_isConstructor;
if (m_isConstructor)
{
uchar* data = m_data - 1;
data[0]++;
}
return *this;
}