diff --git a/changelog.txt b/changelog.txt index c32d18d3..8f0df60f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -64,4 +64,12 @@ 2.屏蔽3288 自适应幅面功能 3.修复显示UI时通过协议reset停止扫描时按钮未正常复位问题 4.更新除穿孔以及跳过空白页算法 - 5.新增待纸扫描超时设置 -- 22.10.20 \ No newline at end of file + 5.新增待纸扫描超时设置 -- 22.10.20 +2022年10月28日 彭明 + 版本号: + 1. 版本增加HSV答题卡留红除杂色功能,增加Twain协议(0x8116),以及UI部分控件互斥逻辑; + 2. GScanCap增加HSVFilterType字段定义; + 3. 更新PC本地图像处理算法(HSV留红除杂色 v1.4 ->v1.5.1 自动纠偏裁切算法 v1.3.12->v1.4.2) +2022年11月6日 + 1. 针对G300 3288 版本 增加200 300真实DPI; + 2. 开放速度优先以及画质优先模式; \ No newline at end of file diff --git a/huagao/CImageProcPage.cpp b/huagao/CImageProcPage.cpp index bb238060..466a336f 100644 --- a/huagao/CImageProcPage.cpp +++ b/huagao/CImageProcPage.cpp @@ -45,6 +45,7 @@ CImageProcPage::CImageProcPage(CWnd* pParent /*=nullptr*/) , m_ckbRemoveHole(FALSE) , m_ckbDetachNoise(FALSE) , m_ckbHSVCorrect(FALSE) + , m_ckbHSVDetechNoise(FALSE) , indent(5) , noise(8) ,threshold(40) @@ -77,7 +78,8 @@ void CImageProcPage::ImageProcPageUpdate(int val,int twss, int cmduplexsel,bool if (val == 0) { GetDlgItem(IDC_CKHSVCORRECT)->EnableWindow(TRUE); - GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(TRUE); + //GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(TRUE); + GetDlgItem(IDC_CKB_ANSWERSHEETFILTER)->EnableWindow(TRUE); if (dpi >= 500) { GetDlgItem(IDC_CHMULTIPUT)->EnableWindow(FALSE); ((CButton*)GetDlgItem(IDC_CHMULTIPUT))->SetCheck(FALSE); @@ -94,8 +96,13 @@ void CImageProcPage::ImageProcPageUpdate(int val,int twss, int cmduplexsel,bool else { GetDlgItem(IDC_CKHSVCORRECT)->EnableWindow(FALSE); ((CButton*)GetDlgItem(IDC_CKHSVCORRECT))->SetCheck(FALSE); - GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(FALSE); - ((CButton*)GetDlgItem(IDC_CKBACKGROUNDSMOOTH))->SetCheck(FALSE); + //GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(FALSE); + //((CButton*)GetDlgItem(IDC_CKBACKGROUNDSMOOTH))->SetCheck(FALSE); + + GetDlgItem(IDC_CKB_ANSWERSHEETFILTER)->EnableWindow(FALSE); + ((CButton*)GetDlgItem(IDC_CKB_ANSWERSHEETFILTER))->SetCheck(FALSE); + ((CButton*)GetDlgItem(IDC_CKB_ANSWERSHEETFILTER))->SetCheck(FALSE); + GetDlgItem(IDC_CHMULTIPUT)->EnableWindow(FALSE); ((CButton*)GetDlgItem(IDC_CHMULTIPUT))->SetCheck(FALSE); GetDlgItem(IDC_CMBMULTIOUT)->EnableWindow(FALSE); @@ -112,14 +119,14 @@ void CImageProcPage::ImageProcPageUpdate(int val,int twss, int cmduplexsel,bool else { m_temp->EnableWindow(TRUE); - GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(FALSE); + //GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(FALSE); } - if (val == 2) + if (val == 2)//黑白模式下 { ((CButton*)GetDlgItem(IDC_CHECKDETACHNOISE))->EnableWindow(true); - //m_temp->SetCurSel(4); + GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(FALSE); } else { @@ -140,6 +147,8 @@ void CImageProcPage::ImageProcPageUpdate(int val,int twss, int cmduplexsel,bool else ((CButton*)GetDlgItem(IDC_CKCROPMODEL))->EnableWindow(true); + //GetDlgItem(IDC_CKBACKGROUNDSMOOTH)->EnableWindow(val<=1?TRUE:FALSE); + } void CImageProcPage::ImageAutoDescrewUpdate(int val) @@ -170,6 +179,7 @@ void CImageProcPage::DoDataExchange(CDataExchange* pDX) DDX_Check(pDX, IDC_CKREMOVEHOLE, m_ckbRemoveHole); DDX_Check(pDX, IDC_CHECKDETACHNOISE,m_ckbDetachNoise); DDX_Check(pDX, IDC_CKHSVCORRECT, m_ckbHSVCorrect); + DDX_Check(pDX, IDC_CKB_ANSWERSHEETFILTER, m_ckbHSVDetechNoise); } BOOL CImageProcPage::OnInitDialog() diff --git a/huagao/CImageProcPage.h b/huagao/CImageProcPage.h index ba3b851c..3a3c2475 100644 --- a/huagao/CImageProcPage.h +++ b/huagao/CImageProcPage.h @@ -29,6 +29,7 @@ public: BOOL m_ckbMultioutput; BOOL m_ckbRemoveHole; BOOL m_ckbHSVCorrect; + BOOL m_ckbHSVDetechNoise; BOOL m_ckbDetachNoise; void ImageProcPageUpdate(int val,int twss,int cmduplexsel, bool is_Crop,int dpi); void ImageAutoDescrewUpdate(int val); diff --git a/huagao/CTwainUI.cpp b/huagao/CTwainUI.cpp index 71a44572..c891bc67 100644 --- a/huagao/CTwainUI.cpp +++ b/huagao/CTwainUI.cpp @@ -144,7 +144,13 @@ BOOL CTwainUI::OnInitDialog() ((CButton*)m_pageBasic->GetDlgItem(IDC_RDQUALITYPRIORITY))->ShowWindow(SW_SHOW); ((CButton*)m_pageBasic->GetDlgItem(IDC_RDSPEEDPRIORITY))->ShowWindow(SW_SHOW); } -#endif // G400 +#elif defined(G300) + if (atoi(m_hardwareVersion.substr(4, 6).c_str()) >= 221106) + { + ((CButton*)m_pageBasic->GetDlgItem(IDC_RDQUALITYPRIORITY))->ShowWindow(SW_SHOW); + ((CButton*)m_pageBasic->GetDlgItem(IDC_RDSPEEDPRIORITY))->ShowWindow(SW_SHOW); + } +#endif setvisable_dogear(false); setvisable_autopaper(false); setvisable_fixedpaper(false); @@ -294,6 +300,7 @@ void CTwainUI::UpdateUI() m_pageImageProc->m_en_fillhole_right = settings->en_fillholeratio_right; m_pageImageProc->GetDlgItem(IDC_SLDOUTHOLE)->EnableWindow(m_pageImageProc->m_ckbRemoveHole); m_pageImageProc->m_ckbfadeback = settings->fadeback; + m_pageImageProc->m_ckbHSVDetechNoise = settings->hsvFilter; ((CButton*)m_pageImageProc->GetDlgItem(IDC_CKBACKGROUNDSMOOTH))->SetCheck(m_pageImageProc->m_ckbfadeback); ((CButton*)m_pageImageProc->GetDlgItem(IDC_CKBACKGROUNDSMOOTH))->EnableWindow(settings->pixtype == 2); m_pageImageProc->m_edit_faderange.SetValue(settings->fadeback_range); @@ -315,7 +322,10 @@ void CTwainUI::UpdateUI() m_pageImageProc->m_ckbMultioutput = settings->multi_output_red == TRUE ? TRUE : FALSE;//多流除红 else m_pageImageProc->m_ckbMultioutput = FALSE;//多流除红 + m_pageImageProc->m_ckbHSVCorrect = settings->hsvcorrect == TRUE ? TRUE : FALSE;//答题卡除红 + m_pageImageProc->m_ckbHSVDetechNoise = settings->hsvFilter == TRUE ? TRUE : FALSE; + m_pageImageProc->GetDlgItem(IDC_CKMULTIOUTPUT)->EnableWindow(settings->pixtype == 2);//彩色可用 ((CButton*)m_pageImageProc->GetDlgItem(IDC_CKMULTIOUTPUT))->SetCheck(settings->pixtype == 2); m_pageImageProc->UpdateData(FALSE); @@ -566,6 +576,7 @@ void CTwainUI::UpDateScanParam(PCONFIGPARAMS configItem, bool updateDs) configItem->EnMultiOutPutR = m_pageImageProc->m_ckbMultioutput;//多流除红 configItem->EnHsvCorrect = m_pageImageProc->m_ckbHSVCorrect;//答题卡除红 + configItem->EnHsvFilterDetechNoise = m_pageImageProc->m_ckbHSVDetechNoise; //!< Page feed paper m_pageFeedPaper->UpdateData(); @@ -690,6 +701,7 @@ void CTwainUI::UpDateScanParam(PCONFIGPARAMS configItem, bool updateDs) settings->en_multi_output = ((CButton*)m_pageImageProc->GetDlgItem(IDC_CHMULTIPUT))->GetCheck(); settings->multioutput = ((CComboBox*)m_pageImageProc->GetDlgItem(IDC_CMBMULTIOUT))->GetCurSel(); settings->hsvcorrect = configItem->EnHsvCorrect; + settings->hsvFilter = configItem->EnHsvFilterDetechNoise; #ifdef REAL300DPI settings->resolution_native = settings->resolution_dst > 240.0f ? 300.0f : 200.0f; #else // REAL300DPI diff --git a/huagao/Device/G400ScanConfig.cpp b/huagao/Device/G400ScanConfig.cpp index a84c5437..446721f0 100644 --- a/huagao/Device/G400ScanConfig.cpp +++ b/huagao/Device/G400ScanConfig.cpp @@ -6,7 +6,7 @@ G400ScanConfig::G400ScanConfig(GScanCap& gcap) cfg.params.doubleFeeded = gcap.hardwarecaps.en_doublefeed == 0 ? 0 : 1; cfg.params.dpi = G400_DPI::G400_D200;//gcap.resolution_dst <= 200.0f ? G400_DPI::G400_D200 : (gcap.resolution_dst <= 300.0f ? G400_DPI::G400_D300 : G400_DPI::G400_D600); cfg.params.enableLed = 1; - if (gcap.filter != 3 || gcap.enhance_color) + if (gcap.filter != 3 || gcap.enhance_color||gcap.hsvFilter!=0||gcap.hsvcorrect!=0||gcap.fadeback!=0) cfg.params.isColor = 1; else cfg.params.isColor = SupPixelTypes[gcap.pixtype]; @@ -46,7 +46,7 @@ G400AndroidScanConfig::G400AndroidScanConfig(GScanCap& gcap) cfg.params.doubleFeeded = gcap.hardwarecaps.en_doublefeed == 0 ? 0 : 1; cfg.params.dpi = G400_DPI::G400_D200;//gcap.resolution_dst <= 200.0f ? G400_DPI::G400_D200 : (gcap.resolution_dst <= 300.0f ? G400_DPI::G400_D300 : G400_DPI::G400_D600); cfg.params.enableLed = 1; - if (gcap.filter != 3 || gcap.enhance_color) + if (gcap.filter != 3 || gcap.enhance_color ||gcap.fadeback!=0|| gcap.hsvFilter != 0) cfg.params.isColor = 1; else cfg.params.isColor = SupPixelTypes[gcap.pixtype]; diff --git a/huagao/Device/GScan439Android.cpp b/huagao/Device/GScan439Android.cpp index 37ff72ba..cacb1846 100644 --- a/huagao/Device/GScan439Android.cpp +++ b/huagao/Device/GScan439Android.cpp @@ -202,7 +202,7 @@ void GScan439Android::config_params(GScanCap& param) PaperStatus ps = { param.papertype,param.paperAlign }; cfg.g200params.paper = SupPaperTyps.count(ps) > 0 ? SupPaperTyps[ps] : 0; - if (param.filter != 3 || param.enhance_color != 0 || param.hsvcorrect) + if (param.filter != 3 || param.enhance_color != 0 || param.hsvcorrect||param.hsvFilter!=0||param.fadeback!=0) cfg.g200params.color = 1;//color else { diff --git a/huagao/Device/GScanO1003399.cpp b/huagao/Device/GScanO1003399.cpp index 42f33ca3..09055117 100644 --- a/huagao/Device/GScanO1003399.cpp +++ b/huagao/Device/GScanO1003399.cpp @@ -279,7 +279,7 @@ void GScanO1003399::config_params(GScanCap& param) // cfg.g200params.paper = SupPaperTyps_39.count(ps) > 0 ? SupPaperTyps_39[ps] : 0; //} - if (param.filter != 3 || param.enhance_color != 0 || param.hsvcorrect) + if (param.filter != 3 || param.enhance_color != 0 || param.hsvcorrect||param.hsvFilter!=0||param.fadeback!=0) cfg.g200params.color = 1;//color else { @@ -326,6 +326,7 @@ void GScanO1003399::config_params(GScanCap& param) param39.gamma = param.gamma; param39.hardwarecaps = param.hardwarecaps; param39.hsvcorrect = param.hsvcorrect; + param39.HsvFilterType = param.hsvFilter; param39.imageRotateDegree = param.imageRotateDegree; param39.indent = param.indent; param39.is_autocontrast = param.is_autocontrast; diff --git a/huagao/Device/GScanO400.cpp b/huagao/Device/GScanO400.cpp index f0da883d..e15a5739 100644 --- a/huagao/Device/GScanO400.cpp +++ b/huagao/Device/GScanO400.cpp @@ -412,8 +412,45 @@ void GScanO400::config_params(GScanCap& params) } } else -#endif // G400 params.resolution_native = 200.0f; +#elif defined(G300) + if (atoi(fw.substr(4, 6).c_str()) >= 221106) + { + int dpi = 1; + if (params.is_high_imagequality) + { + if (params.resolution_dst < 300) { + params.resolution_native = 200.0f; + dpi = 1; + } + else if (params.resolution_dst >= 300 && params.resolution_dst < 500) { + params.resolution_native = 300.0f; + dpi = 2; + } + else { + params.resolution_native = 600.0f; + dpi = 3; + } + cfgdata = (cfgdata & 0xffffff3f) + (dpi << 6); + } + else + { + if (params.resolution_dst < 500) + { + params.resolution_native = 200.0f; + dpi = 1; + } + else + { + params.resolution_native = 300.0f; + dpi = 2; + } + cfgdata = (cfgdata & 0xffffff3f) + (dpi << 6); + } + } + else + params.resolution_native = 200.0f; +#endif //G300 gcap = params; USBCB usbcb = { CONFIGURED_DATA,cfgdata,0 }; FileTools::writelog(log_INFO, "config hardware param" + to_string(cfgdata)); diff --git a/huagao/Device/ImageMatQueue.cpp b/huagao/Device/ImageMatQueue.cpp index 7a8e3988..e97a6b87 100644 --- a/huagao/Device/ImageMatQueue.cpp +++ b/huagao/Device/ImageMatQueue.cpp @@ -190,7 +190,7 @@ void ImageMatQueue::setparam(const GScanCap& param) #endif bool normalCrop = ((param.autodescrew) || (islongcustomcrop ? islongcustomcrop : param.is_autocrop) || (param.fillbackground)) ? false : param.normalCrop; m_iaList.push_back(shared_ptr(new CImageApplyAutoCrop(islongcustomcrop ? islongcustomcrop : param.is_autocrop, - param.autodescrew, param.fillbackground, cv::Size(fixedSize.cx, fixedSize.cy), param.is_convex, false, param.AutoCrop_threshold, param.noise, param.indent, normalCrop))); + param.autodescrew, param.fillbackground, cv::Size(fixedSize.cx, fixedSize.cy), param.is_convex, false, param.AutoCrop_threshold, param.noise, param.indent, normalCrop,param.hsvFilter==0))); /* m_iaList.push_back(shared_ptr(new CImageApplyAutoCrop(true, param.autodescrew, param.fillbackground, cv::Size(fixedSize.cx, fixedSize.cy), param.is_convex, false, param.AutoCrop_threshold, param.noise, param.indent))); if(!(islongcustomcrop ? islongcustomcrop : param.is_autocrop)) @@ -202,7 +202,8 @@ void ImageMatQueue::setparam(const GScanCap& param) // m_iaList.push_back(shared_ptr(new CImageApplyHSVCorrect(CImageApplyHSVCorrect::CorrectOption::LowSaturation_Removal, true))); //} - if (param.fadeback && param.pixtype == 2) { + if (param.fadeback)//&& param.pixtype == 2 + { m_iaList.push_back(shared_ptr(new CImageApplyFadeBackGroudColor(100,0,param.fadeback_range))); } //filter 0 r 1 g 2 b 3 none enhance color 0 none 1 r 2 g 3 b @@ -225,9 +226,15 @@ void ImageMatQueue::setparam(const GScanCap& param) } //答题卡除红 - if (scanParam.hsvcorrect) + if ((scanParam.hsvcorrect || scanParam.hsvFilter)&&scanParam.pixtype==2) { - m_iaList.push_back(shared_ptr(new CImageApplyHSVCorrect(CImageApplyHSVCorrect::CorrectOption::Red_Removal))); + CImageApplyHSVCorrect::CorrectOption hsv;//= config.hsvcorrect?CImageApplyHSVCorrect::CorrectOption::Red_Removal: + if (scanParam.hsvcorrect) + hsv = CImageApplyHSVCorrect::CorrectOption::Red_Removal; + else + hsv = CImageApplyHSVCorrect::CorrectOption::FXB_Colour_Cast; + + m_iaList.push_back(shared_ptr(new CImageApplyHSVCorrect(hsv))); } //锐化 if (param.sharpen) { @@ -723,7 +730,7 @@ void ImageMatQueue::duplex_process(CacheInfo info) CImageApply* ptr = m_iaList[j].get(); if (typeid(*ptr) == typeid(CImageApplyAutoCrop)) { - rects = dynamic_cast(ptr)->rotatedROIs(); + //rects = dynamic_cast(ptr)->rotatedROIs(); isDesaskew = dynamic_cast(ptr)->isDesaskew(); } else if (typeid(*ptr) == typeid(CImageApplyRotation)) @@ -766,7 +773,7 @@ void ImageMatQueue::duplex_process(CacheInfo info) if (scanParam.pixtype == 1 && mats[i].channels() == 3)//gray cv::cvtColor(mats[i], mats[i], COLOR_BGR2GRAY); #else - if (scanParam.pixtype == 1 && scanParam.hsvcorrect) + if (scanParam.pixtype == 1 && (scanParam.hsvcorrect || scanParam.fadeback))// && scanParam.hsvcorrect if (mats[i].channels() == 3) cvtColor(mats[i], mats[i], cv::COLOR_BGR2GRAY); #endif @@ -837,7 +844,7 @@ void ImageMatQueue::single_process(cv::Mat& mat) for (int i = 0; i < mats.size(); i++) { if (!mats[i].empty()) { IMat2Bmp idata; - if (scanParam.pixtype == 1 && scanParam.hsvcorrect) + if (scanParam.pixtype == 1 && (scanParam.hsvcorrect!=0 || scanParam.fadeback)) if (mats[i].channels() == 3) cvtColor(mats[i], mats[i], cv::COLOR_BGR2GRAY); if (scanParam.en_multi_output) { diff --git a/huagao/Device/PublicFunc.h b/huagao/Device/PublicFunc.h index 0846e6b9..f38765cb 100644 --- a/huagao/Device/PublicFunc.h +++ b/huagao/Device/PublicFunc.h @@ -101,7 +101,7 @@ const std::string INDENT = "Indent"; const std::string AUTOCROP_THRESHOLD = "AutoCrop_Threshold"; const std::string NOISE = "Noise"; const std::string LOWPOWERMODE = "ilowpowermode"; - +const std::string ANSWERSHEETFILTER = "iAnswersheetFilter"; //twain õȲ const std::string ROLLERMSGDATE = "RollerMsgDate"; @@ -145,6 +145,7 @@ typedef struct tagCONFIGPARAMS int OutHoleRatio; bool EnMultiOutPutR; bool EnHsvCorrect; + bool EnHsvFilterDetechNoise; /*ֽѡ*/ bool EnUltrasonicDetect; @@ -300,6 +301,7 @@ struct GScanCap bool en_fillholeratio_left; bool en_fillholeratio_right; int autopaper_timeout; + int hsvFilter; std::string Caption; std::string SavePath; }; @@ -382,7 +384,9 @@ struct GScanCap_3399 int fillholeratio_down; int fillholeratio_left; int fillholeratio_right; - uint32_t reserve[1024]; /**< Ԥ4096ֽЭչ*/ + std::uint8_t fold_concatmode; /*ƴģʽ*/ + int HsvFilterType; /**< ⿨ɫ ݶ0 Ϊرգ1Ϊɫ>*/ + uint32_t reserve[1023]; /**< Ԥ4096ֽЭչ*/ }; typedef struct Paper_Status { diff --git a/huagao/Device/scn_config.cpp b/huagao/Device/scn_config.cpp index 6370f0ca..e87dc82a 100644 --- a/huagao/Device/scn_config.cpp +++ b/huagao/Device/scn_config.cpp @@ -7,7 +7,7 @@ hgConfigClass::hgConfigClass(GScanCap param) m_param = { 0 }; PaperStatus ps = { param.papertype,param.paperAlign }; m_param.paper = ContainspaperTypesKey(ps) ? SupPaperTyps[ps] : 0; - if(param.filter!=3||param.enhance_color!=0||param.hsvcorrect) + if(param.filter!=3||param.enhance_color!=0||param.hsvcorrect|| param.fadeback != 0 || param.hsvFilter != 0) m_param.color = 1;//color else { diff --git a/huagao/GscanJsonConfig.cpp b/huagao/GscanJsonConfig.cpp index 5afb1cb0..cd3bf804 100644 --- a/huagao/GscanJsonConfig.cpp +++ b/huagao/GscanJsonConfig.cpp @@ -945,6 +945,7 @@ json GscanJsonConfig::GscancapToJson(GScanCap& cap) js[CONFIG][ITEMCAPTION] = cap.Caption; js[CONFIG][SAVEPATH] = cap.SavePath.c_str(); js[CONFIG][LOWPOWERMODE] = cap.hardwarecaps.lowpowermode; + js[CONFIG][ANSWERSHEETFILTER] = cap.hsvFilter; return js; } @@ -1019,6 +1020,7 @@ GScanCap GscanJsonConfig::JsonToGscancap(json& js) cap.indent = json_cast(js[CONFIG][INDENT]).to_int(); cap.AutoCrop_threshold = json_cast(js[CONFIG][AUTOCROP_THRESHOLD]).to_int(); cap.hardwarecaps.lowpowermode = (LowPowerMode)json_cast(js[CONFIG][LOWPOWERMODE]).to_int(); + cap.hsvFilter = json_cast(js[CONFIG][ANSWERSHEETFILTER]).to_int(); cap.is_convex = json_cast(js[CONFIG][ISCONVEX]).to_int(); cap.Caption = json_cast(js[CONFIG][ITEMCAPTION]).to_string(); cap.SavePath = json_cast(js[CONFIG][SAVEPATH]).to_string(); @@ -1092,6 +1094,7 @@ json GscanJsonConfig::GetDefaultJson() "AutoCrop_Threshold": 40 , "isConvex": true , "ilowpowermode": 4, + "iAnswersheetFilter": 0, "Caption": "" , "SavePath": "" } @@ -1161,6 +1164,7 @@ json GscanJsonConfig::GetDefaultJson() "AutoCrop_Threshold": 40 , "isConvex": true , "ilowpowermode": 4, + "iAnswersheetFilter": 0, "Caption": "" , "SavePath": "" } diff --git a/huagao/ImageProcess/ImageApplyAutoCrop.cpp b/huagao/ImageProcess/ImageApplyAutoCrop.cpp index 046625cd..923f9128 100644 --- a/huagao/ImageProcess/ImageApplyAutoCrop.cpp +++ b/huagao/ImageProcess/ImageApplyAutoCrop.cpp @@ -1,5 +1,8 @@ #include "ImageApplyAutoCrop.h" #include "ImageProcess_Public.h" +#include +#include +#include "ImageApplyDispersion.h" CImageApplyAutoCrop::CImageApplyAutoCrop() : m_isCrop(false) @@ -11,11 +14,12 @@ CImageApplyAutoCrop::CImageApplyAutoCrop() , 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) + double threshold, int noise, int indent, bool normalCrop, bool dispersion) : m_isCrop(isCrop) , m_isDesaskew(isDesaskew) , m_isFillBlank(isFillBlank) @@ -26,6 +30,7 @@ CImageApplyAutoCrop::CImageApplyAutoCrop(bool isCrop, bool isDesaskew, bool isFi , m_indent(indent) , m_fixedSize(fixedSize) , m_normalCrop(normalCrop) + , m_isDispersion(dispersion) { } @@ -33,313 +38,63 @@ CImageApplyAutoCrop::~CImageApplyAutoCrop() { } -cv::Mat concatenateMatrix(const cv::Mat& first, const cv::Mat& second) -{ - cv::Mat mul1 = cv::Mat::eye(3, 3, CV_64F); - cv::Mat mul2 = 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; - 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]; - 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, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0)); - } - - 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, 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); - 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); - } - - m_maxContour.clear(); - m_maxContour.push_back(cv::Point(0, t_rect.height - 1)); - m_maxContour.push_back(cv::Point(0, 0)); - m_maxContour.push_back(cv::Point(t_rect.width - 1, 0)); - m_maxContour.push_back(cv::Point(t_rect.width - 1, t_rect.height - 1)); - - contours.clear(); - contours.push_back(m_maxContour); - } - - cv::Scalar autoBGColor; - if (m_isFillBlank) - { - if (m_isConvexHull) - { - 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(); - 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 + 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& 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; + (void)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; } } -cv::Scalar CImageApplyAutoCrop::getBackGroudColor(const cv::Mat& image, int total) +#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) { - if (image.channels() == 3) - { - cv::Mat image_bgr[3]; - cv::split(image, image_bgr); + int interpolation = flags; + cv::Mat src = _src.getMat(), M0 = _M0.getMat(); + cv::Mat dst = _dst.getMat(); - 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]); + 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; } - else - return cv::Scalar::all(getBackGroudChannelMean(image, total)); + + 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 CImageApplyAutoCrop::getBackGroudChannelMean(const cv::Mat& gray, int total) +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); @@ -357,10 +112,10 @@ uchar CImageApplyAutoCrop::getBackGroudChannelMean(const cv::Mat& gray, int tota hist_array[i] = hist.at(i, 0); int length = 1; - const int length_max = 255 - m_threshold; + const int length_max = 255 - threshold; while (length < length_max) { - for (size_t i = m_threshold + 1; i < 256 - length; i++) + for (size_t i = threshold + 1; i < 256 - length; i++) { int count = 0; uint pixSum = 0; @@ -377,3 +132,199 @@ uchar CImageApplyAutoCrop::getBackGroudChannelMean(const cv::Mat& gray, int tota } 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; +#define COLOR_SCALE_THRE 0.5 +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 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) + { + cv::Mat mat_dispersion = src(cv::boundingRect(maxContour)); + dispersion_apply.apply(mat_dispersion, 0); + } + + cv::Scalar blankColor; + if (isFillBlank) + if (isColorBlank) + blankColor = getBackGroudColor(resizeMat, rect.size.area() * FX_FY * FX_FY, COLOR_SCALE_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); + } +} diff --git a/huagao/ImageProcess/ImageApplyAutoCrop.h b/huagao/ImageProcess/ImageApplyAutoCrop.h index e1f1f4ae..63fa3616 100644 --- a/huagao/ImageProcess/ImageApplyAutoCrop.h +++ b/huagao/ImageProcess/ImageApplyAutoCrop.h @@ -2,27 +2,31 @@ * ==================================================== * 功能:自动裁剪、纠偏、除黑底 - * 作者:刘丁维 - * 生成时间:2020/4/21 + * 作者:刘丁绿 + * 生成时间_020/4/21 * 最近修改时间:2020/4/21 v1.0 - 2020/7/22 v1.1 增加获取图像有效区域轮廓的接口maxContour(用于配合一体机的“跳过空白页”算法,PC端暂时无需使用) - 2020/10/16 v1.2 修复自动裁剪尺寸精度丢失的BUG;提高除黑底缩进精度。 - 2020/10/28 v1.2.1 修复凹凸多边形填充背景的逻辑BUG。 - 2020/10/28 v1.2.2 修复图像处理必定会缩小尺寸的BUG。 - 2020/10/29 v1.2.3 避免无谓的纠偏(0°纠偏) - 2020/11/30 v1.3.0 增加功能,可识别文稿颜色进行填充黑底。 - 2021/06/18 v1.3.1 调整默认noise为8。 - 2021/07/01 v1.3.2 修复 无裁切情况下,自适应颜色除黑底不生效的BUG。 - 2021/07/08 v1.3.3 完善流程。当无法定位内容时,且为固定幅面裁切,则返回按照固定幅面进行裁切的结果。 - 2021/07/08 v1.3.4 调整参数,让消除背景噪声不对纵向像素造成影响。 - 2021/07/09 v1.3.5 增加normalCrop机制,当m_isCrop m_isDesaskew m_isFillBlank均为false时可选用,实现传统裁切。 - 2021/07/13 v1.3.6 调整normalCrop逻辑,当normalCrop为true时,m_isCrop m_isDesaskew m_isFillBlank失效。 - 2021/07/19 v1.3.7 调整仿射变换模式为INTER_LINEAR。 - 2021/07/22 v1.3.8 修复第二次寻边,找不到外界轮廓会导致崩溃的BUG。 - 2021/08/02 v1.3.9 精细化除黑底算法,可以应对只有条纹内容的黑色图像。 - 2021/10/08 v1.3.10 优化算法,用腐蚀代替绘制实现缩进,提高整体算法效率。 - 2021/10/19 v1.3.11 解决一些极端情况,例如纸张自然0角度,纸张定格在图像顶部的处理。 - * 版本号:v1.3.11 + 2020/7/22 v1.1 增加获取图像有效区域轮廓的接口maxContour(用于配合一体机的“跳过空白页”算法,PC端暂时无需使用_ + 2020/10/16 v1.2 修复自动裁剪尺寸精度丢失的BUG;提高除黑底缩进精度 + 2020/10/28 v1.2.1 修复凹凸多边形填充背景的逻辑BUG + 2020/10/28 v1.2.2 修复图像处理必定会缩小尺寸的BUG + 2020/10/29 v1.2.3 避免无谓的纠偏(0°纠偏_ + 2020/11/30 v1.3.0 增加功能,可识别文稿颜色进行填充黑底 + 2021/06/18 v1.3.1 调整默认noise + 2021/07/01 v1.3.2 修复 无裁切情况下,自适应颜色除黑底不生效的BUG + 2021/07/08 v1.3.3 完善流程。当无法定位内容时,且为固定幅面裁切,则返回按照固定幅面进行裁切的结果 + 2021/07/08 v1.3.4 调整参数,让消除背景噪声不对纵向像素造成影响 + 2021/07/09 v1.3.5 增加normalCrop机制,当m_isCrop m_isDesaskew m_isFillBlank均为false时可选用,实现传统裁切 + 2021/07/13 v1.3.6 调整normalCrop逻辑,当normalCrop为true时,m_isCrop m_isDesaskew m_isFillBlank失效 + 2021/07/19 v1.3.7 调整仿射变换模式为INTER_LINEAR + 2021/07/22 v1.3.8 修复第二次寻边,找不到外界轮廓会导致崩溃的BUG + 2021/08/02 v1.3.9 精细化除黑底算法,可以应对只有条纹内容的黑色图像 + 2021/10/08 v1.3.10 优化算法,用腐蚀代替绘制实现缩进,提高整体算法效率 + 2021/10/19 v1.3.11 解决一些极端情况,例如纸张自然0角度,纸张定格在图像顶部的处理 + 2021/10/19 v1.3.12 微调裁切纠偏像素精度 + 2022/04/24 v1.4 重构算法,增加除色散功能 + 2022/05/03 v1.4.1 完善逻辑 + 2022/06/09 v1.4.2 修复获取文稿底色时,threshold值,设为固定值0.5 + * 版本号:v1.4.2 * ==================================================== */ @@ -37,21 +41,21 @@ class CImageApplyAutoCrop : public CImageApply public: CImageApplyAutoCrop(); - /* - * isCrop [in]:自动幅面裁剪使能,true自动裁剪,false为固定裁剪 + * isCrop [in]:自动幅面裁剪使能,true自动裁剪,false为固定裁剿 * isDesaskew [in]:自动纠偏使能,true自动纠偏,false为不纠偏 * isFillBlank [in]:黑底填充使能,true为填充,false为不填充 - * fixedSize [in]:固定幅面尺寸,当isCrop为false时生效,结果尺寸按fixedSize大小输出,单位像素 + * fixedSize [in]:固定幅面尺寸,当isCrop为false时生效,结果尺寸按fixedSize大小输出,单位像紿 * isConvex [in]:黑底填充时的填充方式,true为凸多边形填充,false为凹多边形填充,默认true * isFillColor [in]:黑底填充时采用自适应色彩填充,false为白色填充,true为自适应文稿底色填充,默认false - * threshold [in]:二值化阈值,取值范围(0, 255),默认40 - * noise [in]:除噪像素,能够消除noise宽度的背景竖条纹干扰,默认2 - * indent [in]:轮廓缩进,裁剪、纠偏或者黑底填充时,对探索到的纸张轮廓进行缩进indent像素,默认5 - * normalCrop [in]:为true且m_isCrop m_isDesaskew m_isFillBlank均为false时生效,固定裁切采用最传统的裁切方式,默认false + * threshold [in]:二值化阈值,取值范囿0, 255),默访0 + * noise [in]:除噪像素,能够消除noise宽度的背景竖条纹干扰,默访 + * indent [in]:轮廓缩进,裁剪、纠偏或者黑底填充时,对探索到的纸张轮廓进行缩进indent像素,默访 + * normalCrop [in]:为true时,m_isCrop m_isDesaskew m_isFillBlank失效,固定裁切采用最传统的裁切方式,默认false + * dispersion [in]:为true时,除色散;false时不除色散。默认为true */ CImageApplyAutoCrop(bool isCrop, bool isDesaskew, bool isFillBlank, const cv::Size& fixedSize, bool isConvex = true, - bool isFillColor = false, double threshold = 40, int noise = 8, int indent = 5, bool normalCrop = false); + bool isFillColor = false, double threshold = 40, int noise = 8, int indent = 5, bool normalCrop = false, bool dispersion = true); virtual ~CImageApplyAutoCrop(); @@ -67,12 +71,10 @@ public: bool isConvexHull() { return m_isConvexHull; } + cv::RotatedRect getROI() { return m_rect; } + double threshold() { return m_threshold; } - cv::RotatedRect& rotatedROI() { return m_rect; } - - const std::vector& rotatedROIs() { return m_rects; } - int noise() { return m_noise; } int indent() { return m_indent; } @@ -97,10 +99,7 @@ public: void setFixedSize(cv::Size size) { m_fixedSize = size; } -private: - cv::Scalar getBackGroudColor(const cv::Mat& image, int total); - - uchar getBackGroudChannelMean(const cv::Mat& gray, int total); + void setDispersion(bool enable) { m_isDispersion = enable; } private: bool m_isCrop; @@ -108,16 +107,19 @@ private: bool m_isFillBlank; bool m_isConvexHull; bool m_isFillColor; + bool m_isDispersion; double m_threshold; int m_noise; int m_indent; - bool m_normalCrop; //为true且m_isCrop m_isDesaskew m_isFillBlank均为false时生效,固定裁切采用最传统的裁切方式 + bool m_normalCrop; //为true且m_isCrop m_isDesaskew m_isFillBlank均为false时生效,固定裁切采用最传统的裁切方弿 cv::Size m_fixedSize; cv::RotatedRect m_rect; - std::vector m_maxContour; std::vector m_rects; + std::vector m_maxContour; }; -#endif // !IMAGE_APPLY_AUTO_CROP_H +void autoCrop_desaskew_fillBlank(const cv::Mat& src, cv::Mat& dst, bool isAutoCrop, bool isDesaskew, bool isFillBlank, int dWidth, int dHeight, + bool isConvex = true, bool isColorBlank = false, double threshold = 40, int noise = 8, int indent = 5, bool isNormalCrop = false, bool dispersion = true); +#endif // !IMAGE_APPLY_AUTO_CROP_H diff --git a/huagao/ImageProcess/ImageApplyHSVCorrect.cpp b/huagao/ImageProcess/ImageApplyHSVCorrect.cpp index 9580057b..2cb4673f 100644 --- a/huagao/ImageProcess/ImageApplyHSVCorrect.cpp +++ b/huagao/ImageProcess/ImageApplyHSVCorrect.cpp @@ -1,164 +1,173 @@ #include "ImageApplyHSVCorrect.h" #include -CImageApplyHSVCorrect::CImageApplyHSVCorrect(CorrectOption mode, bool cvtColor, uint bgr) - : m_table(new uint[256 * 256 * 256]) +CImageApplyHSVCorrect::CImageApplyHSVCorrect(CorrectOption mode, bool cvtColor, uint bgr, uint alpha) + : m_table(new uint[256 * 256 * 256]) { - initLUT(); - switch (mode) - { - case CImageApplyHSVCorrect::Red_Removal: - set_HSV_value(std::pair(0, 63), std::pair(30, 255), std::pair(120, 255), bgr, cvtColor); - set_HSV_value(std::pair(200, 255), std::pair(30, 255), std::pair(120, 255), bgr, cvtColor); - break; - case CImageApplyHSVCorrect::LowSaturation_Removal: - set_HSV_value(std::pair(0, 255), std::pair(0, 30), std::pair(0, 255), bgr, cvtColor); - break; - default: - break; - } + initLUT(); + uint temp; + switch (mode) + { + case CImageApplyHSVCorrect::Red_Removal: + set_HSV_value(std::pair(0, 63), std::pair(30, 255), std::pair(120, 255), bgr, cvtColor); + set_HSV_value(std::pair(200, 255), std::pair(30, 255), std::pair(120, 255), bgr, cvtColor); + break; + case CImageApplyHSVCorrect::LowSaturation_Removal: + if (alpha < 0) + temp = 35; + else + temp = alpha; + set_HSV_value(std::pair(0, 255), std::pair(0, temp), std::pair(0, 255), bgr, cvtColor); + break; + case CImageApplyHSVCorrect::FXB_Colour_Cast: + set_HSV_value(std::pair(45, 105), std::pair(0, 255), std::pair(0, 255), 0x00FFFFFF, true); + set_HSV_value(std::pair(180, 235), std::pair(0, 255), std::pair(0, 255), 0x00FFFFFF, true); + set_HSV_value(std::pair(0, 30), std::pair(0, 50), std::pair(0, 255), 0x00FFFFFF, true); + break; + default: + break; + } } CImageApplyHSVCorrect::~CImageApplyHSVCorrect() { - delete[] m_table; + delete[] m_table; } void CImageApplyHSVCorrect::apply(cv::Mat& pDib, int side) { - (void)side; - if (pDib.empty() || pDib.channels() != 3) return; + (void)side; + if (pDib.empty() || pDib.channels() != 3) return; #if 0 - uchar* src = pDib.data; - cv::Mat z = cv::Mat::zeros(pDib.size(), CV_8UC3); - uchar* dst = z.data; + uchar* src = pDib.data; + cv::Mat z = cv::Mat::zeros(pDib.size(), CV_8UC3); + uchar* dst = z.data; - int bytesPerLine = pDib.cols * pDib.channels(); - for (size_t i = 0, rows = pDib.rows; i < rows; i++) - { - uchar* ptr = pDib.ptr(i); - for (size_t j = 0, cols = pDib.cols; j < cols; j++) - { - int offset = i * 3; - int index = *reinterpret_cast(ptr + offset) & 0x00ffffff; - uint color = m_table[index]; - *reinterpret_cast(dst + offset) |= color; - } - } - pDib = z; + int bytesPerLine = pDib.cols * pDib.channels(); + for (size_t i = 0, rows = pDib.rows; i < rows; i++) + { + uchar* ptr = pDib.ptr(i); + for (size_t j = 0, cols = pDib.cols; j < cols; j++) + { + int offset = i * 3; + int index = *reinterpret_cast(ptr + offset) & 0x00ffffff; + uint color = m_table[index]; + *reinterpret_cast(dst + offset) |= color; + } + } + pDib = z; #else - cv::Mat bgra; - cv::cvtColor(pDib, bgra, cv::COLOR_BGR2BGRA); + cv::Mat bgra; + cv::cvtColor(pDib, bgra, cv::COLOR_BGR2BGRA); - long total = bgra.total(); - uint* ptr = bgra.ptr(); - for (long i = 0; i < total; i++) - ptr[i] = m_table[ptr[i] & 0x00FFFFFF]; + long total = bgra.total(); + uint* ptr = bgra.ptr(); + for (long i = 0; i < total; i++) + ptr[i] = m_table[ptr[i] & 0x00FFFFFF]; - cv::cvtColor(bgra, pDib, cv::COLOR_BGRA2BGR); + cv::cvtColor(bgra, pDib, cv::COLOR_BGRA2BGR); #endif } void CImageApplyHSVCorrect::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)isTwoSide; + int i = 0; + for (cv::Mat& var : mats) { + if (i != 0 && isTwoSide == false) + break; + if (!var.empty()) + apply(var, 0); + i++; + } } void CImageApplyHSVCorrect::initLUT() { #if 0 - uchar h, s, v; + uchar h, s, v; #endif - for (uint b = 0; b < 256; b++) - for (uint g = 0; g < 256; g++) - for (uint r = 0; r < 256; r++) - { + for (uint b = 0; b < 256; b++) + for (uint g = 0; g < 256; g++) + for (uint r = 0; r < 256; r++) + { #if 0 - RGB_2_HSV_full(r, g, b, h, s, v); + RGB_2_HSV_full(r, g, b, h, s, v); - uint index = b | (g << 8) | (r << 16); - if (h < 12 || h > 245) - m_table[index] = index & 0x00ffffff; - else - m_table[index] = (v | (v << 8) | (v << 16)) & 0x00ffffff; + uint index = b | (g << 8) | (r << 16); + if (h < 12 || h > 245) + m_table[index] = index & 0x00ffffff; + else + m_table[index] = (v | (v << 8) | (v << 16)) & 0x00ffffff; #else - m_table[b | (g << 8) | (r << 16)] = b | (g << 8) | (r << 16); + m_table[b | (g << 8) | (r << 16)] = b | (g << 8) | (r << 16); #endif - } + } } void CImageApplyHSVCorrect::set_single(const uint src_b, const uint src_g, const uint src_r, - const uint dst_b, const uint dst_g, const uint dst_r) + const uint dst_b, const uint dst_g, const uint dst_r) { - m_table[src_b | (src_g << 8) | (src_r << 16)] = dst_b | (dst_g << 8) | (dst_r << 16); + m_table[src_b | (src_g << 8) | (src_r << 16)] = dst_b | (dst_g << 8) | (dst_r << 16); } void CImageApplyHSVCorrect::set_HSV_value(const std::pair& range_h, - const std::pair& range_s, - const std::pair& range_v, - uint bgr, bool cvtGray) + const std::pair& range_s, + const std::pair& range_v, + uint bgr, bool cvtGray) { - uchar h, s, v; - for (int b = 0; b < 256; b++) - for (int g = 0; g < 256; g++) - for (int r = 0; r < 256; r++) - { - RGB_2_HSV_full(r, g, b, h, s, v); - if (contained(h, range_h) && contained(s, range_s) && contained(v, range_v)) - { - if (cvtGray) - { - int a = (b + g + r) / 3 * 0x00010101; - m_table[(b | (g << 8) | (r << 16)) & 0x00ffffff] = (b + g + r) / 3 * 0x00010101; - } - else - m_table[(b | (g << 8) | (r << 16)) & 0x00ffffff] = bgr & 0x00ffffff; - } - } + uchar h, s, v; + for (int b = 0; b < 256; b++) + for (int g = 0; g < 256; g++) + for (int r = 0; r < 256; r++) + { + RGB_2_HSV_full(r, g, b, h, s, v); + if (contained(h, range_h) && contained(s, range_s) && contained(v, range_v)) + { + if (cvtGray) + { + int a = (b + g + r) / 3 * 0x00010101; + m_table[(b | (g << 8) | (r << 16)) & 0x00ffffff] = (b + g + r) / 3 * 0x00010101; + } + else + m_table[(b | (g << 8) | (r << 16)) & 0x00ffffff] = bgr & 0x00ffffff; + } + } } void CImageApplyHSVCorrect::set_table(const uint* table) { - memcpy(m_table, table, 256 * 256 * 256); + memcpy(m_table, table, 256 * 256 * 256 * sizeof(uint)); } bool CImageApplyHSVCorrect::contained(uchar value, const std::pair& range) { - return value >= range.first && value <= range.second; + return value >= range.first && value <= range.second; } void CImageApplyHSVCorrect::RGB_2_HSV_full(int r, int g, int b, uchar& h, uchar& s, uchar& v) { - int minn = cv::min(r, cv::min(g, b)); - int maxx = cv::max(r, cv::max(g, b)); - v = static_cast(maxx); //V + int minn = cv::min(r, cv::min(g, b)); + int maxx = cv::max(r, cv::max(g, b)); + v = static_cast(maxx); //V - int delta = maxx - minn; - float _h; - if (maxx == 0) - { - h = s = v = 0; - return; - } - else - s = static_cast(delta * 255 / maxx); + int delta = maxx - minn; + float _h; + if (maxx == 0) + { + h = s = v = 0; + return; + } + else + s = delta; - if (r == maxx) - _h = static_cast(g - b) / static_cast(delta); - else if (g == maxx) - _h = 2 + static_cast(b - r) / static_cast(delta); - else - _h = 4 + static_cast(r - g) / static_cast(delta); - - float __h = _h * 42.6666666667f; - h = (__h >= 0) ? static_cast(__h) : static_cast(__h + 256); -} + if (r == maxx) + _h = static_cast(g - b) / static_cast(delta); + else if (g == maxx) + _h = 2 + static_cast(b - r) / static_cast(delta); + else + _h = 4 + static_cast(r - g) / static_cast(delta); + float __h = _h * 42.6666666667f; + h = (__h >= 0) ? static_cast(__h) : static_cast(__h + 256); +} \ No newline at end of file diff --git a/huagao/ImageProcess/ImageApplyHSVCorrect.h b/huagao/ImageProcess/ImageApplyHSVCorrect.h index 2e5806d3..7336bdac 100644 --- a/huagao/ImageProcess/ImageApplyHSVCorrect.h +++ b/huagao/ImageProcess/ImageApplyHSVCorrect.h @@ -4,12 +4,16 @@ * ܣɫͼɫУLUTʵ֣ԤBGRֵHVSBGRԭͼвֵУ * ߣά * ʱ䣺2020/3/21 - * ޸ʱ䣺v1.0 2020/03/21 - v1.1 2020/06/15 ЧHSVȡֵΧ - v1.2 2021/08/02 ڴָ룬ӦROIͼڴƫơ - v1.3 2021/08/26 滻⿨Red_Removalʵַ - v1.4 2022/04/22 ӹܣ֧ػҶֵԭɫأɾĬϹ캯УѡDeafaultLowSaturation_Removal - * 汾ţv1.4 + * ޸ʱ䣺v1.0 2020/03/21 + v1.1 2020/06/15 ЧHSVȡֵΧ + v1.2 2021/08/02 ڴָ룬ӦROIͼڴƫơ + v1.3 2021/08/26 滻⿨Red_Removalʵַ + v1.4 2022/04/22 ӹܣ֧ػҶֵԭɫأɾĬϹ캯УѡDeafaultLowSaturation_Removal + v1.4.1 2022/04/25 ͣݷԽ硣 + v1.4.2 2022/06/09 ޸һڴСĴ + v1.5 2022/08/22 Ͷȼ㷽ʽ캯Ӳalpha΢ģʽIJ + v1.5.1 2022/09/23 ӷƫɫԤ跽 + * 汾ţv1.5 * * ==================================================== */ @@ -19,22 +23,24 @@ #include "ImageApply.h" -class CImageApplyHSVCorrect : public CImageApply +class CImageApplyHSVCorrect : public CImageApply { public: enum CorrectOption { Deafault, //ĬϣκγɫЧ LowSaturation_Removal, //ͱͶ - Red_Removal //ɫɫH:[0, 85][170, 255],S:[10, 255],V:[120,255] + Red_Removal, //ɫɫH:[0, 85][170, 255],S:[10, 255],V:[120,255] + FXB_Colour_Cast //ƫɫ }; public: /* * mode [in]:Ԥɫģʽ * cvtColor [in]:ʹĬֵʹûҶֵtrueΪҶֵfalseΪĬֵ - * bgr:[in] uintʾBGRֵBڵλRڸλcvtGray ΪfalseʱЧ) + * bgr [in] uintʾBGRֵBڵλRڸλcvtGray ΪfalseʱЧ) + * alpha [int] ڵLowSaturation_RemovalģʽǿȡalphaΪʱͶĬΪ[0, 35]alphaΪǸʱͶΪ[0, alpha] */ - CImageApplyHSVCorrect(CorrectOption mode = CorrectOption::Deafault, bool cvtColor = false, uint bgr = 0x00FFFFFF); + CImageApplyHSVCorrect(CorrectOption mode = CorrectOption::Deafault, bool cvtColor = false, uint bgr = 0x00FFFFFF, uint alpha = -1); virtual ~CImageApplyHSVCorrect(); @@ -90,5 +96,4 @@ private: uint* m_table; }; -#endif - +#endif \ No newline at end of file diff --git a/huagao/huagaods.cpp b/huagao/huagaods.cpp index 04a506d4..65078a1c 100644 --- a/huagao/huagaods.cpp +++ b/huagao/huagaods.cpp @@ -72,6 +72,7 @@ enum class CapTypeEx : unsigned short { TwEx_IEnMultiOutPutType = 0x8113, TwEx_IFixedPaper = 0x8114, TwEx_IHighImageQuality = 0x8115, + TwEx_IHsvFilter = 0x8116 }; enum class PaperSizeEx : unsigned short { @@ -1199,6 +1200,8 @@ Result HuagaoDs::identityOpenDs(const Identity&) { m_scanparam->multi_output_red = 0;//Dzɫģʽ¶ m_scanparam->detachnoise.is_detachnoise = false; m_scanparam->fadeback = false; + m_scanparam->hsvFilter = 0; + m_scanparam->hsvcorrect = 0; //if (mech == PixelType::BlackWhite) //{ // if (m_scanparam->filter == (uint8)Filter::None && m_scanparam->enhance_color == Enchace_Color::Enhance_None) @@ -2287,6 +2290,21 @@ Result HuagaoDs::identityOpenDs(const Identity&) { } return CapSupGetAllResetEx(msg, data, m_scanparam->is_split, false); }; + + m_query[(CapType)(CapTypeEx::TwEx_IHsvFilter)] = msgSupportGetAllSetReset; + m_caps[(CapType)(CapTypeEx::TwEx_IHsvFilter)] = [this](Msg msg, Capability& data)->Result { + //CapabilityPrintf(msg, enum2str(CapTypeEx::TwEx_CropModel), msg == Msg::Set ? to_string((float)data.currentItem()) : ""); + if (Msg::Set == msg) { + auto mech = data.currentItem(); + if (m_scanparam->pixtype != 2)//color + return badValue(); + + m_scanparam->hsvFilter = mech?1:0; + return success(); + } + bool hsv_v=(bool)(m_scanparam->hsvFilter); + return CapSupGetAllResetEx(msg, data, hsv_v, false); + }; #ifdef UV m_query[(CapType)(CapTypeEx::TwEx_UVModel)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_UVModel)] = [this](Msg msg, Capability& data)->Result { diff --git a/huagao/huagaotwds.rc b/huagao/huagaotwds.rc index daebd817..d26f6e0a 100644 Binary files a/huagao/huagaotwds.rc and b/huagao/huagaotwds.rc differ diff --git a/huagao/resource.h b/huagao/resource.h index 1ccf0b72..f19d7c12 100644 Binary files a/huagao/resource.h and b/huagao/resource.h differ diff --git a/huagao/stdafx.h b/huagao/stdafx.h index 72789f41..6b01789f 100644 Binary files a/huagao/stdafx.h and b/huagao/stdafx.h differ