| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
共有 740 人关注过本帖
标题:基于zxing的条形码轮廓的机器识别++原创哦~
只看楼主 加入收藏
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1246
专家分:1687
注 册:2021-10-13
收藏
 问题点数:0 回复次数:3 
基于zxing的条形码轮廓的机器识别++原创哦~
前天傍晚车子被人撞了,昨天拿去修,估计要3-4天。终于有时间坐下来好好研究这个条形码了。

通过2天的摸索(当然之前也研究过,只是没有这次这么专注),大致了解了zxing 的工作原理。由于不是很懂C++和C,同时下载了c++源码和C语言封装的源代码,还下载了不同的版本,最新的好像是3.0。
分别用2种语言去实现了对条码轮廓的机器识别,待会一一把算法讲解给大家,理解了它的工作方式,我们甚至可以用vfp语言独立去完成机器识别,都不用任何其他的库,当然估计没几个人愿意去尝试。

zxing的所有操作基于imageview,我叫它图像视图,这是原生C++类,将原始的字节数组、宽度、高度等信息“打包”成一个统一的对象,以供后续的解码函数使用。
第一步就是要传给zxing图像数据,就是原始的字节数组,被称为rawimagedata, 这里我碰到第一个坑,它跟后来要讲的rawdata很难区分,不太容易搞懂。zxing不提供如何从图片中获得rawimagedata的方法,要我们自己提供,真坑爹啊,你说你搞都搞了,还差那么一点吗,就不能一起提供吗!好在它的这个原始字节数据就是屏幕/图像上所有像素的数据,以RGBA,BGRA等格式顺序排列,可以是彩色,可以是灰度,反正它内部最终是转为为黑白数据的,研究过bmp位图文件格式的应该都知道,跟这个位图文件的数据区很像,但是位图文件的末尾通常有行填充,即每行像素数据的字节数必须是4的倍数。这又是一个让人抓狂的问题,从bmp文件中提取这个数据,还得剔除行填充内容。如果是其他格式的图片文件,比如jpg,tif,png等文件需要转换以后再提取像素数据,由此可见,烦的一逼。。。所幸我以前写了个功能强大的imageconverter类就是用来做这个事的,可以实现不同文件格式的互换,截图,剪贴簿,放大,缩小,旋转,灰度化,二值化等等,纯vfp代码实现。创建imageview的是ZXing_ImageView_new 函数,
c++ 代码:
ZXing_ImageView* iv = ZXing_ImageView_new(imagedata, width, height, ZXing_ImageFormat_Lum, 0, 0);
vfp 申明代码:
DECLARE INTEGER ZXing_ImageView_new IN zxingc.dll STRING data, INTEGER width, INTEGER height, INTEGER format, INTEGER rowStride, INTEGER pixStride

第二步,zxing从这个rawimagedata中开始识别条码,主要实现函数是ZXing_ReadBarcodes;
c++代码:
zxing_Results* results = zxing_ReadBarcodes(iv, opts);
vfp 申明代码:
DECLARE INTEGER ZXing_ReadBarcodes IN zxingc.dll INTEGER iv, INTEGER opts
第一个参数就是上面的imageview,第二个参数是一个结构体,用来设置解码选项,这里可以设定解码选项是某种或几种格式的二维码,以及某种或者几种一维码,也可以全部,类似flag,可以叠加组合。
常见选项是tryHarder(是否费力),tryRotate(是否旋转),tryInvert(是否反白),isPure(是否纯净),不同的选项识别的速度不同。这个结构体不能直接访问,只能通过一些列的api函数去创建,访问,以及设置。

第三步,分析识别出来的条码。识别出来的结果保存在一个叫做results的对象数组里,每一个result对象就是一个barcode对象(c语言里把它叫barcode对象),主要属性就是text(条码文本),barcodeFormat(条码格式),resultpoints(旧版的条码坐标数组),新版的叫Position(C语言封装的就是这个,可以直接拿来用,直接就是条码的外接最小矩形),我开始傻傻的搞不清,做了2套定位条码轮廓的识别方案。等会主要就讲根据resultpoints的机器识别方法。这个对象数组有一些列的方法,用来执行数组操作的,常用的比如size(),获得数组的大小;at()根据索引引用;empty()判断是否为空,为空就是没有识别出条码;push_back()添加一个数组元素等。

最后,我们根据得到条码坐标去做条码轮廓的机器识别。
这里的resultpoints是一个坑,不小心又要跌倒吃不少苦头,它识别一维条码后返回的是2个点,识别二维码的时候返回有的是3个点,有的是4个点,不同的格式返回不同的数值,一步步摸索,不知道拐了几个弯,碰了几次壁。。。
扫一维条码,我们可以得到2个点,这两个点的连线和条码相交,位置主打一个随心所欲,不知道源码是怎么实现的,有时在条码外侧,有时在条码外侧。二维条码得到的点是二维码中的三/四个定位的小方块的中心,这时的四边形倒是在条码的内部。

