| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2444 人关注过本帖, 1 人收藏
标题:【转帖】VFP实现验证码去噪二值化算法
只看楼主 加入收藏
sam_jiang
Rank: 9Rank: 9Rank: 9
等 级:贵宾
威 望:14
帖 子:806
专家分:1231
注 册:2021-10-13
结帖率:96.83%
收藏(1)
已结贴  问题点数:20 回复次数:27 
【转帖】VFP实现验证码去噪二值化算法
网上看到一段vfp代码,可以将验证码图片的背景去掉,得到验证码字符。可用于车牌自动识别,但是可惜我把代码重新打了一遍,并没有成功,不知道原因,哪位大侠可以看看哪里出了问题???
附文章出处: https://www.
程序代码:
declare integer GdipSaveImageToStream in gdiplus.dll;
    long nlmage,;
    integer stream,;
    string csidEncoder,;
    long encoderparams
declare integer GdipCreateBitmapFromScan0 in gdiplus.dll; 
    integer nwidth,;
    integer nheight,;
    integer nstride,;
    integer npixelformat,;
    string @cscan0,;
    integer @nlmage
declare integer GdipGetImageHeight in gdiplus.dll;
    integer img,;
    integer @imgheight
declare integer GdipGetImageWidth in gdiplus.dll;
    integer img,;
    integer @imagewidth
declare integer GdipDrawImageRect in gdiplus.dll;
    integer ngraphics,;
    integer nImage,;
    single,;
    single,;
    single,;
    single
declare integer GdipGetImageGraphicsContext in gdiplus.dll;
    integer nImage,;
    integer @ngraphics
declare integer GdipLoadImageFromFile in gdiplus.dll;
    string wfilename,;
    integer @nImage
declare integer GdipDeleteGraphics in gdiplus.dll;
    integer graphics
declare long GdipDisposeImage in gdiplus.dll;
    long image
declare long CreateStreamOnHGlobal in ole32.dll;
    long hGlobal,;
    long fDeleteOnRelease,;
    long @ppstm && 创建字符流指针
declare integer GetProcessHeap in win32api
declare integer HeapAlloc in win32api long, long, long 
declare integer HeapFree in win32api integer,integer,integer
declare integer ReleaseStgMedium in ole32.dll string pmedium

store 0 to nimage, lnheight,lnwidth,newbitmap,ngraphics
clsidEncoder=0h00F47C55041AD3119A730000f81EF32E &&BMP
picfile=getpict()
GdipLoadImageFromFile(strconv(picfile+chr(0),5),@nimage)
GdipGetImageHeight(nimage,@lnheight)&&获得图像高度
GdipGetImageWidth(nimage,@lnwidth) &&获取图像宽度
pixelformat_32bppaRGB=0x0026200A
GdipCreateBitmapFromScan0(lnwidth,lnheight,0,pixelformat_32bppaRGB,0,@newbitmap)
GdipGetImageGraphicsContext(newbitmap,@ngraphics)
GdipDrawImageRect(ngraphics,nimage,0,0,lnwidth,lnheight)
GdipDisposeImage(nimage) &&此时nimage已没用,销毁
streamsize=lnheight*lnwidth*4+54 &&计算内存大小
&& 申请内存
hprocheap=GetProcessHeap()
Ihmemptr=HeapAlloc(hprocheap,0,streamsize) &&申请
IHStream=0
CreateStreamOnHGlobal(Ihmemptr,1,@IHStream) &&创建字符流指针
GdipSaveImageToStream(newbitmap,IHStream,@clsidEncoder,0)
Ihmemptr=Ihmemptr+54 &&像素开始地址
mR=0
mG=0
mB=0
RUM=10 &&二值化颜色容差
for i=0 to streamsize-54 step 4 &&统计平均颜色
    B=asc(sys(2600,Ihmemptr+i,1))
    G=asc(sys(2600,Ihmemptr+i+1,1))
    R=asc(sys(2600,Ihmemptr+i+2,1))
    mR=mR+R
    mG=mG+G
    mB=mB+B
