调整穿孔UI,更新算法

This commit is contained in:
masayume 2022-09-20 09:08:11 +08:00
parent e964c60148
commit 6d233d10be
20 changed files with 605 additions and 315 deletions

View File

@ -38,7 +38,7 @@
6.添加待纸扫描协议
7.开放G300最大尺寸和最大尺寸自动裁切
8.修复上批次图像未上传,下次扫描多上传问题
2020年4月18日
2022年4月18日
版本3.3.5.8
1.折角检测默认值设置为70
2.dpi输入框添加
@ -58,7 +58,9 @@
16.协议问题排查
17.完成安卓3399适配
18.取消UI置顶
2020年8月10日
2022年8月10日
版本3.3.5.9
1.调整usb通信加长readbulk超时时间
2.屏蔽3288 自适应幅面功能
3.修复显示UI时通过协议reset停止扫描时按钮未正常复位问题
4.更新除穿孔以及跳过空白页算法

142
huagao/CFillHoleDlg.cpp Normal file
View File

@ -0,0 +1,142 @@
// CFillHoleDlg.cpp: 实现文件
//
#include "stdafx.h"
#include "CFillHoleDlg.h"
#include "afxdialogex.h"
#include "resource.h"
// CFillHoleDlg 对话框
IMPLEMENT_DYNAMIC(CFillHoleDlg, CDialogEx)
CFillHoleDlg::CFillHoleDlg(int up, int down, int left, int right, bool en_up, bool en_down, bool en_left, bool en_right, CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_FILLHOLE, pParent),m_up(up),m_down(down),m_left(left),m_right(right)
,m_en_up(en_up),m_en_down(en_down),m_en_left(en_left),m_en_right(en_right)
{
}
CFillHoleDlg::~CFillHoleDlg()
{
}
void CFillHoleDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDITHOLE_UP, m_Edit_Up);
DDX_Control(pDX, IDC_EDITHOLE_DOWN, m_Edit_Down);
DDX_Control(pDX, IDC_EDITHOLE_LEFT, m_Edit_Left);
DDX_Control(pDX, IDC_EDITHOLE_RIGHT, m_Edit_Right);
DDX_Control(pDX, IDC_SLDHOLE_UP, m_Slider_Up);
DDX_Control(pDX, IDC_SLDHOLE_DOWN, m_Slider_Down);
DDX_Control(pDX, IDC_SLDHOLE_LEFT, m_Slider_Left);
DDX_Control(pDX, IDC_SLDHOLE_RIGHT, m_Slider_Right);
}
BOOL CFillHoleDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
m_Edit_Up.SetSlideLink(this, IDC_SLDHOLE_UP);
m_Edit_Up.SetParams(0.0, 0.49, 10, _T("%0.2f"), 20);
m_Edit_Up.SetValue(m_en_up ? m_up/100.0f : 0.0);
m_Edit_Down.SetSlideLink(this, IDC_SLDHOLE_DOWN);
m_Edit_Down.SetParams(0.0, 0.49, 10, _T("%0.2f"), 20);
m_Edit_Down.SetValue(m_en_down ? m_down/100.0f : 0.0);
// setup third slider-edit box - floating point
m_Edit_Left.SetSlideLink(this, IDC_SLDHOLE_LEFT);
m_Edit_Left.SetParams(0.0, 0.49, 10, _T("%0.2f"), 20);
m_Edit_Left.SetValue(m_en_left ? m_left/100.0f : 0.0);
m_Edit_Right.SetSlideLink(this, IDC_SLDHOLE_RIGHT);
m_Edit_Right.SetParams(0.0, 0.49, 10, _T("%0.2f"), 20);
m_Edit_Right.SetValue(m_en_right ? m_right/100.0f : 0.0);
((CButton*)GetDlgItem(IDC_CKHOLE_UP))->SetCheck(m_en_up);
m_Edit_Up.EnableWindow(m_en_up);
m_Slider_Up.EnableWindow(m_en_up);
((CButton*)GetDlgItem(IDC_CKHOLE_DOWN))->SetCheck(m_en_down);
m_Edit_Down.EnableWindow(m_en_down);
m_Slider_Down.EnableWindow(m_en_down);
((CButton*)GetDlgItem(IDC_CKHOLE_RIGHT))->SetCheck(m_en_right);
m_Edit_Right.EnableWindow(m_en_right);
m_Slider_Right.EnableWindow(m_en_right);
((CButton*)GetDlgItem(IDC_CKHOLE_LEFT))->SetCheck(m_en_left);
m_Edit_Left.EnableWindow(m_en_left);
m_Slider_Left.EnableWindow(m_en_left);
return true;
}
void CFillHoleDlg::OnClose(){
CString text;
m_Edit_Up.GetWindowText(text);
m_up = _wtof(text.GetBuffer()) * 100.0;
m_Edit_Down.GetWindowText(text);
m_down = _wtof(text.GetBuffer()) * 100.0;
m_Edit_Left.GetWindowText(text);
m_left = _wtof(text.GetBuffer()) * 100.0;
m_Edit_Right.GetWindowText(text);
m_right = _wtof(text.GetBuffer()) * 100.0;
CDialogEx::OnClose();
}
BEGIN_MESSAGE_MAP(CFillHoleDlg, CDialogEx)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_CKHOLE_UP, &CFillHoleDlg::OnBnClickedCkholeUp)
ON_BN_CLICKED(IDC_CKHOLE_DOWN, &CFillHoleDlg::OnBnClickedCkholeDown)
ON_BN_CLICKED(IDC_CKHOLE_LEFT, &CFillHoleDlg::OnBnClickedCkholeLeft)
ON_BN_CLICKED(IDC_CKHOLE_RIGHT, &CFillHoleDlg::OnBnClickedCkholeRight)
END_MESSAGE_MAP()
// CFillHoleDlg 消息处理程序
void CFillHoleDlg::OnBnClickedCkholeUp()
{
// TODO: 在此添加控件通知处理程序代码
m_en_up = ((CButton*)GetDlgItem(IDC_CKHOLE_UP))->GetCheck();
m_Edit_Up.EnableWindow(m_en_up);
m_Slider_Up.EnableWindow(m_en_up);
if(!m_en_up)
m_Edit_Up.SetValue(0.0);
}
void CFillHoleDlg::OnBnClickedCkholeDown()
{
// TODO: 在此添加控件通知处理程序代码
m_en_down = ((CButton*)GetDlgItem(IDC_CKHOLE_DOWN))->GetCheck();
m_Edit_Down.EnableWindow(m_en_down);
m_Slider_Down.EnableWindow(m_en_down);
if (!m_en_down)
m_Edit_Down.SetValue(0.0);
}
void CFillHoleDlg::OnBnClickedCkholeLeft()
{
// TODO: 在此添加控件通知处理程序代码
m_en_left = ((CButton*)GetDlgItem(IDC_CKHOLE_LEFT))->GetCheck();
m_Edit_Left.EnableWindow(m_en_left);
m_Slider_Left.EnableWindow(m_en_left);
if (!m_en_left)
m_Edit_Left.SetValue(0.0);
}
void CFillHoleDlg::OnBnClickedCkholeRight()
{
// TODO: 在此添加控件通知处理程序代码
m_en_right = ((CButton*)GetDlgItem(IDC_CKHOLE_RIGHT))->GetCheck();
m_Edit_Right.EnableWindow(m_en_right);
m_Slider_Right.EnableWindow(m_en_right);
if (!m_en_right)
m_Edit_Right.SetValue(0.0);
}