核心算法:

第一,找上下边界,确定四个点的Y坐标;

一维条码稍微麻烦点,如果条码是水平的,那比较简单,通常条码的宽高比在2.5~5:1, 假设2点间的距离为150,算它是5:1,那么高度大概是30,根据2点连线,我们往上跳跃20~30个像素(20%),如果此时的线段上所有的像素是白色,我们可以认为,已经到达条码的空白静区,那么我们在逐像素往下移;如果这条线段上有黑色像素,那么说明我们的边界还在条码内部,那么再按5个像素步长外移,如此循环,每次跳跃都要检查线段是否全白,一旦检查出整条线段全是白色,再逐像素回退,直到线段上有黑色像素,这时就到达我们的上边界,为什么要设计跳跃式检测呢?主要是为了判断效率,比逐像素往外扩展要快一点。由于实际上zxing给出的2个端点位置是偏上部的,并不是条码中心位置,估计她的扫描原理也是跳跃式的,否则没法解释这2点的位置会因为条码不同位置会不同;接下来找下边界,由于初始线段是偏上的,所以首次跳跃可以多点,30-50个像素(约30%),再5个像素步长下移,同上边界一样的原理,考虑到条码下面可能有文字,步长可以适当加大点,这样有大几率越过字符和条码之间的空隙,把文字也包括在里面。当然要做边界检查,万一条码是靠近图像的边缘,可能一次跳跃就跑到图像外面去了,一旦跃出边界就以边界为边,再慢慢回退。

第二,找左右边界,确定四个点的X坐标;

左右边界的寻找方法和上下边界类似,根据我们找出来的2条上下边界,连接成左右2条线段,zxing的初始端点基本上在条码边界附近,有的偏外,有的在条码内部。我们直接5~10个步长往外移,如果竖直的线段全白,就可以逐像素回退,直到检测到线段上有黑点,这样就确定了X坐标,所有点外移一个像素,就是这个条码的最小外接矩形,也就是轮廓了。

第三,假如条码是倾斜的,怎么办?这个方法好像要失效了。。。倾斜的条码算法稍微复杂些,首先我们得知道,斜线在像素点阵下是个什么样的?斜线其实在像素点阵下就是一组折线,肉眼看起来有锯齿状,这时找边界就不是简单的上下移动及左右移动了,同时还得左右及上下移动。这时我们要引入浮点像素,才能解决这个问题。假设zxing给出的初始端点是(30,20),和(180,40)意味着有个顺时针方向的倾斜(左斜),我们搜索边界时,端点是向右上移动的,如果是右斜,那么端点是往左上移动。按照我么前面的跳跃法,先往上跳20个像素,那么向右应该跳跃多少像素?我们只需看高度差和水平差,这2个点的差是(150,20),我们向上移动了10个像素,那么就应该往右移动1.3333个点,这时左上的点的坐标(31.3333, 10),右下的坐标是(181.3333,30),此时对线段上所有的浮点像素进行判断是否为白色,就取样20个点,就得把边界线分成20份,求出每个点的坐标,分别是(31.3333, 38.8333, 46.3333, 53.8333, 61.3333, 68.8333, 76.3333, 83.8333, 91.3333, 98.8333, 106.3333, 113.8333, 121.3333, 128.8333, 136.3333, 143.8333, 151.3333, 158.8333, 166.3333, 173.8333, 181.3333)和(10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30)对小数进行四舍五入,就可以进行判断了。(31~38,10),(39~46,11)...(174~181,30),正好是一组折线,同理可以找出其他3条边的边界线。

至于二维码,就更简单了,去掉右下角的无效点,给它计算一个端点,使四个点正好成为矩形,沿四周扩展,先上下扩展,再左右扩展,倾斜的情况和一维码相同。水平的条码其实也可以沿用倾斜的方法算,这样可以统一代码。

这个算法非常强大,速度很快的,由于我是在C++/c语言实现的,代码就不上传了。知道了算法,用vfp也可以做到,就看有没有人愿意去花时间实践一下了。。。

另外C语言版的zxing封装库,那个position是直接给出条码的边框的,但是如果包含文字,它会把文字剔除的,个人觉得那样不好。这个position也是个坑鬼,实现的函数是ZXing_Barcode_position,这个函数参数是barcode或result类,返回值是c语言的结构体,当时郁闷得不得了,vfp申明integer(指针)没有用,申明成string也不行。。。实在没有办法,只好在c语言层面动手改了,为vfp做适配,所有输出函数都加了“_vfp”后缀,主要实现函数ZXing_Barcode_position_vfp。

vfp测试代码:使用libzxing.dll 和我自己封装的zxing4vfp.dll
程序代码:
CLEAR
CLEAR DLLS