endfor
mRGB=int((mR+mG+mB)/(lnheight*lnwidth*3)) &&自动二值化阀值
if mRGB<128 &&二值化阀值调节
    mRGB=mRGB+RUM
endif
NUM=mRGB &&自动对比度阀值
for i=0 to lnwidth-1
    for ii=0 to lnheight-1
        Ihmemptrtmp=Ihmemptr+(ii*lnwidth+i)*4 &&第i,ii点像素的地址
        B=asc(sys(2600,Ihmemptr+i,1))
        G=asc(sys(2600,Ihmemptr+i+1,1))
        R=asc(sys(2600,Ihmemptr+i+2,1))
        if int((R+G+B)/3)>mRGB
            sys(Ihmemptrtmp,4,0hFFFFFF00)
        endif
        if (R+G+B)/3>128
            uR=(255-R)/255
            uG=(255-G)/255
            uB=(255-B)/255
            R2=R+NUM*uR
            G2=G+NUM*uG
            B2=B+NUM*uB
            R=icase(R2>255,255,R2<0,0,R2)
            G=icase(G2>255,255,G2<0,0,G2)
            B=icase(B2>255,255,B2<0,0,B2)
        else
            R2=R-NUM
            G2=G-NUM
            B2=B-NUM
            R=icase(R2>255,255,R2<0,0,R2)
            G=icase(G2>255,255,G2<0,0,G2)
            B=icase(B2>255,255,B2<0,0,B2)
        endif
        if int((R+G+B)/3)>mRGB
            sys(Ihmemptrtmp,4,0hFFFFFF00)
        else
            sys(Ihmemptrtmp,3,0hFFFFFF00)
        endif
    endfor
endfor
strtofile(sys(2600,Ihmemptr-54,streamsize),"6.bmp") &&保存到本地
HeapFree(hprocheap,0,Ihmemptr)
ReleaseStgMedium(0h04000000+bintoc(Ihmemptr,"4rs")+0h00000000)
GdipDisposeImage(newbitmap)
GdipDeleteGraphics(ngraphics)


附上验证码测试图片
图片附件: 游客没有浏览图片的权限,请 登录注册
搜索更多相关主题的帖子: 验证码 二值化 integer long dll 
2022-10-01 23:03
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10609
专家分:43210
注 册:2014-5-20
收藏
得分:0 
测试执行到 ReleaseStgMedium(0h04000000+bintoc(Ihmemptr,"4rs")+0h00000000) 时出现异常,是不是参数不对? 下面有 HeapFree(hprocheap,0,Ihmemptr) ,这句是不是可以忽略?

转换输出文件正常,看了一下那个 6.bmp 好象没什么变化?

这段代码有一个BUG --- 内存泄漏:
&& 申请内存
......
Ihmemptr=HeapAlloc(hprocheap,0,streamsize) &&申请
......
Ihmemptr=Ihmemptr+54 &&像素开始地址
......
HeapFree(hprocheap,0,Ihmemptr)
......

这句有问题:Ihmemptr=Ihmemptr+54
从HeapAlloc到HeapFree,Ihmemptr应该是始终如一


2022-10-02 06:49
schtg
Rank: 12Rank: 12Rank: 12
来 自:Usa
等 级:贵宾
威 望:67
帖 子:1790
专家分:3389
注 册:2012-2-29
收藏
得分:0 
回复 楼主 sam_jiang
二值化没有成功!
原图
图片附件: 游客没有浏览图片的权限,请 登录注册

二值化后图像
图片附件: 游客没有浏览图片的权限,请 登录注册
2022-10-02 07:14
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10609
专家分:43210
注 册:2014-5-20
收藏(1)
得分:0 
ReleaseStgMedium(0h04000000+bintoc(Ihmemptr,"4rs")+0h00000000)
参数是一个STGMEDIUM结构,成员tymed设置为4(TYMED_ISTREAM),是由IStream指针标识的流对象。
Ihmemptr是分配的内存地址,不是指针标识的流对象。
这时个人认为应该用 CreateStreamOnHGlobal(Ihmemptr,1,@IHStream) 创建的流指针对象IHStream:
ReleaseStgMedium(0h04000000+bintoc(IHStream,"4rs")+0h00000000)

