#include "hg_imageprocess.hpp" #include #include #include 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 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(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>& contours, std::vector& 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 all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage)); size_t total = all_contours.size(); contours.resize(total); cv::SeqIterator it = all_contours.begin(); for (size_t i = 0; i < total; i++, ++it) { CvSeq* c = *it; reinterpret_cast(c)->color = static_cast(i); int count = c->total; int* data = new int[static_cast(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(c->h_next)->color : -1; int h_prev = c->h_prev ? reinterpret_cast(c->h_prev)->color : -1; int v_next = c->v_next ? reinterpret_cast(c->v_next)->color : -1; int v_prev = c->v_prev ? reinterpret_cast(c->v_prev)->color : -1; hierarchy[i] = cv::Vec4i(h_next, h_prev, v_next, v_prev); } storage.release(); } std::vector getMaxContour(const std::vector>& contours, const std::vector& hierarchy) { std::vector 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& 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& src, std::vector& 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>& 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(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(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 hierarchy; std::vector> contours; findContours(thre, contours, hierarchy, cv::RETR_EXTERNAL); std::vector 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(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(a * x + b * y + c); p.y = static_cast(d * x + e * y + f); } 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); } } 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()); 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(cv::max(0, cv::min(i + brightness, 255))); //update contrast if (ptr[i] < 128) ptr[i] = static_cast(cv::max(0, cv::min(ptr[i] - contrast, 127))); else ptr[i] = static_cast(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(cv::min(255, static_cast(cv::pow(static_cast(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(m_width * fx); int dHeight = static_cast(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; }