Jmetertess4j识别图形验证码因工作远程Windows机器的运行内存限制OCRserver存在无法启动的情况因此寻找其他方式替代。找到了tess4j及其相关依赖可以实现。初始代码由豆包完成后续使用deepseek、千问、元宝优化最终完成。一、使用工具JMeter压测平台通过 JSR223 Sampler 执行脚本GroovyJMeter 内嵌脚本语言负责图像预处理逻辑Tess4JTesseract OCR 的 Java 封装基于 JNA供 Groovy 直接调用二、JAR 包放置位置将 Tess4J 及其所有依赖 JAR 放入 JMeter 的lib/目录JMETER_HOME/└── lib/├── tess4j-5.9.0.jar├── lept4j-1.18.0.jar├── jna-platform-5.14.0.jar├── jna-5.14.0.jar└── jai-imageio-core-1.4.0.jar三、语言包放置位置将 Tesseract 英文语言数据文件放入lib/tessdata/目录JMETER_HOME/└── lib/└── tessdata/└── eng.traineddata脚本中setDatapath(../lib/tessdata)使用相对路径需确保从 JMeter 的bin/目录启动默认启动方式否则需按实际启动位置调整路径。四、运行方式JMeter 中新建http request用来获取图形验证码再添加后置处理器JSR223 后置处理器语言选择Groovy将脚本粘贴进去importjava.io.FileOutputStreamimportjava.text.SimpleDateFormatimportnet.sourceforge.tess4j.Tesseractimportjava.io.Fileimportjavax.imageio.ImageIOimportjava.awt.image.BufferedImageimportjava.awt.Colorimportjava.awt.Graphics2Dimportjava.awt.RenderingHintsbyte[]imageDataprev.getResponseData()def timestampnewSimpleDateFormat(yyyyMMddHHmmssSSS).format(newDate())def random(int)(Math.random()*1000)def savePath./img/newFile(savePath).mkdirs()def originalFilenewFile(savePathcaptcha_orig_${timestamp}_${random}.png)def processedFilenewFile(savePathcaptcha_proc_${timestamp}_${random}.png)def debugBinFilenewFile(savePathdebug_bin_${timestamp}_${random}.png)try{FileOutputStreamfosnewFileOutputStream(originalFile)fos.write(imageData)fos.close()BufferedImageimgImageIO.read(originalFile)if(imgnull){vars.put(vCode,);return}intwidthimg.getWidth()intheightimg.getHeight()// 1. 动态阈值二值化 longsumBrightness0for(inty0;yheight;y){for(intx0;xwidth;x){intrgbimg.getRGB(x,y)intr(rgb16)0xFFintg(rgb8)0xFFintbrgb0xFFsumBrightness(rgb)/3}}intavgBright(int)(sumBrightness/(width*height))intdynamicThresholdMath.max(180,avgBright-20)log.info(☀️ 动态阈值: ${dynamicThreshold} (平均亮度: ${avgBright}))for(inty0;yheight;y){for(intx0;xwidth;x){intrgbimg.getRGB(x,y)intr(rgb16)0xFFintg(rgb8)0xFFintbrgb0xFFintmax_valMath.max(r,Math.max(g,b))intmin_valMath.min(r,Math.min(g,b))if(max_valdynamicThreshold||(max_val-min_val)30){img.setRGB(x,y,Color.BLACK.getRGB())}else{img.setRGB(x,y,Color.WHITE.getRGB())}}}ImageIO.write(img,png,debugBinFile)// 2. 原图 3x3 开运算 BufferedImageerodednewBufferedImage(width,height,BufferedImage.TYPE_INT_RGB)for(inty0;yheight;y){for(intx0;xwidth;x){if(x0||y0||xwidth-1||yheight-1){eroded.setRGB(x,y,img.getRGB(x,y));continue}if(img.getRGB(x,y)Color.BLACK.getRGB()){booleanhasWhitefalsefor(intdy-1;dy1;dy){for(intdx-1;dx1;dx){if(img.getRGB(xdx,ydy)Color.WHITE.getRGB()){hasWhitetrue;break}}if(hasWhite)break}eroded.setRGB(x,y,hasWhite?Color.WHITE.getRGB():Color.BLACK.getRGB())}else{eroded.setRGB(x,y,Color.WHITE.getRGB())}}}// 膨胀恢复BufferedImagedilatednewBufferedImage(width,height,BufferedImage.TYPE_INT_RGB)for(inty0;yheight;y){for(intx0;xwidth;x){if(x0||y0||xwidth-1||yheight-1){dilated.setRGB(x,y,eroded.getRGB(x,y));continue}booleanhasBlackfalsefor(intdy-1;dy1;dy){for(intdx-1;dx1;dx){if(eroded.getRGB(xdx,ydy)Color.BLACK.getRGB()){hasBlacktrue;break}}if(hasBlack)break}dilated.setRGB(x,y,hasBlack?Color.BLACK.getRGB():Color.WHITE.getRGB())}}imgdilated// 3. 放大 4 倍 intscale4intnewWwidth*scaleintnewHheight*scaleBufferedImagescaledImgnewBufferedImage(newW,newH,BufferedImage.TYPE_INT_RGB)Graphics2Dg2dscaledImg.createGraphics()g2d.setColor(Color.WHITE)g2d.fillRect(0,0,newW,newH)g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)g2d.drawImage(img,0,0,newW,newH,null)g2d.dispose()imgscaledImg widthnewW heightnewH// 4. 连通域过滤 boolean[][]visitednewboolean[width][height]intminValidXwidth,maxValidX0,minValidYheight,maxValidY0for(inty0;yheight;y){for(intx0;xwidth;x){if(img.getRGB(x,y)Color.BLACK.getRGB()!visited[x][y]){def queue[[x,y]]def comp[]visited[x][y]trueintcMinXx,cMaxXx,cMinYy,cMaxYy,count0while(queue){def(cx,cy)queue.remove(0)comp.add([cx,cy])countif(cxcMinX)cMinXcxif(cxcMaxX)cMaxXcxif(cycMinY)cMinYcyif(cycMaxY)cMaxYcyfor(int[]d:[[-1,0],[1,0],[0,-1],[0,1],[-1,-1],[-1,1],[1,-1],[1,1]]){intnxcxd[0],nycyd[1]if(nx0nxwidthny0nyheight!visited[nx][ny]img.getRGB(nx,ny)Color.BLACK.getRGB()){visited[nx][ny]truequeue.add([nx,ny])}}}intcompWcMaxX-cMinX1intcompHcMaxY-cMinY1doubledensity(double)count/(compW*compH)booleanisNoise(count100)||(compH30)||(density0.15compW50)if(isNoise){for(def p in comp)img.setRGB(p[0],p[1],Color.WHITE.getRGB())}else{if(cMinXminValidX)minValidXcMinXif(cMaxXmaxValidX)maxValidXcMaxXif(cMinYminValidY)minValidYcMinYif(cMaxYmaxValidY)maxValidYcMaxY}}}}// 5. 裁剪与补白 if(maxValidXminValidXmaxValidYminValidY){intcropXMath.max(0,minValidX-10)intcropYMath.max(0,minValidY-10)intcropWMath.min(width-cropX,(maxValidX-minValidX)20)intcropHMath.min(height-cropY,(maxValidY-minValidY)20)BufferedImagecroppedimg.getSubimage(cropX,cropY,cropW,cropH)intpadding40BufferedImagepaddedImgnewBufferedImage(cropWpadding*2,cropHpadding*2,BufferedImage.TYPE_INT_RGB)Graphics2DgPadpaddedImg.createGraphics()gPad.setColor(Color.WHITE)gPad.fillRect(0,0,paddedImg.getWidth(),paddedImg.getHeight())gPad.drawImage(cropped,padding,padding,null)gPad.dispose()imgpaddedImg}ImageIO.write(img,png,processedFile)// 6. Tesseract 识别 TesseracttesseractnewTesseract()tesseract.setDatapath(../lib/tessdata)tesseract.setLanguage(eng)tesseract.setOcrEngineMode(1)tesseract.setTessVariable(tessedit_char_whitelist,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ)tesseract.setTessVariable(user_defined_dpi,300)tesseract.setTessVariable(preserve_interword_spaces,0)tesseract.setTessVariable(load_system_dawg,0)tesseract.setTessVariable(load_freq_dawg,0)tesseract.setTessVariable(tessedit_enable_doc_dict,0)// 第一道防线PSM 7 (单行)tesseract.setTessVariable(tessedit_pageseg_mode,7)StringresultRawtesseract.doOCR(processedFile)def resultresultRaw.replaceAll([^0-9A-Za-z],)// 第二道防线如果 PSM 7 没凑够 4 个降级为 PSM 8 (单词模式)if(result.length()4){log.info(⚠️ PSM 7 识别残缺(${result})触发 PSM 8 重试...)tesseract.setTessVariable(tessedit_pageseg_mode,8)resultRawtesseract.doOCR(processedFile)def resultRetryresultRaw.replaceAll([^0-9A-Za-z],)if(resultRetry.length()result.length())resultresultRetry}// 7. 结果校验与兜底提取 if(result.length()!4){log.warn(⚠️ 识别结果非4位: ${result} (原始输出: ${resultRaw.replaceAll(\\s, )})尝试从原始输出重新提取...)// Tesseract 有时会在中间插入空格或换行重新从原始输出提取def cleanedresultRaw.replaceAll([^0-9A-Za-z],)if(cleaned.length()4){resultcleaned.substring(0,4)log.info( 重新提取结果: ${result})}}if(result.length()4)resultresult.substring(0,4)// 8. 可选特定字符后处理 // 如果跑批中发现固定混淆对取消下面注释并按需修改// 例验证码不含数字0把O映射为0// result result.replace(O, 0)// 例验证码不含数字1把I/l映射为1// result result.replace(I, 1).replace(l, 1)vars.put(vCode,result)log.info(✅最终验证码 vCode${result})}catch(Exceptione){log.error(❌ 处理异常${e.getMessage()},e)vars.put(vCode,)}识别结果自动写入变量vCode后续请求通过${vCode}引用五、调试输出脚本会在 JMeter 启动目录下自动创建img/文件夹保存以下文件供对比排查文件说明captcha_orig_*原始验证码图片debug_bin_*二值化后的调试图captcha_proc_*最终送入 OCR 的处理图
企业数字化 ERP 产品动态
相关推荐
前后端问题 一 前端1.1 现象:Access to XMLHttpRequest at ... from origin ... has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource.说明:如果后端iis部署,也有可能不是跨域的问题,而是应用程序池里对应的程序停了.1.2 js在 … · 2026/11/11 8:38:42
设置 Excel 表的格式 Excel 提供有好多那种预先已经设定好的表格样式, 这些样式拿来用可以使得表格的格式被很快地就设置好。那些预定义的表格样式要是没有办法满足你的需求的话, 你就可以去创建一个自定义的表格样式, 并且把这个自定义的表格样式应用上去。虽然只能把那些自定义的表格样式给删除掉… · 2026/11/11 8:38:41
CI 老被 Docker Hub 的 429 卡住,自建 Registry 把镜像搬进内网 下午三点,二十几个 CI 任务挤在同一条出口 IP 上拉基础镜像,Docker Hub 甩回 429(Too Many Requests),流水线整批卡住,等下一个六小时窗口。这不是段子,Docker Hub 的限流政策白纸黑字写在官方 … · 2026/11/7 22:46:45
工地太忙根本没时间复习考试?3证书电工证在哪考全攻略 工地太忙根本没时间复习考试?3证书电工证在哪考全攻略 工地太忙根本没时间复习考试,特别是刚毕业的河南工程类学生,一边要适应高强度的工作节奏,一边还要应对3证书电工证的考试和复审。你是不是也遇到过这样的情况?别急,今天我们就来聊聊… · 2026/11/11 8:38:07
电工证 北京模拟考试题库 电工证 北京怎么转过来多少钱 之前考的证在外省能不能转过来?这个问题几乎每个准备在北京发展或者已经在京工作的电工都会问。现在越来越多的人拿着外省的电工证来北京找工作,但不少人都遇到过“证不能用”“不能报名考试”的问题,还有的担心 多少钱… · 2026/11/11 8:38:07
昆明考低压电工证过期了不知道怎么复审延期?良心建议来了 昆明考低压电工证过期了不知道怎么复审延期?良心建议来了 证书过期了不知道怎么复审延期,这不是个例,是很多在昆明考低压电工证的工友真实经历。尤其是像我这样在河南工地干了十几年的建筑工人,走南闯北,最怕的就是证书到期了没人提醒,一拖再拖,等到用… · 2026/11/11 8:38:07
上海电工证初级哪里考多久拿证 工地太忙也能考过 上海电工证初级哪里考多久拿证 工地太忙也能考过 工地太忙根本没时间复习考试,但 电工证 又是上岗的“通行证”,尤其是对于 初级电工证 来说,拿到证是进入行业的第一步。很多人以为考证是件复杂的事,其实只要方法对, 多久拿证… · 2026/11/11 8:38:07
低压电工证图案题怎么考?官方报名入口在这里,急需上岗的别错过 低压电工证图案题怎么考?官方报名入口在这里,急需上岗的别错过 你还在为找不到电工工作发愁?低压电工证图案题是上岗的硬门槛,别再等了!现在就通过【官方报名入口】报名,别让证书耽误你的职业生涯。 低压电工证图案题到底考什么?别被吓退… · 2026/11/11 8:38:07
黄骅市低压电工证在哪考?外省证能转过来吗?别再被坑了 黄骅市低压电工证在哪考?外省证能转过来吗?别再被坑了 之前考的证在外省能不能转过来?这是很多想在黄骅市从事低压电工工作的朋友最关心的问题之一。如果你是刚从外地过来,手里拿着低压电工证,或者正在考虑报考黄骅市低压电工证,那么你肯定想知道:… · 2026/11/11 8:35:00
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/10 2:16:10
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/10 17:46:39
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/10 17:11:43
_中职电工证最新政策解读 中职电工证在哪考?工地太忙根本没时间复习考试的救星来了 工地太忙根本没时间复习考试,这是很多正在备考 中职电工证 的建筑工友的真实写照。你不是不想考,而是真的没时间。但别担心,这期内容是 新乡初次报考人员… · 2026/11/11 0:00:52
昆明电工证培训价格值不值得考?老电工告诉你真相 昆明电工证培训价格值不值得考?老电工告诉你真相 之前考的证在外省能不能转过来?这个问题是很多在外地打工、换城市生活、或者是想在昆明发展的朋友最关心的。尤其是现在云南发展快,很多企业都在招持证上岗的电工,如果你手头有证但不在本地,那这个证是不… · 2026/11/11 0:00:52
特种电缆张工网上查询全攻略:外省证如何转过来 特种电缆张工网上查询全攻略:外省证如何转过来 之前考的证在外省能不能转过来?这是很多特种作业从业者最关心的问题,尤其是像特种电缆张工这类证书,直接关系到日常工作是否合规、能否顺利晋升。很多小伙伴因为证书归属地的问题,陷入职业发展的瓶颈,甚至… · 2026/11/11 0:00:52
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 20:19:29
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 19:22:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 17:00:47