2022-10-02 07:15
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10609
专家分:43210
注 册:2014-5-20
收藏(1)
得分:20 
简单写一个玩玩
转换前
图片附件: 游客没有浏览图片的权限,请 登录注册

转换后
图片附件: 游客没有浏览图片的权限,请 登录注册

程序代码:
cDefPath = ADDBS(JUSTPATH(SYS(16)))
SET DEFAULT TO (cDefPath)
** 输入/输出文件
inFile  = cDefPath + "验证码测试图片.png"
outFile = cDefPath + "验证码测试图片.bmp"

DECLARE long GdiplusStartup              IN gdiplus long@,string@,long
DECLARE long GdiplusShutdown             IN gdiplus long
DECLARE long GdipCreateBitmapFromScan0   IN gdiplus long,long,long,long,string@,long@
DECLARE long GdipGetImageGraphicsContext IN gdiplus long,long@
DECLARE long GdipLoadImageFromFile       IN gdiplus string@,long@
DECLARE long GdipGetImageWidth           IN gdiplus long,long@
DECLARE long GdipGetImageHeight          IN gdiplus long,long@
DECLARE long GdipSaveImageToFile         IN gdiplus long,string@,string@,long
DECLARE long GdipDrawImageRectI          IN gdiplus long,long,long,long,long,long
DECLARE long GdipDisposeImage            IN gdiplus long
DECLARE long GdipDeleteGraphics          IN gdiplus long
DECLARE long GdipBitmapLockBits          IN GDIPLUS long, string@, long, long, string@
DECLARE long GdipBitmapUnlockBits        IN GDIPLUS long, string@
DECLARE long GdipCreateFromHDC           IN gdiplus long,long@

DECLARE long GetDC     IN user32 long
DECLARE long ReleaseDC IN user32 long,long

#define ImageLockModeReadWrite    0x0003
#define PixelFormat_32bppARGB     0x0026200A

** 初始化GDI
stGSI = 0h01000000000000000000000000000000
pGDI  = 0
GdiplusStartup(@pGDI, @stGSI, 0)
** 载入文件
pImage = 0
GdipLoadImageFromFile(STRCONV(inFile+0h00,5), @pImage)
nWidth  = 0
nHeight = 0
GdipGetImageWidth(pImage, @nWidth)
GdipGetImageHeight(pImage, @nHeight)
** 统一转换为32位ARGB格式
pBitmap = 0
GdipCreateBitmapFromScan0(nWidth, nHeight, 0, PixelFormat_32bppARGB, 0, @pBitmap)
pGraphics = 0
GdipGetImageGraphicsContext(pBitmap, @pGraphics)
GdipDrawImageRectI(pGraphics, pImage, 0, 0, nWidth, nHeight)
GdipDisposeImage(pImage)
** 获取缓存图像数据地址和大小
stRct = REPLICATE(0h00,8) + BINTOC(nWidth,"4rs") + BINTOC(nHeight,"4rs")
stBmpData = REPLICATE(0h00, 24)    && BITMAPDATA 结构
GdipBitmapLockBits(pBitmap, @stRct, ImageLockModeReadWrite, PixelFormat_32bppARGB, @stBmpData)
pData  = CTOBIN(SUBSTR(stBmpData,17,4),"4rs")   && 图像数据首址
nCount = nWidth * nHeight * 4                   && 图像数据字总节数(32位4字节)
** 转换图像数据
Gray(pData, nCount)    && 灰度化
Binary(pData, nCount)  && 二值化
GdipBitmapUnlockBits(pBitmap, @stBmpData)    &&更新数据
** 输出文件
CLSID_BMP = 0h00F47C55041AD3119A730000F81EF32E
GdipSaveImageToFile(pBitmap, STRCONV(outFile+0h00,5), CLSID_BMP, 0)

GdipDeleteGraphics(pGraphics)
GdipDisposeImage(pBitmap)
** 显示图片
DrawImageWindowFromFile(_screen.hWnd, 10, 10, nWidth, nHeight, outFile)
GdiplusShutdown(pGDI)
CLEAR ALL
RETURN