44
huagao/CFillHoleDlg.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include "Sliders.h"
#include "SmartEdit.h"
// CFillHoleDlg 对话框
class CFillHoleDlg : public CDialogEx
{
DECLARE_DYNAMIC(CFillHoleDlg)
public:
CFillHoleDlg(int up, int down, int left,int right,bool en_up,bool en_down,bool en_left,bool en_right,CWnd* pParent = nullptr); // 标准构造函数
virtual ~CFillHoleDlg();
CSmartEdit m_Edit_Up;
CSmartEdit m_Edit_Down;
CSmartEdit m_Edit_Left;
CSmartEdit m_Edit_Right;
CLinkSlider m_Slider_Up;
CLinkSlider m_Slider_Down;
CLinkSlider m_Slider_Left;
CLinkSlider m_Slider_Right;
bool m_en_up;
bool m_en_down;
bool m_en_left;
bool m_en_right;
int m_up;
int m_down;
int m_left;
int m_right;
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_FILLHOLE };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
virtual BOOL OnInitDialog();
virtual void OnClose();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedCkholeUp();
afx_msg void OnBnClickedCkholeDown();
afx_msg void OnBnClickedCkholeLeft();
afx_msg void OnBnClickedCkholeRight();
};

View File

@ -6,7 +6,7 @@
#include "afxdialogex.h"
#include "resource.h"
#include <vector>
#include "CFillHoleDlg.h"
static std::vector<CString> filters = {
@ -216,6 +216,7 @@ BEGIN_MESSAGE_MAP(CImageProcPage, CTabPageSSL)
ON_BN_CLICKED(IDC_CKBACKGROUNDSMOOTH, &CImageProcPage::OnBnClickedCkbackgroundsmooth)
ON_BN_CLICKED(IDC_CHMULTIPUT, &CImageProcPage::OnBnClickedChmultiput)
ON_BN_CLICKED(IDC_CKMULTIOUTPUT, &CImageProcPage::OnBnClickedCkmultioutput)
ON_BN_CLICKED(IDC_BTNHOLE, &CImageProcPage::OnBnClickedBtnhole)
END_MESSAGE_MAP()
void CImageProcPage::EnableOutHoleCheckChanged(BOOL enable)
@ -225,6 +226,7 @@ void CImageProcPage::EnableOutHoleCheckChanged(BOOL enable)
GetDlgItem(IDC_EDIT_HOLE)->EnableWindow(enable);
GetDlgItem(IDC_SLDOUTHOLE)->EnableWindow(enable);
GetDlgItem(IDC_EDIT_HOLE)->EnableWindow(enable);
}
void CImageProcPage::SetOutHoleValue(int value)
@ -259,6 +261,7 @@ void CImageProcPage::OnCtrlChange()
UpdateData();
m_edit_hole.EnableWindow(m_ckbRemoveHole);
m_slider_hole.EnableWindow(m_ckbRemoveHole);
GetDlgItem(IDC_BTNHOLE)->EnableWindow(m_ckbRemoveHole);
}
@ -380,3 +383,19 @@ void CImageProcPage::OnBnClickedCkmultioutput()
}
}
}
void CImageProcPage::OnBnClickedBtnhole()
{
// TODO: 在此添加控件通知处理程序代码
CFillHoleDlg holedlg(m_fillhole_up, m_fillhole_down, m_fillhole_left, m_fillhole_right,m_en_fillhole_up,m_en_fillhole_down,m_en_fillhole_left,m_en_fillhole_right, this);
holedlg.DoModal();
m_fillhole_up = holedlg.m_up;
m_fillhole_down = holedlg.m_down;
m_fillhole_left = holedlg.m_left;
m_fillhole_right = holedlg.m_right;
m_en_fillhole_up = holedlg.m_en_up;
m_en_fillhole_down = holedlg.m_en_down;
m_en_fillhole_left = holedlg.m_en_left;
m_en_fillhole_right = holedlg.m_en_right;
}

View File

@ -35,6 +35,14 @@ public:
void EnableOutHoleCheckChanged(BOOL enable);
void SetOutHoleValue(int value);
void SetMultiOutputEnable(int pixtype, bool enable);
int m_fillhole_up;
int m_fillhole_down;
int m_fillhole_left;
int m_fillhole_right;
int m_en_fillhole_up;
int m_en_fillhole_down;
int m_en_fillhole_left;
int m_en_fillhole_right;
int detachnoise;
int indent;
int noise;
@ -69,4 +77,5 @@ public:
afx_msg void OnBnClickedCkbackgroundsmooth();
afx_msg void OnBnClickedChmultiput();
afx_msg void OnBnClickedCkmultioutput();
afx_msg void OnBnClickedBtnhole();
};

View File

@ -283,6 +283,15 @@ void CTwainUI::UpdateUI()
m_pageImageProc->m_ckbRemoveHole = settings->fillhole.is_fillhole==TRUE?TRUE:FALSE;//除穿孔可用性
m_pageImageProc->m_edit_hole.SetValue(settings->fillhole.fillholeratio/ 100.0);//穿孔搜索范围比例系数
m_pageImageProc->m_edit_hole.EnableWindow(settings->fillhole.is_fillhole == TRUE ? TRUE : FALSE);
m_pageImageProc->GetDlgItem(IDC_BTNHOLE)->EnableWindow(settings->fillhole.is_fillhole == TRUE ? TRUE : FALSE);
m_pageImageProc->m_fillhole_up = settings->fillholeratio_up;
m_pageImageProc->m_fillhole_down = settings->fillholeratio_down;
m_pageImageProc->m_fillhole_left = settings->fillholeratio_left;
m_pageImageProc->m_fillhole_right = settings->fillholeratio_right;
m_pageImageProc->m_en_fillhole_up = settings->en_fillholeratio_up;
m_pageImageProc->m_en_fillhole_down = settings->en_fillholeratio_down;
m_pageImageProc->m_en_fillhole_left = settings->en_fillholeratio_left;
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;
((CButton*)m_pageImageProc->GetDlgItem(IDC_CKBACKGROUNDSMOOTH))->SetCheck(m_pageImageProc->m_ckbfadeback);
@ -652,6 +661,14 @@ void CTwainUI::UpDateScanParam(PCONFIGPARAMS configItem, bool updateDs)
settings->fadeback_range = m_pageImageProc->m_slider_faderange.m_iPosition;
settings->fillhole.is_fillhole = configItem->EnOutHole;
settings->fillhole.fillholeratio = configItem->OutHoleRatio;
settings->fillholeratio_up = m_pageImageProc->m_fillhole_up;
settings->fillholeratio_down = m_pageImageProc->m_fillhole_down;
settings->fillholeratio_left = m_pageImageProc->m_fillhole_left;
settings->fillholeratio_right = m_pageImageProc->m_fillhole_right;
settings->en_fillholeratio_up = m_pageImageProc->m_en_fillhole_up;
settings->en_fillholeratio_down = m_pageImageProc->m_en_fillhole_down;
settings->en_fillholeratio_left = m_pageImageProc->m_en_fillhole_left;
settings->en_fillholeratio_right = m_pageImageProc->m_en_fillhole_right;
settings->multi_output_red = configItem->EnMultiOutPutR;
settings->en_multi_output = ((CButton*)m_pageImageProc->GetDlgItem(IDC_CHMULTIPUT))->GetCheck();
settings->multioutput = ((CComboBox*)m_pageImageProc->GetDlgItem(IDC_CMBMULTIOUT))->GetCurSel();

View File

@ -356,6 +356,11 @@ void GScanO1003399::config_params(GScanCap& param)
param39.fadeback = param.fadeback;
param39.fadebackrange = param.fadeback_range;
param39.dogeardistabce = param.dogeardistance;
param39.fillholeratio_up = param.fillholeratio_up;
param39.fillholeratio_down = param.fillholeratio_down;
param39.fillholeratio_left = param.fillholeratio_left;
param39.fillholeratio_right = param.fillholeratio_right;
param39.fillhole.fillholeratio = std::max(std::max(std::max(param.fillholeratio_down, param.fillholeratio_up), std::max(param.fillholeratio_left, param.fillholeratio_right)),1);
m_usb->write_bulk(&param39, sizeof(param39));
m_param .resolution_native = param.resolution_dst >= 500.0f ? 300.0f : 200.0f;

View File

@ -197,6 +197,8 @@ enum Scanner_Reg_Defs
SR_SET_AUTOMATICCONTROLFEEDMODE_ENABLE,
SR_GET_AUTOMATICCONTROLFEEDMODE_THRESHOLD,
SR_SET_AUTOMATICCONTROLFEEDMODE_THRESHOLD,
SR_GET_TRAYPOSITION,
SR_SET_TRAYPOSITION,
SR_GET_CUO_ERROR = 0x50,
SR_GET_DOU_ERROR,
SR_GET_JAM_ERROR,

View File

@ -165,57 +165,18 @@ void ImageMatQueue::setparam(const GScanCap& param)
m_iaList.clear();
if (scanParam.fillhole.is_fillhole && scanParam.resolution_dst < 500) {
float ratio = scanParam.fillhole.fillholeratio / 100.0;
m_iaList.push_back(shared_ptr<CImageApply>(new CImageApplyOutHole(25, ratio, 40)));
if (scanParam.fillhole.is_fillhole) {
m_iaList.push_back(shared_ptr<CImageApply>(new CImageApplyOutHole(25,
{scanParam.fillholeratio_up/100.0f,scanParam.fillholeratio_down/100.0f,scanParam.fillholeratio_left/100.0f,scanParam.fillholeratio_right/100.0f},
40)));
} //确保能够获取正反两面图
if (param.is_autodiscradblank_normal || param.is_autodiscradblank_vince) {
//m_iaList.push_back(shared_ptr<CImageApply>(new CImageApplyDiscardBlank()));
CImageApplyDiscardBlank* disBlank = new CImageApplyDiscardBlank();
//跳过空白页阈值
int area = 200;
int intensity = 15;
int maxHeight = 3307;//A3 height
//页面最大高度获取
if (param.papertype == TwSS::MaxSize)
maxHeight = 6614;
//阈值参数赋值
if (param.discardblank_percent < 10) {
area = 70 + (int)((param.discardblank_percent - 1) * 13.33);
intensity = 8 + param.discardblank_percent / 2;
}
else if (param.discardblank_percent < 20) {
area = 190 + (param.discardblank_percent - 10) * 14;
intensity = 15;
}
else if (param.discardblank_percent < 40) {
area = 400 + (param.discardblank_percent - 20) * 10;
intensity = 20;
}
else if (param.discardblank_percent < 60) {
area = 600 + (param.discardblank_percent - 40) * 20;
intensity = 20;
}
else if (param.discardblank_percent < 80) {
area = 1000 + (param.discardblank_percent - 60) * 55;
intensity = 30;
}
else {
area = 2100 + (param.discardblank_percent - 80) * (maxHeight - 2100) / 20;
intensity = 40;
}
//判断是否为跳过空白页发票
if (param.is_autodiscradblank_vince) {
area *= 1.5;
intensity *= 1.5;
}
//设置参数阈值
disBlank->setMinArea(area);
disBlank->setIntensity(intensity);
m_iaList.push_back(shared_ptr<CImageApply>(disBlank));
//m_iaList.push_back(shared_ptr<CImageApply>(new CImageApplyDiscardBlank(param.areanum,param.devnmax)));
if (param.is_autodiscradblank_normal)
m_iaList.push_back(shared_ptr<CImageApply>(new CImageApplyDiscardBlank(40, 30, param.discardblank_percent, 200)));
else
m_iaList.push_back(shared_ptr<CImageApply>(new CImageApplyDiscardBlank(40, 30, param.discardblank_percent * 15 / 10, 150)));
}
{

View File

@ -67,6 +67,14 @@ const std::string EN_MULTIOUT = "bMultiOut";
const std::string MULTIOUTTYPE = "iMultiOut";
const std::string OUTHOLE = "bOutHole";
const std::string OUTHOLERATIO = "iOutHoleRatio";
const std::string OUTHOLERATIOUP = "iOutHoleRatioup";
const std::string OUTHOLERATIODOWN = "iOutHoleRatiodown";
const std::string OUTHOLERATIOLEFT = "iOutHoleRatioleft";
const std::string OUTHOLERATIORIGHT = "iOutHoleRatioright";
const std::string EN_OUTHOLERATIOUP = "bOutHoleRatioup";
const std::string EN_OUTHOLERATIODOWN = "bOutHoleRatiodown";
const std::string EN_OUTHOLERATIOLEFT = "bOutHoleRatioleft";
const std::string EN_OUTHOLERATIORIGHT = "bOutHoleRatioright";
const std::string DETACHNOISE = "detachnoise";
const std::string DETACHNOISEVALUE = "detachnoisevalue";
const std::string FADEBACK = "bfadeback";
@ -282,6 +290,14 @@ struct GScanCap
FillHole fillhole;
DetachNoise detachnoise; /**< 黑白降噪*/
uint8_t is_autotext; /**< 自动文本方向识别*/
int fillholeratio_up;
int fillholeratio_down;
int fillholeratio_left;
int fillholeratio_right;
bool en_fillholeratio_up;
bool en_fillholeratio_down;
bool en_fillholeratio_left;
bool en_fillholeratio_right;
std::string Caption;
std::string SavePath;
};
@ -360,6 +376,10 @@ struct GScanCap_3399
bool fadeback; /**< 除底色>*/
int fadebackrange; /**< 除底色范围>*/
bool isuploadexceptionimage; /**< 是否上传双张报错后的异常图像>*/
int fillholeratio_up; /**< ´©¿×ÒƳýÉÏÏÂ×óÓÒ·¶Î§>*/
int fillholeratio_down;
int fillholeratio_left;
int fillholeratio_right;
uint32_t reserve[1024]; /**< 预留4096字节做协议扩展*/
};

View File

@ -199,7 +199,6 @@ int UsbScanEx::control_msg(int rtype, int req, int value, int index, int len, vo
LPOVERLAPPED lp_overlap = ov + CTRL_IN_OUT;
b_ret = DeviceIoControl(m_h_dev, IOCTL_SEND_USB_REQUEST, &irp, sizeof(irp), data, len, &dw_ret, NULL);
FileTools::writelog(log_ERROR, " USB control_msg ret = " + std::to_string(b_ret));
//if (!b_ret)
// b_ret = WaitForSingleObject(lp_overlap->hEvent, timeout) == WAIT_OBJECT_0;

View File

@ -126,6 +126,15 @@ void GscanJsonConfig::SaveGscanCapConfig(const GScanCap & gcap, const std::strin
outJson["Config"].Add(MULTIOUTTYPE, (int)(gcap.multioutput));
outJson["Config"].Add(OUTHOLE, (bool)(gcap.fillhole.is_fillhole), false);
outJson["Config"].Add(OUTHOLERATIO, (int)(gcap.fillhole.fillholeratio));
outJson["Config"].Add(OUTHOLERATIOUP, (int)(gcap.fillholeratio_up));
outJson["Config"].Add(OUTHOLERATIODOWN, (int)(gcap.fillholeratio_down));
outJson["Config"].Add(OUTHOLERATIOLEFT, (int)(gcap.fillholeratio_left));
outJson["Config"].Add(OUTHOLERATIORIGHT, (int)(gcap.fillholeratio_right));
outJson["Config"].Add(EN_OUTHOLERATIOUP, (bool)(gcap.fillholeratio_up),true);
outJson["Config"].Add(EN_OUTHOLERATIODOWN, (bool)(gcap.fillholeratio_down), true);
outJson["Config"].Add(EN_OUTHOLERATIOLEFT, (bool)(gcap.fillholeratio_left), true);
outJson["Config"].Add(EN_OUTHOLERATIORIGHT, (bool)(gcap.fillholeratio_right), true);
outJson["Config"].Add(DETACHNOISE, (bool)(gcap.detachnoise.is_detachnoise), false);
outJson["Config"].Add(DETACHNOISEVALUE, (int)(gcap.detachnoise.detachnoise));
outJson["Config"].Add(FADEBACK, (bool)(gcap.fadeback), false);
@ -198,6 +207,14 @@ void GscanJsonConfig::WriteJsonArrayToFile(std::vector<GScanCap> cfgArray, const
root["Config"].AddEmptySubArray(MULTIOUTPUT);
root["Config"].AddEmptySubArray(OUTHOLE);
root["Config"].AddEmptySubArray(OUTHOLERATIO);
root["Config"].AddEmptySubArray(OUTHOLERATIOUP);
root["Config"].AddEmptySubArray(OUTHOLERATIODOWN);
root["Config"].AddEmptySubArray(OUTHOLERATIOLEFT);
root["Config"].AddEmptySubArray(OUTHOLERATIORIGHT);
root["Config"].AddEmptySubArray(EN_OUTHOLERATIOUP);
root["Config"].AddEmptySubArray(EN_OUTHOLERATIODOWN);
root["Config"].AddEmptySubArray(EN_OUTHOLERATIOLEFT);
root["Config"].AddEmptySubArray(EN_OUTHOLERATIORIGHT);
root["Config"].AddEmptySubArray(HSVCORRECT);
/*< feeder settings*/
@ -258,6 +275,15 @@ void GscanJsonConfig::WriteJsonArrayToFile(std::vector<GScanCap> cfgArray, const
root["Config"][MULTIOUTTYPE].Add((int)cfgArray[i].multioutput);
root["Config"][OUTHOLE].Add(i, (bool)cfgArray[i].fillhole.is_fillhole);
root["Config"][OUTHOLERATIO].Add((int)cfgArray[i].fillhole.fillholeratio);
root["Config"][OUTHOLERATIOUP].Add ((int)(cfgArray[i].fillholeratio_up));
root["Config"][OUTHOLERATIODOWN].Add((int)(cfgArray[i].fillholeratio_down));
root["Config"][OUTHOLERATIOLEFT].Add((int)(cfgArray[i].fillholeratio_left));
root["Config"][OUTHOLERATIORIGHT].Add((int)(cfgArray[i].fillholeratio_right));
root["Config"][EN_OUTHOLERATIOUP].Add((int)(cfgArray[i].en_fillholeratio_up));
root["Config"][EN_OUTHOLERATIODOWN].Add((int)(cfgArray[i].en_fillholeratio_down));
root["Config"][EN_OUTHOLERATIOLEFT].Add((int)(cfgArray[i].en_fillholeratio_left));
root["Config"][EN_OUTHOLERATIORIGHT].Add((int)(cfgArray[i].en_fillholeratio_right));
root["Config"][DETACHNOISE].Add(i, (bool)cfgArray[i].detachnoise.is_detachnoise);
root["Config"][DETACHNOISEVALUE].Add((int)cfgArray[i].detachnoise.detachnoise);
root["Config"][FADEBACK].Add(i, (bool)cfgArray[i].fadeback);
@ -451,6 +477,24 @@ std::vector<GScanCap> GscanJsonConfig::parseJsonFromString(const std::string str
root["Config"].Get(OUTHOLE, itmOutHole);
neb::CJsonObject itmOutHoleRatio;
root["Config"].Get(OUTHOLERATIO, itmOutHoleRatio);
neb::CJsonObject itmOutHoleRatioUp;
root["Config"].Get(OUTHOLERATIOUP, itmOutHoleRatioUp);
neb::CJsonObject itmOutHoleRatioDown;
root["Config"].Get(OUTHOLERATIODOWN, itmOutHoleRatioDown);
neb::CJsonObject itmOutHoleRatioLeft;
root["Config"].Get(OUTHOLERATIOLEFT, itmOutHoleRatioLeft);
neb::CJsonObject itmOutHoleRatioRight;
root["Config"].Get(OUTHOLERATIORIGHT, itmOutHoleRatioRight);
neb::CJsonObject itmEnOutHoleRatioUp;
root["Config"].Get(EN_OUTHOLERATIOUP, itmEnOutHoleRatioUp);
neb::CJsonObject itmEnOutHoleRatioDown;
root["Config"].Get(EN_OUTHOLERATIODOWN, itmEnOutHoleRatioDown);
neb::CJsonObject itmEnOutHoleRatioLeft;
root["Config"].Get(EN_OUTHOLERATIOLEFT, itmEnOutHoleRatioLeft);
neb::CJsonObject itmENOutHoleRatioRight;
root["Config"].Get(EN_OUTHOLERATIORIGHT, itmENOutHoleRatioRight);
neb::CJsonObject itmDetachNoise;
root["Config"].Get(DETACHNOISE, itmDetachNoise);
neb::CJsonObject itmDetachNoiseValue;
@ -583,6 +627,23 @@ std::vector<GScanCap> GscanJsonConfig::parseJsonFromString(const std::string str
cfp.fillhole.is_fillhole = b_value ? 1 : 0;
itmOutHoleRatio.Get(i, i_value);
cfp.fillhole.fillholeratio = i_value;
itmOutHoleRatioUp.Get(i, i_value);
cfp.fillholeratio_up = i_value;
itmOutHoleRatioDown.Get(i, i_value);
cfp.fillholeratio_down = i_value;
itmOutHoleRatioLeft.Get(i, i_value);
cfp.fillholeratio_left = i_value;
itmOutHoleRatioRight.Get(i, i_value);
cfp.fillholeratio_right = i_value;
itmEnOutHoleRatioUp.Get(i, b_value);
cfp.en_fillholeratio_up = b_value ? 1 : 0;
itmEnOutHoleRatioDown.Get(i, b_value);
cfp.en_fillholeratio_down = b_value ? 1 : 0;
itmEnOutHoleRatioLeft.Get(i, b_value);
cfp.en_fillholeratio_left = b_value ? 1 : 0;
itmENOutHoleRatioRight.Get(i, b_value);
cfp.en_fillholeratio_right = b_value ? 1 : 0;
itmDetachNoise.Get(i, b_value);
cfp.detachnoise.is_detachnoise = b_value ? 1 : 0;
itmDetachNoiseValue.Get(i, i_value);
@ -714,6 +775,22 @@ std::vector<GScanCap> GscanJsonConfig::parseJsonFromString(const std::string str
cfp.fillhole.is_fillhole = bvalue?1:0;
root["Config"].Get(OUTHOLERATIO, index);
cfp.fillhole.fillholeratio = index;
root["Config"].Get(OUTHOLERATIOUP, index);
cfp.fillholeratio_up = index;
root["Config"].Get(OUTHOLERATIODOWN, index);
cfp.fillholeratio_down = index;
root["Config"].Get(OUTHOLERATIOLEFT, index);
cfp.fillholeratio_left = index;
root["Config"].Get(OUTHOLERATIORIGHT, index);
cfp.fillholeratio_right = index;
root["Config"].Get(EN_OUTHOLERATIOUP, bvalue);
cfp.en_fillholeratio_up = bvalue ? 1 : 0;
root["Config"].Get(EN_OUTHOLERATIODOWN, bvalue);
cfp.en_fillholeratio_down = bvalue ? 1 : 0;
root["Config"].Get(EN_OUTHOLERATIOLEFT, bvalue);
cfp.en_fillholeratio_left = bvalue ? 1 : 0;
root["Config"].Get(EN_OUTHOLERATIORIGHT, bvalue);
cfp.en_fillholeratio_right = bvalue ? 1 : 0;
root["Config"].Get(DETACHNOISE, bvalue);
cfp.detachnoise.is_detachnoise = bvalue ? 1 : 0;
root["Config"].Get(DETACHNOISEVALUE, index);
@ -832,6 +909,14 @@ json GscanJsonConfig::GscancapToJson(GScanCap& cap)
js[CONFIG][MULTIOUTTYPE] = cap.multioutput;
js[CONFIG][OUTHOLE] = cap.fillhole.is_fillhole;
js[CONFIG][OUTHOLERATIO] = cap.fillhole.fillholeratio;
js[CONFIG][OUTHOLERATIOUP] = cap.fillholeratio_up;
js[CONFIG][OUTHOLERATIODOWN] = cap.fillholeratio_down;
js[CONFIG][OUTHOLERATIOLEFT] = cap.fillholeratio_left;
js[CONFIG][OUTHOLERATIORIGHT] = cap.fillholeratio_right;
js[CONFIG][EN_OUTHOLERATIOUP] = cap.en_fillholeratio_up;
js[CONFIG][EN_OUTHOLERATIODOWN] = cap.en_fillholeratio_down;
js[CONFIG][EN_OUTHOLERATIOLEFT] = cap.en_fillholeratio_left;
js[CONFIG][EN_OUTHOLERATIORIGHT] = cap.en_fillholeratio_right;
js[CONFIG][HSVCORRECT] = cap.hsvcorrect;
js[CONFIG][ULTRADETECT] = cap.hardwarecaps.en_doublefeed;
js[CONFIG][BINDINGDETECT] = cap.hardwarecaps.en_stapledetect;
@ -900,6 +985,14 @@ GScanCap GscanJsonConfig::JsonToGscancap(json& js)
cap.multioutput = json_cast(js[CONFIG][MULTIOUTTYPE]).to_int();
cap.fillhole.is_fillhole = json_cast(js[CONFIG][OUTHOLE]).to_int();
cap.fillhole.fillholeratio = json_cast(js[CONFIG][OUTHOLERATIO]).to_int();
cap.fillholeratio_up = json_cast(js[CONFIG][OUTHOLERATIOUP]).to_int();
cap.fillholeratio_down = json_cast(js[CONFIG][OUTHOLERATIODOWN]).to_int();
cap.fillholeratio_left = json_cast(js[CONFIG][OUTHOLERATIOLEFT]).to_int();
cap.fillholeratio_right = json_cast(js[CONFIG][OUTHOLERATIORIGHT]).to_int();
cap.en_fillholeratio_up = json_cast(js[CONFIG][EN_OUTHOLERATIOUP]).to_bool();
cap.en_fillholeratio_down = json_cast(js[CONFIG][EN_OUTHOLERATIODOWN]).to_bool();
cap.en_fillholeratio_left = json_cast(js[CONFIG][EN_OUTHOLERATIOLEFT]).to_bool();
cap.en_fillholeratio_right = json_cast(js[CONFIG][EN_OUTHOLERATIORIGHT]).to_bool();
cap.hsvcorrect = json_cast(js[CONFIG][HSVCORRECT]).to_int();
cap.hardwarecaps.en_doublefeed = json_cast(js[CONFIG][ULTRADETECT]).to_int();
cap.hardwarecaps.en_stapledetect = json_cast(js[CONFIG][BINDINGDETECT]).to_int();
@ -963,6 +1056,14 @@ json GscanJsonConfig::GetDefaultJson()
"iMultiOut": 0 ,
"bOutHole": false ,
"iOutHoleRatio": 10 ,
"iOutHoleRatioup": 10 ,
"iOutHoleRatiodown": 10 ,
"iOutHoleRatioleft": 10 ,
"iOutHoleRatioright": 10 ,
"bOutHoleRatioup": true ,
"bOutHoleRatiodown": true ,
"bOutHoleRatioleft": true ,
"bOutHoleRatioright": true ,
"bHsvCorrect": false ,
"bUltrasonicDetect": true ,
"bBindingDetect": false ,
@ -1023,6 +1124,14 @@ json GscanJsonConfig::GetDefaultJson()
"iMultiOut": 0 ,
"bOutHole": false ,
"iOutHoleRatio": 10 ,
"iOutHoleRatioup": 10 ,
"iOutHoleRatiodown": 10 ,
"iOutHoleRatioleft": 10 ,
"iOutHoleRatioright": 10 ,
"bOutHoleRatioup": true ,
"bOutHoleRatiodown": true ,
"bOutHoleRatioleft": true ,
"bOutHoleRatioright": true ,
"bHsvCorrect": false ,
"bUltrasonicDetect": true ,
"bBindingDetect": false ,

View File

@ -1,187 +1,22 @@
#include "ImageApplyDiscardBlank.h"
#include "ImageApplyDiscardBlank.h"
#include "ImageProcess_Public.h"
CImageApplyDiscardBlank::CImageApplyDiscardBlank()
: m_res(false)
, m_dSize(20)
, m_devTh(15, 15, 15, 15)
{
}
CImageApplyDiscardBlank::CImageApplyDiscardBlank(int blockSize, int devTh)
: m_res(false)
, m_dSize(blockSize)
CImageApplyDiscardBlank::CImageApplyDiscardBlank(double threshold, int edge, double devTh, double meanTh)
: m_threshold(threshold)
, m_edge(edge)
, m_devTh(devTh)
, m_meanTh(meanTh)
{
}
CImageApplyDiscardBlank::CImageApplyDiscardBlank(bool isnormal)
: m_res(false)
, m_isNormalDiscard(isnormal)
{
m_dSize = m_isNormalDiscard ? 200 : 300;
m_devTh = m_isNormalDiscard ? cv::Scalar::all(8) : cv::Scalar::all(20);
}
CImageApplyDiscardBlank::~CImageApplyDiscardBlank(void)
{
}
int CImageApplyDiscardBlank::processRectR(const cv::Mat& image, cv::RotatedRect& rotatedRect, std::vector<cv::Point>& maxContour,
double scale, double thresh, int blobAreaSize)
{
cv::Mat gray;
int blockCount = 0;
if (image.channels() == 3)
if (scale != 1.0f)
{
cv::Size ResImgSiz = cv::Size(image.cols * scale, image.rows * scale);
resize(image, gray, cv::Size(), scale, scale, 0);
cvtColor(gray, gray, CV_BGR2GRAY);
}
else
cvtColor(image, gray, CV_BGR2GRAY);
else
if (scale != 1.0f)
resize(image, gray, cv::Size(), scale, scale, 0);
else
gray = image;
cv::Mat threshold_img;
threshold(gray, threshold_img, thresh, 255.0, CV_THRESH_BINARY);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> h1;
hg::findContours(threshold_img, contours, h1, CV_CHAIN_APPROX_SIMPLE);
threshold_img.release();
if (contours.size() == 0)
return blockCount;
std::vector<cv::Point2f> list_com;
for (int i = 0; i < contours.size(); i++)
{
double area = cv::contourArea(contours[i]);
if (area > blobAreaSize)
{
blockCount++;
for (int j = 0; j < contours[i].size(); j++)
list_com.push_back(contours[i][j]);
}
}
if (list_com.size() == 0)
return blockCount;
rotatedRect = cv::minAreaRect(list_com);
rotatedRect.center.x /= (float)scale;
rotatedRect.center.y /= (float)scale;
rotatedRect.size.width /= (float)scale;
rotatedRect.size.height /= (float)scale;
if (rotatedRect.angle < -45.0f)
{
rotatedRect.angle += 90.0f;
float l_temp = rotatedRect.size.width;
rotatedRect.size.width = rotatedRect.size.height;
rotatedRect.size.height = l_temp;
}
if (rotatedRect.angle > 45.0f)
{
rotatedRect.angle -= 90.0f;
float l_temp = rotatedRect.size.width;
rotatedRect.size.width = rotatedRect.size.height;
rotatedRect.size.height = l_temp;
}
std::vector<int> hull(list_com.size());
cv::convexHull(list_com, hull);
for (int i = 0; i < hull.size(); i++)
{
cv::Point temp = list_com[hull[i]];
int x = (int)(temp.x / scale);
int y = (int)(temp.y / scale);
maxContour.push_back(cv::Point(x, y));
}
return blockCount;
}
bool CImageApplyDiscardBlank::scalar_LE(const cv::Scalar& val1, const cv::Scalar& val2)
{
for (int i = 0; i < 3; i++)
if (val1[i] > val2[i])
return false;
return true;
}
void CImageApplyDiscardBlank::setIntensity(int val)
{
val = cv::max(cv::min(100, val), 2);
m_devTh = cv::Scalar::all(val);
}
cv::Mat CImageApplyDiscardBlank::getRoiMat(const cv::Mat& image)
{
int gap = 100;
cv::RotatedRect rect;
std::vector<cv::Point> contour;
double scale = 0.25;
double thresh = 50;
int blobSize = 200;
processRectR(image, rect, contour, scale, thresh, blobSize);
cv::Rect rect2 = rect.boundingRect();
cv::Rect inRect = rect2 & cv::Rect(0, 0, image.cols, image.rows);
gap = cv::max(inRect.width - rect.size.width, inRect.height - rect.size.height) + 100;
inRect = cv::Rect(inRect.x + gap, inRect.y + gap, inRect.width - gap * 2, inRect.height - gap * 2);
if (inRect.width <= 0 || inRect.height <= 0)
return cv::Mat();
return image(inRect);
}
void CImageApplyDiscardBlank::apply(cv::Mat& pDib, int side)
{
#ifdef LOG
FileTools::write_log("imgprc.txt", "enter CImageApplyDiscardBlank apply");
#endif // LOG
if (pDib.empty())
{
#ifdef LOG
FileTools::write_log("imgprc.txt", "exit CImageApplyDiscardBlank apply");
#endif // LOG
return;
}
cv::Scalar mean;
cv::Scalar dev;
cv::Mat image = getRoiMat(pDib);
cv::Rect rect;
cv::Rect imRect(0, 0, image.cols, image.rows);
for (int i = 0; i < image.cols; i += m_dSize)
for (int j = 0; j < image.rows; j += m_dSize)
{
rect = cv::Rect(i, j, m_dSize, m_dSize) & imRect;
if (rect != cv::Rect())
{
cv::meanStdDev(image(rect), mean, dev);
if (!scalar_LE(dev, m_devTh))
{
m_res = false;
#ifdef LOG
FileTools::write_log("imgprc.txt", "exit CImageApplyDiscardBlank apply");
#endif // LOG
return;
}
}
}
m_res = true;
if (m_res)
if (apply(pDib, m_threshold, m_edge, m_devTh, m_meanTh))
pDib.release();
#ifdef LOG
FileTools::write_log("imgprc.txt", "exit CImageApplyDiscardBlank apply");
#endif // LOG
}
void CImageApplyDiscardBlank::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
@ -197,30 +32,86 @@ void CImageApplyDiscardBlank::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
}
}
bool CImageApplyDiscardBlank::apply(const cv::Mat& pDib, int blockSize, int devTh)
bool scalar_LE(const cv::Scalar& val1, const cv::Scalar& val2)
{
for (int i = 0; i < 3; i++)
if (val1[i] > val2[i])
return false;
return true;
}
bool maxMinCompare(const cv::Mat& img, const cv::Mat& mask, double devTh, double meanTh)
{
double min, max;
cv::minMaxLoc(img, &min, &max, 0, 0, mask);
if (cv::mean(img, mask)[0] < meanTh)
return false;
return (max - min) < devTh;
}
bool CImageApplyDiscardBlank::apply(const cv::Mat& pDib, double threshold, int edge, double devTh, double meanTh)
{
if (pDib.empty())
return true;
cv::Scalar mean;
cv::Scalar dev;
cv::Scalar s_devTh = cv::Scalar::all(devTh);
cv::Mat image = getRoiMat(pDib);
if (image.empty())
return false;
cv::Rect rect;
cv::Rect imRect(0, 0, image.cols, image.rows);
for (int i = 0; i < image.cols; i += blockSize)
for (int j = 0; j < image.rows; j += blockSize)
cv::Mat img_resize;
cv::resize(pDib, img_resize, cv::Size(), 0.2, 0.2);
cv::Mat threshold_img;
if (img_resize.channels() == 3)
cv::cvtColor(img_resize, threshold_img, cv::COLOR_BGR2GRAY);
cv::threshold(img_resize.channels() == 3 ? threshold_img : img_resize, threshold_img, threshold, 255, cv::THRESH_BINARY);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> h1;
hg::findContours(threshold_img, contours, h1, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
std::vector<cv::Point> contour;
for (const std::vector<cv::Point>& sub : contours)
for (const cv::Point& p : sub)
contour.push_back(p);
cv::RotatedRect rect = hg::getBoundingRect(contour);
rect.size = cv::Size2f(rect.size.width - edge / 2.5, rect.size.height - edge / 2.5);
cv::Point2f box[4];
rect.points(box);
contour.clear();
contours.clear();
for (size_t i = 0; i < 4; i++)
contour.push_back(box[i]);
contours.push_back(contour);
cv::Mat mask = cv::Mat::zeros(img_resize.size(), CV_8UC1);
hg::fillPolys(mask, contours, cv::Scalar::all(255));
int kSize = (devTh / 20) / 2 * 2 + 1;
if (kSize > 1)
cv::blur(img_resize, img_resize, cv::Size(kSize, kSize));
bool b = true;
if (img_resize.channels() == 3)
{
rect = cv::Rect(i, j, blockSize, blockSize) & imRect;
if (rect != cv::Rect())
cv::Mat bgr[3];
cv::split(img_resize, bgr);
for (size_t i = 0; i < 3; i++)
{
cv::meanStdDev(image(rect), mean, dev);
if (!scalar_LE(dev, s_devTh))
return false;
b &= maxMinCompare(bgr[i], mask, devTh, meanTh);
if (!b) break;
}
}
return true;
else
b &= maxMinCompare(img_resize, mask, devTh, meanTh);
/*
if (b)
{
cv::imwrite("空白页/img1/" + std::to_string(index) + ".bmp", img_resize);
cv::imwrite("空白页/mask1/" + std::to_string(index) + ".bmp", mask);
}
else
{
cv::imwrite("空白页/img2/" + std::to_string(index) + ".bmp", img_resize);
cv::imwrite("空白页/mask2/" + std::to_string(index) + ".bmp", mask);
}*/
return b;
}

View File

@ -1,17 +1,23 @@
/*
/*
* ====================================================
*
*
* 2020/4/21
* 2020/4/21 v1.0
2020/8/12 v1.1 setIntensity和setMinAreaisNormal标识位setIntensity的设置范围[2, 20][1, 100]
2020/8/25 v1.1.1 10020
2020/10/16 v1.2 便
2020/10/19 v1.2.1 BUG
2021/04/13 v1.3.0 /
2021/08/12 v1.3.1 opencv版本导致计算结果存在差异的代码
* v1.3.1
*
*
* 2020/4/21
* 2020/4/21 v1.0
2020/8/12 v1.1 setIntensity和setMinAreaisNormal标识位setIntensity的设置范围[2, 20][1, 100]
2020/8/25 v1.1.1 10020
2020/10/16 v1.2 便
2020/10/19 v1.2.1 BUG
2021/04/13 v1.3.0 /
2021/08/12 v1.3.1 opencv版本导致计算结果存在差异的代码
2021/12/14 v1.3.2
2021/12/15 v1.3.3
2021/12/17 v1.3.4
2022/09/07 v1.3.5 BUG
2022/09/19 v1.4
2022/09/19 v1.4.1
* v1.4.1
* ====================================================
*/
@ -24,14 +30,8 @@
class CImageApplyDiscardBlank : public CImageApply
{
public:
CImageApplyDiscardBlank();
CImageApplyDiscardBlank(int blockSize, int devTh);
/*
* isnormal [in]:true标准模式false为票据复写纸模式
* */
CImageApplyDiscardBlank(bool isnormal);
CImageApplyDiscardBlank(double threshold = 40, int edge = 150, double devTh = 50, double meanTh = 200);
virtual ~CImageApplyDiscardBlank(void);
@ -39,25 +39,13 @@ public:
virtual void apply(std::vector<cv::Mat>& mats, bool isTwoSide);
void setIntensity(int val);
void setMinArea(int val) { m_dSize = val; }
static bool apply(const cv::Mat& pDib, int blockSize = 200, int devTh = 15);
static bool apply(const cv::Mat& pDib, double threshold = 40, int edge = 150, double devTh = 50, double meanTh = 200);
private:
static int processRectR(const cv::Mat& image, cv::RotatedRect& rotatedRect, std::vector<cv::Point>& maxContour,
double scale, double thresh, int blobAreaSize);
static bool scalar_LE(const cv::Scalar& val1, const cv::Scalar& val2);
static cv::Mat getRoiMat(const cv::Mat& pDib);
private:
bool m_res;
bool m_isNormalDiscard;
int m_dSize;
cv::Scalar m_devTh;
double m_threshold;
int m_edge;
double m_devTh;
double m_meanTh;
};
#endif // !IMAGE_APPLY_DISCARD_BLANK_H

View File

@ -1,5 +1,6 @@
#include "ImageApplyOutHole.h"
#include "ImageProcess_Public.h"
//#include <QDebug>
#ifdef LOG
#include "Device/filetools.h"
@ -7,13 +8,13 @@
CImageApplyOutHole::CImageApplyOutHole(void)
: CImageApply()
, m_borderSize(600)
, m_edgeScale(0.1f)
, m_threshold(100)
, m_borderSize(20)
, m_edgeScale(0.1f, 0.1f, 0.1f, 0.1f)
, m_threshold(50)
{
}
CImageApplyOutHole::CImageApplyOutHole(float borderSize, float edgeScale, double threshold)
CImageApplyOutHole::CImageApplyOutHole(float borderSize, cv::Vec4f edgeScale, double threshold)
: CImageApply()
, m_borderSize(borderSize)
, m_edgeScale(edgeScale)
@ -31,6 +32,10 @@ void CImageApplyOutHole::apply(cv::Mat& pDib, int side)
(void)side;
}
#define MIN_CONTOUR_SIZE 10
#define RESIZE_FIXED_WIDTH 2448.0
#define LINE_WIDTH 18
#define DILATE_SIZE 16
void CImageApplyOutHole::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
{
#ifdef LOG
@ -53,14 +58,26 @@ void CImageApplyOutHole::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
return;
}
double resize_scale = cv::min(RESIZE_FIXED_WIDTH / static_cast<double>(mats[0].cols), 1.0);
//二值化正反面图像
cv::Mat front = mats[0];
cv::Mat back = mats[1];
cv::Mat front, back;
if (resize_scale != 1.0)
{
cv::resize(mats[0], front, cv::Size(), resize_scale, resize_scale);
cv::resize(mats[1], back, cv::Size(), resize_scale, resize_scale);
}
else
{
front = mats[0];
back = mats[1];
}
cv::Mat front_thre, back_thre;
hg::threshold_Mat(front, front_thre, m_threshold);
hg::threshold_Mat(back, back_thre, m_threshold);
cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(10, 1));
cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5 * resize_scale, 1));
cv::morphologyEx(front_thre, front_thre, cv::MORPH_OPEN, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0));
cv::morphologyEx(back_thre, back_thre, cv::MORPH_OPEN, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar::all(0));
@ -74,6 +91,22 @@ void CImageApplyOutHole::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
hg::findContours(back_thre.clone(), contours_back, b1_back, cv::RETR_EXTERNAL);
//提取正反面图像最大轮廓
for (size_t i = 0; i < contours_front.size(); i++)
if (contours_front[i].size() < MIN_CONTOUR_SIZE)
{
contours_front.erase(contours_front.begin() + i);
b1_front.erase(b1_front.begin() + i);
i--;
}
for (size_t i = 0; i < contours_back.size(); i++)
if (contours_back[i].size() < MIN_CONTOUR_SIZE)
{
contours_back.erase(contours_back.begin() + i);
b1_back.erase(b1_back.begin() + i);
i--;
}
std::vector<cv::Point> maxContour_front = hg::getMaxContour(contours_front, b1_front);
std::vector<cv::Point> maxContour_back = hg::getMaxContour(contours_back, b1_back);
@ -107,27 +140,37 @@ void CImageApplyOutHole::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
cv::bitwise_not(mask, mask);
//cv::imwrite("mask2.jpg", mask); //反色
element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(10, 10));
cv::dilate(mask, mask, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar(255)); //膨胀算法,增大孔洞连通区域面积
//为了避免孔洞彻底贯穿纸边,人为绘制纸张轮廓,确保所有孔洞为封闭图形,不会与背景粘连
cv::polylines(mask, hg::getVertices(mask_rotatedRect), true, cv::Scalar(0), LINE_WIDTH * resize_scale); //绘制纸张矩形边缘
//cv::imwrite("mask3.jpg", mask);
//为了避免孔洞彻底贯穿纸边,人为绘制纸张轮廓,确保所有孔洞为封闭图形,不会与背景粘连
cv::polylines(mask, hg::getVertices(mask_rotatedRect), true, cv::Scalar(255), 15); //绘制纸张矩形边缘
//膨胀算法,增大孔洞连通区域面积
element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(DILATE_SIZE * resize_scale, DILATE_SIZE * resize_scale));
cv::dilate(mask, mask, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT, cv::Scalar(255));
//cv::imwrite("mask4.jpg", mask);
//提取重叠图像轮廓
std::vector<std::vector<cv::Point>> contours_mask;
std::vector<cv::Vec4i> b1_mask;
hg::findContours(mask, contours_mask, b1_mask, cv::RETR_TREE); //提取重叠图像轮廓
hg::findContours(mask, contours_mask, b1_mask, cv::RETR_TREE);
//过滤非孔洞的联通区域
std::vector<std::vector<cv::Point>> hole_contours = filterPoly(contours_mask, b1_mask, mask_rotatedRect, m_edgeScale, m_borderSize);
std::vector<std::vector<cv::Point>> hole_contours = filterPoly(contours_mask, b1_mask, mask_rotatedRect, m_edgeScale, m_borderSize * resize_scale);
for (size_t i = 0; i < hole_contours.size(); i++)
for (size_t j = 0; j < hole_contours[i].size(); j++)
hole_contours[i][j] /= resize_scale;
cv::Scalar color = getBackGroudColor(front(roi_front), rrect_front.size.area());
roi_front.x /= resize_scale;
roi_front.y /= resize_scale;
roi_front.width /= resize_scale;
roi_front.height /= resize_scale;
for (size_t i = 0; i < hole_contours.size(); i++)
{
std::vector<std::vector<cv::Point>> contourss_temp;
contourss_temp.push_back(hole_contours[i]);
cv::Mat front_temp = front(roi_front);
cv::Mat front_temp = mats[0](roi_front);
hg::fillPolys(front_temp, contourss_temp, color);
}
@ -135,16 +178,26 @@ void CImageApplyOutHole::apply(std::vector<cv::Mat>& mats, bool isTwoSide)
{
int width_ = roi_back.width;
roi_back.x = back.cols - roi_back.width - roi_back.x; //因为之前反面图像翻转所以现在ROI也要进行相应翻转
color = getBackGroudColor(back(roi_back), rrect_front.size.area());
color = getBackGroudColor(back(roi_back), rrect_back.size.area());
roi_back.x /= resize_scale;
roi_back.y /= resize_scale;
roi_back.width /= resize_scale;
roi_back.height /= resize_scale;
hole_contours.clear();
hole_contours = filterPoly(contours_mask, b1_mask, mask_rotatedRect, cv::Vec4f(m_edgeScale[0], m_edgeScale[1], m_edgeScale[2], m_edgeScale[3]), m_borderSize * resize_scale);
for (size_t i = 0; i < hole_contours.size(); i++)
{
std::vector<cv::Point> hole_contour;
for (size_t j = 0; j < hole_contours[i].size(); j++)
{
hole_contour.push_back(cv::Point(width_ - hole_contours[i][j].x - 1, hole_contours[i][j].y));
hole_contour[j] /= resize_scale;
}
std::vector<std::vector<cv::Point>> contours_temp;
contours_temp.push_back(hole_contour);
cv::Mat back_temp = back(roi_back);
cv::Mat back_temp = mats[1](roi_back);
hg::fillPolys(back_temp, contours_temp, color);
}
}
@ -222,27 +275,44 @@ void CImageApplyOutHole::getRoi(cv::RotatedRect rrect_front, cv::RotatedRect rre
roi_front.height -= offset_bottom_b - offset_bottom_f;
}
std::vector<std::vector<cv::Point>> CImageApplyOutHole::filterPoly(std::vector<std::vector<cv::Point>>& contours, const std::vector<cv::Vec4i>& m,
cv::RotatedRect roi, float edgeScale, float areaThreshold)
#define SIDE_LENGTH_UP_SCALE 6
#define PI 3.14159265359f
std::vector<std::vector<cv::Point>> CImageApplyOutHole::filterPoly(std::vector<std::vector<cv::Point>>& contours, std::vector<cv::Vec4i>& m,
cv::RotatedRect roi, cv::Vec4f edgeScale, float sideLengthLow)
{
edgeScale = std::min(0.49f, std::max(edgeScale, 0.0f));
cv::RotatedRect roi2(roi.center, cv::Size(static_cast<int>(roi.size.width * (1 - edgeScale * 2)),
static_cast<int>(roi.size.height * (1 - edgeScale * 2))), roi.angle);
cv::RotatedRect roi2(roi.center,
cv::Size2f(roi.size.width * (1 - edgeScale[2] - edgeScale[3]), roi.size.height * (1 - edgeScale[0] - edgeScale[1])),
roi.angle);
cv::Point2f offset_0(roi.size.width * (edgeScale[2] - edgeScale[3]) / 2 + roi.center.x, roi.size.height * (edgeScale[0] - edgeScale[1]) / 2 + roi.center.y);
roi2.center.x = (offset_0.x - roi.center.x) * cos(roi.angle * PI / 180.0f) - (offset_0.y - roi.center.y) * sin(roi2.angle * PI / 180.0f) + roi.center.x;
roi2.center.y = (offset_0.x - roi.center.x) * sin(roi.angle * PI / 180.0f) + (offset_0.y - roi.center.y) * cos(roi2.angle * PI / 180.0f) + roi.center.y;
std::vector<cv::Point> vertices_roi1 = hg::getVertices(roi);
std::vector<cv::Point> vertices_roi2 = hg::getVertices(roi2);
std::vector<std::vector<cv::Point>> hole_contours;
for (size_t i = 0, length = contours.size(); i < length; i++)
for (size_t i = 0; i < contours.size(); i++)
{
if (m[i][2] != -1) continue;
if (m[i][2] != -1)
{
contours.erase(contours.begin() + i);
m.erase(m.begin() + i);
i--;
continue;
}
cv::RotatedRect rrect = hg::getBoundingRect(contours[i]);
if (rrect.size.width < areaThreshold ||
rrect.size.height < areaThreshold ||
rrect.size.width > areaThreshold * 6 ||
rrect.size.height > areaThreshold * 6)
if (rrect.size.width < sideLengthLow ||
rrect.size.height < sideLengthLow ||
rrect.size.width > sideLengthLow * SIDE_LENGTH_UP_SCALE ||
rrect.size.height > sideLengthLow * SIDE_LENGTH_UP_SCALE)
{
contours.erase(contours.begin() + i);
m.erase(m.begin() + i);
i--;
continue;
}
bool enabled = true;
for (size_t j = 0, count = contours[i].size(); j < count; j++)
@ -251,9 +321,10 @@ std::vector<std::vector<cv::Point>> CImageApplyOutHole::filterPoly(std::vector<s
double temp1 = pointPolygonTest(vertices_roi1, p, false); //判断是否在纸张内 10-1
double temp2 = pointPolygonTest(vertices_roi2, p, false); //判断是否在边缘区域内 10-1
//如果在纸张外,或者边缘内,视为非孔洞
if (temp1 < 0 || temp2 > 0)
if (temp1 <= 0 || temp2 >= 0)
{
enabled = false;
//qDebug() << rrect.center.x << ":" << rrect.center.y << "::::" << rrect.size.width << " - " << rrect.size.height;
break;
}
}

View File

@ -16,7 +16,15 @@
* 2022/07/18 v1.6.3 mask的一些逻辑错误
* 2022/07/18 v1.7 BUGborderSize逻辑,穿[borderSize, borderSize * 6]
* 2022/07/22 v1.7.1 BUG
* v1.7.1
* 2022/08/02 v1.7.2
* 2022/09/07 v1.8
* 2022/09/09 v1.8.1 BUG
* 2022/09/15 v1.8.2 BUG
* 2022/09/15 v1.8.3
* 2022/09/15 v1.8.4 BUG
* 2022/09/16 v1.9
* 2022/09/16 v1.9.1
* v1.9.1
* ====================================================
*/
@ -33,11 +41,11 @@ public:
CImageApplyOutHole();
/*
* borderSize [in]:
* edgeScale [in]:(0,0.5),0.1
* borderSize [in]:
* edgeScale [in]:[0, 0.5]0.1 0.1 0.1 0.1穿
* threshold [in]:
*/
CImageApplyOutHole(float borderSize, float edgeScale, double threshold);
CImageApplyOutHole(float borderSize, cv::Vec4f edgeScale, double threshold);
~CImageApplyOutHole(void);
@ -45,7 +53,7 @@ public:
float getBorderSize() { return m_borderSize; }
float getEdgeScale() { return m_edgeScale; }
cv::Vec4f getEdgeScale() { return m_edgeScale; }
double getThreshold() { return m_threshold; }
@ -62,8 +70,8 @@ private:
void getRoi(cv::RotatedRect rrect_front, cv::RotatedRect rrect_back, const cv::Size& srcSize_front, const cv::Size& srcSize_back, cv::Rect& roi_front,
cv::Rect& roi_back, cv::RotatedRect& mask_rotatedRect);
std::vector<std::vector<cv::Point> > filterPoly(std::vector<std::vector<cv::Point>>& contours, const std::vector<cv::Vec4i>& m, cv::RotatedRect roi,
float edgeScale, float areaThreshold);
std::vector<std::vector<cv::Point> > filterPoly(std::vector<std::vector<cv::Point>>& contours, std::vector<cv::Vec4i>& m, cv::RotatedRect roi,
cv::Vec4f edgeScale, float sideLengthLow);
cv::Scalar getBackGroudColor(const cv::Mat& image, const std::vector<cv::Point> pixelPoints);
@ -73,7 +81,7 @@ private:
private:
float m_borderSize;
float m_edgeScale;
cv::Vec4f m_edgeScale;
double m_threshold;
};

View File

@ -1854,6 +1854,7 @@ Result HuagaoDs::identityOpenDs(const Identity&) {
auto mech = data.currentItem<Int32>();
if (mech > 0 && mech < 50) {
m_scanparam->fillhole.fillholeratio = (int)mech;
m_scanparam->fillholeratio_up = m_scanparam->fillholeratio_down = m_scanparam->fillholeratio_left = m_scanparam->fillholeratio_right = int(mech);
return success();
}
return badValue();
@ -2416,6 +2417,9 @@ Result HuagaoDs::pendingXfersReset(const Identity&, PendingXfers& data) {
//guiIndicator.reset();
if (guiIndicator->GetSafeHwnd())
guiIndicator->DestroyWindow();
if (guiTwain.get()) {
((CTwainUI*)(guiTwain.get()))->EnableID_OKorID_Cancel(true);
}
return success();
}
@ -2652,7 +2656,6 @@ Twpp::Result HuagaoDs::pendingXfersStopFeeder(const Identity& origin, PendingXfe
if (scanner->IsConnected()) {
scanner->Stop_scan();
}
data.setCount(scanner->Get_IsImageQueueEmpty() ? 0 : 1);
return success();
}

Binary file not shown.

Binary file not shown.

Binary file not shown.