* 声明 DLL 函数
DECLARE INTEGER _ZxingDetectAndDecodeFromFile@24 IN "zxing4vfp.dll" ;
    AS ZxingDetectAndDecodeFromFile ;
    INTEGER, INTEGER, INTEGER, INTEGER, INTEGER, INTEGER

* 测试图片路径
cImageFile = GETFILE("bmp;png;jpg")

IF EMPTY(cImageFile)
    ? "未选择文件"
    RETURN
ENDIF

* 分配内存
DECLARE INTEGER GlobalAlloc IN kernel32 INTEGER, INTEGER
DECLARE INTEGER GlobalFree IN kernel32 INTEGER

nPathMem = GlobalAlloc(0, LEN(cImageFile) + 1)
nTextMem = GlobalAlloc(0, 256)
nTypeMem = GlobalAlloc(0, 64)
nCornerMem = GlobalAlloc(0, 32)  && 8int (4 个角点 × 2 坐标)

IF nPathMem = 0 OR nTextMem = 0 OR nTypeMem = 0 OR nCornerMem = 0
    ? "内存分配失败"
    RETURN
ENDIF

* 写入数据
= SYS(2600, nPathMem, LEN(cImageFile), cImageFile)
= SYS(2600, nPathMem + LEN(cImageFile), 1, CHR(0))
= SYS(2600, nTextMem, 256, SPACE(256))
= SYS(2600, nTypeMem, 64, SPACE(64))
= SYS(2600, nCornerMem, 32, REPLICATE(CHR(0), 32))

* 调用 DLL
nResult = ZxingDetectAndDecodeFromFile(;
    nPathMem, nTextMem, 256, nTypeMem, 64, nCornerMem)

* 读取结果
cText = SYS(2600, nTextMem, 256)
cType = SYS(2600, nTypeMem, 64)
cCorners = SYS(2600, nCornerMem, 32)

* 释放内存
GlobalFree(nPathMem)
GlobalFree(nTextMem)
GlobalFree(nTypeMem)
GlobalFree(nCornerMem)

* 处理结果
IF nResult = 1
    * 去掉 CHR(0) 后面的内容
    cText = LEFT(cText, AT(CHR(0), cText) - 1)
    cType = LEFT(cType, AT(CHR(0), cType) - 1)
    
    ? "========================================"
    ? "解码结果: " + cText
    ? "条码类型: " + cType
    ? ""
    ? "ZXing 角点坐标:"
    
    * 解析 4 个角点 (8int)
    DIMENSION aCorners[4,2]
    FOR i = 0 TO 3
        aCorners[i+1,1] = CTOBIN(SUBSTR(cCorners, i*8 + 1, 4), "4RS")
        aCorners[i+1,2] = CTOBIN(SUBSTR(cCorners, i*8 + 5, 4), "4RS")
        ? "  点" + STR(i+1) + ": (" + STR(aCorners[i+1,1]) + ", " + STR(aCorners[i+1,2]) + ")"
    ENDFOR
    
    * 计算外接矩形
    minX = aCorners[1,1]
    minY = aCorners[1,2]
    maxX = aCorners[1,1]
    maxY = aCorners[1,2]
    
    FOR i = 2 TO 4
        IF aCorners[i,1] < minX
            minX = aCorners[i,1]
        ENDIF
        IF aCorners[i,2] < minY
            minY = aCorners[i,2]
        ENDIF
        IF aCorners[i,1] > maxX
            maxX = aCorners[i,1]
        ENDIF
        IF aCorners[i,2] > maxY
            maxY = aCorners[i,2]
        ENDIF
    ENDFOR
    
    ? ""
    ? "外接矩形: (" + STR(minX) + "," + STR(minY) + ") - (" + STR(maxX) + "," + STR(maxY) + ")"
    ? "========================================"
    
    * 调用画框函数(只画红色外接矩形)
    DrawBoxOnImage(cImageFile, minX, minY, maxX, maxY)
    
ELSE
    ? "解码失败,返回值: " + STR(nResult)
ENDIF

RETURN


*===========================================
* 使用 GDI[color=#808080]+ 在图片上画外接矩形[/color]
* 红色,1像素宽
*===========================================
FUNCTION DrawBoxOnImage(cImageFile, minX, minY, maxX, maxY)
    LOCAL cOutputFile, nToken, nImage, nGraphics, nPen
    LOCAL nRed, nWidth, nResult
    LOCAL cImageFileW, cOutputFileW
    
    ? "=== 绘制红色外接矩形 ==="
    ? "矩形范围: (" + STR(minX) + "," + STR(minY) + ") - (" + STR(maxX) + "," + STR(maxY) + ")"
    ? "矩形宽度: " + STR(maxX - minX) + ", 高度: " + STR(maxY - minY)
    
    cOutputFile = FORCEEXT(cImageFile, "_boxed.bmp")
    
    *--- 检查输出目录 ---
    LOCAL cDir
    cDir = JUSTPATH(cOutputFile)
    IF NOT DIRECTORY(cDir)
        MD (cDir)
        IF NOT DIRECTORY(cDir)
            ? "? 无法创建输出目录: " + cDir
            RETURN .F.
        ENDIF
    ENDIF
    
    *--- 声明 GDI+ 函数 ---
    DECLARE INTEGER GdiplusStartup IN gdiplus.dll ;
        INTEGER @lpToken, STRING @lpInput, INTEGER @lpOutput
    
    DECLARE INTEGER GdiplusShutdown IN gdiplus.dll INTEGER lpToken
    
    DECLARE INTEGER GdipCreateBitmapFromFile IN gdiplus.dll ;
        STRING lpFile, INTEGER @lpBitmap
    
    DECLARE INTEGER GdipGetImageGraphicsContext IN gdiplus.dll ;
        INTEGER lpImage, INTEGER @lpGraphics
    
    DECLARE INTEGER GdipCreatePen1 IN gdiplus.dll ;
        INTEGER nColor, SINGLE nWidth, INTEGER nUnit, INTEGER @lpPen
    
    DECLARE INTEGER GdipDrawRectangle IN gdiplus.dll ;
        INTEGER lpGraphics, INTEGER lpPen, ;
        SINGLE nX, SINGLE nY, SINGLE nWidth, SINGLE nHeight
    
    DECLARE INTEGER GdipSaveImageToFile IN gdiplus.dll ;
        INTEGER lpImage, STRING lpFile, STRING lpEncoder, INTEGER lpEncoderParams
    
    DECLARE INTEGER GdipDeletePen IN gdiplus.dll INTEGER lpPen
    DECLARE INTEGER GdipDeleteGraphics IN gdiplus.dll INTEGER lpGraphics
    DECLARE INTEGER GdipDisposeImage IN gdiplus.dll INTEGER lpImage
    
    *--- 转换文件名为 Unicode ---
    cImageFileW = STRCONV(cImageFile, 5) + CHR(0) + CHR(0)
    cOutputFileW = STRCONV(cOutputFile, 5) + CHR(0) + CHR(0)
    
    *--- 启动 GDI+ ---
    lcInput = CHR(1) + REPLICATE(CHR(0), 15)
    nToken = 0
    
    nResult = GdiplusStartup(@nToken, @lcInput, 0)
    IF nResult != 0 OR nToken = 0
        ? "? GDI+ 启动失败,错误码: " + STR(nResult)
        RETURN .F.
    ENDIF
    
    *--- 加载图片 ---
    nImage = 0
    nResult = GdipCreateBitmapFromFile(cImageFileW, @nImage)
    IF nResult != 0 OR nImage = 0
        ? "? 加载图片失败,错误码: " + STR(nResult)
        = GdiplusShutdown(nToken)
        RETURN .F.
    ENDIF
    
    *--- 创建 Graphics ---
    nGraphics = 0
    nResult = GdipGetImageGraphicsContext(nImage, @nGraphics)
    IF nResult != 0 OR nGraphics = 0
        = GdipDisposeImage(nImage)
        = GdiplusShutdown(nToken)
        RETURN .F.
    ENDIF
    
    *===========================================
    * 绘制红色外接矩形(1像素宽)
    *===========================================
    nRed = 0xFFFF0000  && 红色 (ARGB)
    nPen = 0
    nWidth = 1.0
    nResult = GdipCreatePen1(nRed, nWidth, 2, @nPen)
    
    IF nResult = 0 AND nPen != 0
        = GdipDrawRectangle(nGraphics, nPen, minX, minY, maxX-minX, maxY-minY)
        = GdipDeletePen(nPen)
        ? "? 红色外接矩形已绘制"
    ELSE
        ? "? 笔创建失败"
        = GdipDeleteGraphics(nGraphics)
        = GdipDisposeImage(nImage)
        = GdiplusShutdown(nToken)
        RETURN .F.
    ENDIF
    
    *--- 保存图片 ---
    LOCAL cEncoder
    cEncoder = CHR(0x06) + CHR(0xF4) + CHR(0x7C) + CHR(0x55) + ;
               CHR(0x04) + CHR(0x1A) + CHR(0xD3) + CHR(0x11) + ;
               CHR(0x9A) + CHR(0x73) + CHR(0x00) + CHR(0x00) + ;
               CHR(0xF8) + CHR(0x1E) + CHR(0xF3) + CHR(0x2E)
    
    nResult = GdipSaveImageToFile(nImage, cOutputFileW, cEncoder, 0)
    
    *--- 清理资源 ---
    = GdipDeleteGraphics(nGraphics)
    = GdipDisposeImage(nImage)
    = GdiplusShutdown(nToken)
    
    CLEAR DLLS
    
    IF nResult = 0
        ? "带框图片已保存: " + cOutputFile
        DECLARE INTEGER ShellExecute IN shell32.dll ;
            INTEGER hwnd, STRING lpOperation, STRING lpFile, ;
            STRING lpParameters, STRING lpDirectory, INTEGER nShowCmd
        = ShellExecute(0, "open", cOutputFile, "", "", 1)
        RETURN .T.
    ELSE
        ? "? 保存图片失败,错误码: " + STR(nResult)
        RETURN .F.
    ENDIF
ENDFUNC


搜索更多相关主题的帖子: dll STR INTEGER 像素 条码 
2026-08-18 03:25
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1246
专家分:1687
注 册:2021-10-13
收藏
得分:0 
使用zxing C语言封装库,及zxing4vfp.dll封装库
程序代码:
* [color=#808080]============================================================================[/color]
* 完整修正 [color=#808080]- 使用 VFP 专用函数[/color]
* [color=#808080]============================================================================[/color]
=RecognizeBarcodeAndDraw()
PROCEDURE RecognizeBarcodeAndDraw(lcInputFile, lcOutputFile)
    LOCAL loImage, lnRet, lnWidth, lnHeight, lcRawData
    LOCAL lnOptions, lnBarcodes, lnCount, lni, lnBarcode
    LOCAL lcText, lnX1, lnY1, lnX2, lnY2, lnX3, lnY3, lnX4, lnY4
    LOCAL lcError, lnFormatsPtr, lnFormatCount
    LOCAL lnPosPtr, lcPosData
    
    * ========== 声明所有需要的函数 ==========
    
    * ReaderOptions
    DECLARE INTEGER ZXing_ReaderOptions_new IN zxingc.dll
    DECLARE ZXing_ReaderOptions_delete IN zxingc.dll INTEGER opts
    DECLARE ZXing_ReaderOptions_setFormats IN zxingc.dll INTEGER opts, INTEGER formats, INTEGER count
    
    * 格式
    DECLARE INTEGER ZXing_BarcodeFormatsFromString IN zxingc.dll STRING str, INTEGER @outCount
    
    * 图像
    DECLARE INTEGER ZXing_ImageView_new IN zxingc.dll STRING data, INTEGER width, INTEGER height, INTEGER format, INTEGER rowStride, INTEGER pixStride
    DECLARE ZXing_ImageView_delete IN zxingc.dll INTEGER iv
    
    * 读取
    DECLARE INTEGER ZXing_ReadBarcodes IN zxingc.dll INTEGER iv, INTEGER opts
    
    * 条码列表
    DECLARE INTEGER ZXing_Barcodes_size IN zxingc.dll INTEGER barcodes
    DECLARE INTEGER ZXing_Barcodes_at IN zxingc.dll INTEGER barcodes, INTEGER index
    DECLARE ZXing_Barcodes_delete IN zxingc.dll INTEGER barcodes
    
    * 条码
    DECLARE INTEGER ZXing_Barcode_position_vfp IN zxingc.dll INTEGER barcode
    DECLARE STRING ZXing_Barcode_text IN zxingc.dll INTEGER barcode
    DECLARE ZXing_Barcode_delete IN zxingc.dll INTEGER barcode
    
    * 释放 VFP 专用位置
    DECLARE ZXing_Position_delete IN zxingc.dll INTEGER pos
    
    * 工具
    DECLARE ZXing_free IN zxingc.dll INTEGER ptr
    DECLARE STRING ZXing_LastErrorMsg IN zxingc.dll
    DECLARE STRING ZXing_Version IN zxingc.dll
    
    *错误处理
    DECLARE integer ZXing_LastErrorMsg IN zxingc.dll
    
    *GDIAPI 申明
    
    * ============================================================================
* GDI[color=#808080]+ 函数声明[/color]
* [color=#808080]============================================================================[/color]

* 获取 Graphics 对象
DECLARE INTEGER GdipGetImageGraphicsContext IN gdiplus.dll ;
    INTEGER image, ;
    INTEGER @graphics

* 创建画笔
DECLARE INTEGER GdipCreatePen1 IN gdiplus.dll ;
    INTEGER color, ;
    SINGLE width, ;
    INTEGER unit, ;
    INTEGER @pen

* 绘制多边形
DECLARE INTEGER GdipDrawPolygon IN gdiplus.dll ;
    INTEGER graphics, ;
    INTEGER pen, ;
    STRING @points, ;
    INTEGER count

* 使用整数坐标版本 (GdipDrawPolygonI)
DECLARE INTEGER GdipDrawPolygonI IN gdiplus.dll ;
    INTEGER graphics, ;
    INTEGER pen, ;
    STRING @points, ;
    INTEGER count
    
* 绘制多边形(另一种声明方式,使用指针)
* [color=#0000FF]DECLARE INTEGER GdipDrawPolygon IN gdiplus.dll ;[/color]
*     [color=#0000FF]INTEGER graphics, ;[/color]
*     [color=#0000FF]INTEGER pen, ;[/color]
*     [color=#0000FF]INTEGER points, ;      && 指针方式[/color]
*     [color=#0000FF]INTEGER count[/color]

* 删除画笔
DECLARE INTEGER GdipDeletePen IN gdiplus.dll INTEGER pen

* 删除 Graphics
DECLARE INTEGER GdipDeleteGraphics IN gdiplus.dll INTEGER graphics
    
    * ========== 检查输入文件 ==========
    IF EMPTY(lcInputFile)
        lcInputFile = GETFILE("BMP;JPG;PNG;TIF;GIF", "选择包含条码的图片", "选择")
        IF EMPTY(lcInputFile)
            RETURN
        ENDIF
    ENDIF
    
    IF NOT FILE(lcInputFile)
        MESSAGEBOX("文件不存在:" + lcInputFile, 16, "错误")
        RETURN
    ENDIF
    
    IF EMPTY(lcOutputFile)
        lcOutputFile = JUSTPATH(lcInputFile) + "\" + JUSTSTEM(lcInputFile) + "_result." + JUSTEXT(lcInputFile)
    ENDIF
    
    * ========== 显示版本信息 ==========
    ? "ZXing 版本:", ZXing_Version()
    
    * ========== 1. 加载图片 ==========
    SET PROCEDURE TO imageconverter.prg ADDITIVE 
    loImage = CREATEOBJECT("ImageConverter1")
    IF NOT loImage.LoadImage(lcInputFile)
        MESSAGEBOX("加载图片失败!", 16, "错误")
        RETURN
    ENDIF
    
    * ========== 2. 获取图片信息 ==========
    lnWidth = loImage.GetImageWidth()
    lnHeight = loImage.GetImageHeight()
    
    IF lnWidth <= 0 OR lnHeight <= 0
        MESSAGEBOX("无效的图片尺寸!", 16, "错误")
        loImage.Destroy()
        RETURN
    ENDIF
    
    * ========== 3. 获取24位RGB数据 ==========
    lcRawData = loImage.GetRawData24()
    
    IF EMPTY(lcRawData) OR LEN(lcRawData) != lnWidth * lnHeight * 3
        MESSAGEBOX("获取图像数据失败!", 16, "错误")
        loImage.Destroy()
        RETURN
    ENDIF
    
    ? "图片信息:", lnWidth, "x", lnHeight, "像素"
    
    * ========== 4. 创建 ImageView ==========
    #DEFINE ZXING_IMAGE_FORMAT_BGR 0x03020100
    LOCAL lnImageView
    lnImageView = ZXing_ImageView_new(lcRawData, lnWidth, lnHeight, ZXING_IMAGE_FORMAT_BGR, 0, 0)
    
    IF lnImageView = 0
        MESSAGEBOX("创建 ImageView 失败!", 16, "错误")
        loImage.Destroy()
        RETURN
    ENDIF
    
    * ========== 5. 创建 ReaderOptions ==========
    lnOptions = ZXing_ReaderOptions_new()
    
    * ========== 6. 设置格式 ==========
    lnFormatCount = 0
    lnFormatsPtr = ZXing_BarcodeFormatsFromString("All", @lnFormatCount)
    
    IF lnFormatsPtr != 0 AND lnFormatCount > 0
        ZXing_ReaderOptions_setFormats(lnOptions, lnFormatsPtr, lnFormatCount)
        ZXing_free(lnFormatsPtr)
    ELSE
        MESSAGEBOX("设置格式失败!", 16, "错误")
        ZXing_ReaderOptions_delete(lnOptions)
        ZXing_ImageView_delete(lnImageView)
        loImage.Destroy()
        RETURN
    ENDIF
    
    * ========== 7. 识别条码 ==========
    lnBarcodes = 0
    TRY
        lnBarcodes = ZXing_ReadBarcodes(lnImageView, lnOptions)
    CATCH TO loException
        * DLL 调用异常,安全退出
        MESSAGEBOX("识别条码时发生异常:" + CHR(13) + loException.Message, 16, "错误")
        * 清理资源
        ZXing_ReaderOptions_delete(lnOptions)
        ZXing_ImageView_delete(lnImageView)
        loImage.Destroy()
        RETURN
    ENDTRY

 
    IF lnBarcodes = 0
        lcError = ZXing_LastErrorMsg()
        MESSAGEBOX("未识别到条码!" + IIF(EMPTY(lcError), "", CHR(13) + "错误信息:" + lcError), 64, "提示")
        ZXing_ReaderOptions_delete(lnOptions)
        ZXing_ImageView_delete(lnImageView)
        loImage.Destroy()
        RETURN
    ENDIF
    
    * ========== 8. 获取条码数量 ==========
    lnCount = ZXing_Barcodes_size(lnBarcodes)
    ? "识别到条码数量:", lnCount
    
    * ========== 9. 遍历所有条码 ==========
    FOR lni = 0 TO lnCount - 1
        lnBarcode = ZXing_Barcodes_at(lnBarcodes, lni)
        
        * ---------- 获取文本 ----------
        lcText = ZXing_Barcode_text(lnBarcode)
        ? "条码", lni + 1, "文本:", STRCONV(lcText,11)
        
        * ---------- 获取位置(使用 VFP 专用函数)----------
        * ZXing_Barcode_position_vfp 返回 ZXing_Position* 指针
        lnPosPtr = ZXing_Barcode_position_vfp(lnBarcode)
        
        IF lnPosPtr != 0
            * 读取 32 字节结构体数据
            lcPosData = SYS(2600, lnPosPtr, 32)
            
            * 解析结构体:
            * offset 0:  topLeft.x     (int, 4字节)
            * offset 4:  topLeft.y     (int, 4字节)
            * offset 8:  topRight.x    (int, 4字节)
            * offset 12: topRight.y    (int, 4字节)
            * offset 16: bottomRight.x (int, 4字节)
            * offset 20: bottomRight.y (int, 4字节)
            * offset 24: bottomLeft.x  (int, 4字节)
            * offset 28: bottomLeft.y  (int, 4字节)
            
            lnX1 = CTOBIN(SUBSTR(lcPosData, 1, 4), "4RS")
            lnY1 = CTOBIN(SUBSTR(lcPosData, 5, 4), "4RS")
            lnX2 = CTOBIN(SUBSTR(lcPosData, 9, 4), "4RS")
            lnY2 = CTOBIN(SUBSTR(lcPosData, 13, 4), "4RS")
            lnX3 = CTOBIN(SUBSTR(lcPosData, 17, 4), "4RS")
            lnY3 = CTOBIN(SUBSTR(lcPosData, 21, 4), "4RS")
            lnX4 = CTOBIN(SUBSTR(lcPosData, 25, 4), "4RS")
            lnY4 = CTOBIN(SUBSTR(lcPosData, 29, 4), "4RS")
            
            * 释放位置内存(重要!)
            ZXing_Position_delete(lnPosPtr) &&这个函数忘了加_vfp后缀了。
            
            ? "顶点坐标: P1(" + TRANS(lnX1) + "," + TRANS(lnY1) + ") P2(" + TRANS(lnX2) + "," + TRANS(lnY2) + ")"
            ? "          P3(" + TRANS(lnX3) + "," + TRANS(lnY3) + ") P4(" + TRANS(lnX4) + "," + TRANS(lnY4) + ")"
            
            * 在图像上绘制四边形
            IF NOT loImage.DrawPolygon(lnX1, lnY1, lnX2, lnY2, lnX3, lnY3, lnX4, lnY4, 0xFFFF0000, 3)
                ? "警告:绘制多边形失败"
            ENDIF
        ELSE
            ? "警告:无法获取条码位置"
        ENDIF
        
        ZXing_Barcode_delete(lnBarcode)
    ENDFOR
    
    * ========== 10. 释放资源 ==========
    ZXing_Barcodes_delete(lnBarcodes)
    ZXing_ReaderOptions_delete(lnOptions)
    ZXing_ImageView_delete(lnImageView)
    
    * ========== 11. 保存结果 ==========
    IF loImage.SaveImage(lcOutputFile)
        MESSAGEBOX("处理完成!" + CHR(13) + "识别到 " + TRANS(lnCount) + " 个条码" + CHR(13) + ;
            "结果保存至:" + lcOutputFile, 64, "完成")
    ELSE
        MESSAGEBOX("保存图片失败!", 16, "错误")
    ENDIF
    
    loImage.Destroy()
    ON ERROR 
ENDPROC

* [color=#808080]============================================================================[/color]
* 扩展 ImageConverter 类 [color=#808080]- 添加绘制多边形功能[/color]
* [color=#808080]============================================================================[/color]

DEFINE CLASS ImageConverter1 AS ImageConverter

    *----------------------------------------------------------------------------
    * DrawPolygon - 在图像上绘制多边形(四边形)
    * 参数:四个顶点的坐标 (x1,y1) (x2,y2) (x3,y3) (x4,y4)
    *      tnColor - 颜色值 (如 0xFF0000 表示红色)
    *      tnPenWidth - 线条宽度(默认2)
    * 返回:成功 .T.,失败 .F.
    *----------------------------------------------------------------------------
    PROCEDURE DrawPolygon(tnX1, tnY1, tnX2, tnY2, tnX3, tnY3, tnX4, tnY4, tnColor, tnPenWidth)
        LOCAL lnGraphics, lnPen, lnRet, lcPoints
        
        IF THIS.himage = 0
            RETURN .F.
        ENDIF
        
        IF PCOUNT() < 8
            RETURN .F.
        ENDIF
        
        IF EMPTY(tnColor)
            tnColor = 0xFFFF0000  && 红色
        ENDIF
        
        IF EMPTY(tnPenWidth) OR tnPenWidth <= 0
            tnPenWidth = 2
        ENDIF
        
        * 创建Graphics对象
        lnGraphics = 0
        lnRet = GdipGetImageGraphicsContext(THIS.himage, @lnGraphics)
        IF lnRet != 0 .OR. lnGraphics = 0
            RETURN .F.
        ENDIF
        
        * 创建画笔
        lnPen = 0
        lnRet = GdipCreatePen1(tnColor, tnPenWidth, 2, @lnPen)  && Unit=2 (像素)
        IF lnRet != 0 .OR. lnPen = 0
            = GdipDeleteGraphics(lnGraphics)
            RETURN .F.
        ENDIF
        
        * 构建点数组 (4个点,每个点8字节:x和y各4字节浮点数)
        lcPoints = ;
            FLOATTOSTR(tnX1) + FLOATTOSTR(tnY1) + ;
            FLOATTOSTR(tnX2) + FLOATTOSTR(tnY2) + ;
            FLOATTOSTR(tnX3) + FLOATTOSTR(tnY3) + ;
            FLOATTOSTR(tnX4) + FLOATTOSTR(tnY4)
        
        * 绘制多边形
        lnRet = GdipDrawPolygonI(lnGraphics, lnPen, @lcPoints, 4)
        
        * 清理资源
        = GdipDeletePen(lnPen)
        = GdipDeleteGraphics(lnGraphics)
        
        RETURN (lnRet = 0)
    ENDPROC

    *----------------------------------------------------------------------------
    * DrawRectangle - 绘制矩形(简化版,使用最小包围盒)
    * 参数:左上角和右下角坐标
    *----------------------------------------------------------------------------
    PROCEDURE DrawRectangle(tnLeft, tnTop, tnRight, tnBottom, tnColor, tnPenWidth)
        LOCAL lnX1, lnY1, lnX2, lnY2, lnX3, lnY3, lnX4, lnY4
        
        lnX1 = tnLeft
        lnY1 = tnTop
        lnX2 = tnRight
        lnY2 = tnTop
        lnX3 = tnRight
        lnY3 = tnBottom
        lnX4 = tnLeft
        lnY4 = tnBottom
        
        RETURN THIS.DrawPolygon(lnX1, lnY1, lnX2, lnY2, lnX3, lnY3, lnX4, lnY4, tnColor, tnPenWidth)
    ENDPROC

    *----------------------------------------------------------------------------
    * GetBoundingBox - 计算四个顶点的最小包围盒
    *----------------------------------------------------------------------------
    PROCEDURE GetBoundingBox(tnX1, tnY1, tnX2, tnY2, tnX3, tnY3, tnX4, tnY4)
        LOCAL lnMinX, lnMaxX, lnMinY, lnMaxY
        
        lnMinX = MIN(tnX1, tnX2, tnX3, tnX4)
        lnMaxX = MAX(tnX1, tnX2, tnX3, tnX4)
        lnMinY = MIN(tnY1, tnY2, tnY3, tnY4)
        lnMaxY = MAX(tnY1, tnY2, tnY3, tnY4)
        
        RETURN lnMinX, lnMaxX, lnMinY, lnMaxY
    ENDPROC

ENDDEFINE

* [color=#808080]============================================================================[/color]
* 辅助函数
* [color=#808080]============================================================================[/color]

*----------------------------------------------------------------------------
* FLOATTOSTR - 将浮点数转换为4字节二进制字符串(用于GDI+点数组)
*----------------------------------------------------------------------------
FUNCTION FLOATTOSTR(tnValue)
    LOCAL lcResult
    lcResult = BINTOC(tnValue, "4RS")
    RETURN lcResult
ENDFUNC

2026-08-18 03:28
sam_jiang
Rank: 10Rank: 10Rank: 10
等 级:贵宾
威 望:14
帖 子:1246
专家分:1687
注 册:2021-10-13
收藏
得分:0 
效果图,没有条码样品,没办法全试一遍。。。
图片附件: 游客没有浏览图片的权限,请 登录注册
图片附件: 游客没有浏览图片的权限,请 登录注册
图片附件: 游客没有浏览图片的权限,请 登录注册

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


条码的细条是1个像素的条码,有可能没法识别,这是zxing的问题,没办法。。。
2026-08-18 03:37
schtg
Rank: 13Rank: 13Rank: 13Rank: 13
来 自:USA
等 级:贵宾
威 望:67
帖 子:2746
专家分:4967
注 册:2012-2-29
收藏
得分:0 
2026-08-18 06:41
快速回复:基于zxing的条形码轮廓的机器识别++原创哦~
数据加载中...
 
   
关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

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