** 灰度化
FUNCTION Gray(pData, nCount)
    FOR  i=0 TO nCount-1 STEP 4
        avg = 0.114 * ASC(SYS(2600,pData+i,1));
            + 0.587 * ASC(SYS(2600,pData+i+1,1));
            + 0.299 * ASC(SYS(2600,pData+i+2,1))
        avg = INT(avg)
        SYS(2600,pData+i,3,CHR(avg)+CHR(avg)+CHR(avg))
    ENDFOR
ENDFUNC

** 二值化
FUNCTION Binary(pData, nCount)
    * 计算阈值
    k1 = 127    && 随机取0~255
    k2 = 0
    DO WHILE k1 != k2
        k2 = k1
        avgPix = 0
        avgBac = 0
        avgPixCnt = 0
        FOR i=0 TO nCount-1 STEP 4
            FOR j=i TO i+2    && R、G、B,不考虑A
                ki = ASC(SYS(2600,pData+j,1))
                IF ki < k2
                    avgPix = avgPix + ki    && 像素数据
                    avgPixCnt = avgPixCnt + 1
                ELSE
                    avgBac = avgBac + ki    && 背景数据
                ENDIF
            ENDFOR
        ENDFOR 
        avgPix = INT(avgPix / avgPixCnt)
        avgBac = INT(avgBac / (nCount-avgPixCnt))
        k1 = INT((avgPix+avgBac) / 2)
    ENDDO
    * 二值化
    FOR i=0 TO nCount-1 STEP 4
        FOR j=i TO i+2
            ki = IIF(ASC(SYS(2600,pData+j,1)) < k1, 0, 255)
            SYS(2600, pData+j, 1, CHR(ki))
        ENDFOR
    ENDFOR  
ENDFUNC

** 显示图片
FUNCTION DrawImageWindowFromFile(hWnd, nX, nY, nWidth, nHeight, cFileName)
    LOCAL hDC, pImage, pGraphics 
    pImage = 0
    GdipLoadImageFromFile(STRCONV(cFileName+0h00,5), @pImage)
    hDC = GetDC(hWnd)
    pGraphics = 0
    GdipCreateFromHDC(hDC, @pGraphics)
    GdipDrawImageRectI(pGraphics, pImage, nX, nY, nWidth, nHeight)
    GdipDisposeImage(pImage)
    GdipDeleteGraphics(pGraphics)
    ReleaseDC(hWnd, hDC)
ENDFUNC


[此贴子已经被作者于2022-10-2 18:37编辑过]

2022-10-02 18:14
schtg
Rank: 12Rank: 12Rank: 12
来 自:Usa
等 级:贵宾
威 望:67
帖 子:1790
专家分:3389
注 册:2012-2-29
收藏
得分:0 
@吹版,威武,谢谢!
2022-10-02 19:17
mywisdom88
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:191
帖 子:3147
专家分:8408
注 册:2015-3-25
收藏
得分:0 
吹版,然后,怎么把2427转为文字?
2022-10-03 15:32
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:10609
专家分:43210
注 册:2014-5-20
收藏
得分:0 
以下是引用mywisdom88在2022-10-3 15:32:28的发言:

吹版,然后,怎么把2427转为文字?

OCR图文识别,没试过
以前好象有讨论过,有这方面的控件或接口API。
也有人用过 Microsoft Office Document Imaging (MODI.Document)控件的OCR方法,现在不知能不能用。
楼主的目的应该也是这个
2022-10-03 17:20
nbwww
Rank: 8Rank: 8
等 级:贵宾
威 望:11
帖 子:334
专家分:810
注 册:2021-1-9
收藏
得分:0 
https://www.
吹版如果有空的话能不能试下这个  VFP能不能直接调用
2022-10-03 20:01
nbwww
Rank: 8Rank: 8
等 级:贵宾
威 望:11
帖 子:334
专家分:810
注 册:2021-1-9
收藏
得分:0 
这个不用去噪  直接就能识别出来
2022-10-03 20:02
快速回复:【转帖】VFP实现验证码去噪二值化算法
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.053798 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved