y21song 发表于 2017-8-19 23:06:23

【已解决】如何实现屏幕找图不生成临时文件

本帖最后由 y21song 于 2017-8-21 15:36 编辑

问题已经解决,本人比较菜,调试了三天才找到问题,修改后的代码请在7楼查看,希望能帮助到其他人!感谢阿福的找图源码!


现在使用的是改了一下阿福的找图代码,先抓图保存,再来找图片的办法,代码如下#include <GdiPlus.au3>
;#include <myOcrFunc.au3>
#include <ScreenCapture.au3>


Global $x1,$y1,$x2,$y2,$pic,$aPosMsg
Local $hBMP, $hImage1, $aReturn, $error,$hBitmap1
Func search_pic($x1,$y1,$x2,$y2,$pic) ;当前屏幕找图,说明:$x1:屏幕上的左上角X坐标,$y1:屏幕上的左上角Y坐标,$x2:屏幕上的右下角X坐标,$y2:屏幕上的右下角Y坐标,$pic:要找图的路径及名称,$aPosMsg:返回坐标值(X坐标,Y坐标,长,高)
        _GDIPlus_Startup ()
        $s_dir = @ScriptDir
        ;_ScreenCapture_Capture ( $s_dir & "\Source.bmp",$x1,$y1,$x2,$y2 )
        _ScreenCapture_Capture ( "z:\Source.bmp",$x1,$y1,$x2,$y2 ,false)

#CS         $hBMP = _ScreenCapture_Capture("",$x1,$y1,$x2,$y2, False)
;         ;MsgBox(0,"",$hBMP)
;         $hBitmap1 = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)
;         MsgBox(0,"",$hBitmap1)
#CE


        $hBitmap1 = _GDIPlus_BitmapCreateFromFile("z:\Source.bmp")
        ;$hBitmap1 = _GDIPlus_BitmapCreateFromFile($s_dir & "\Source.bmp")
        $array1 = _myReadBitmapMsg($hBitmap1, 1)
        $search_pic=$pic
        $hBitmap2 = _GDIPlus_BitmapCreateFromFile($search_pic)
        $array2 = _myReadBitmapMsg($hBitmap2, 1)

        $iMax = _GDIPlus_ImageGetWidth ($hBitmap1) / _GDIPlus_ImageGetWidth ($hBitmap2)

       $t=TimerInit()
       $aPosMsg = _ArrayComp($array1, $array2, False, $iMax)
       ;ConsoleWrite("找到一个图片块用去时间:"&TimerDiff($t)&'毫秒,位置信息(为空未找到):'&$aPosMsg&@CRLF)

        ;$t=TimerInit()
        ;$aPosMsg = _ArrayComp($array1, $array2, True, $iMax)
        ;ConsoleWrite("找到所有图片块用去时间:"&TimerDiff($t)&'毫秒,位置信息(为空未找到):'&$aPosMsg&@CRLF)

        ;~ If $aPosMsg<>"" Then
        ;~         Local $aPos, $i, $hGraphics = _GDIPlus_ImageGetGraphicsContext ($hBitmap1)
        ;~         $aPosMsg = StringSplit($aPosMsg,"|",2)
        ;~         For $i = 0 To UBound($aPosMsg)-1
        ;~               $aPos = StringSplit($aPosMsg[$i],",",2)
        ;~               _GDIPlus_GraphicsDrawRect($hGraphics, $aPos, $aPos, $aPos, $aPos)
        ;~         Next
        ;~         ;_GDIPlus_ImageSaveToFile($hBitmap1, $s_dir&"\Target.bmp")
        ;~         _GDIPlus_GraphicsDispose ($hGraphics)
        ;~ EndIf
        _GDIPlus_ImageDispose ($hBitmap1)
        _WinAPI_DeleteObject ($hBitmap1)
        _GDIPlus_ImageDispose ($hBitmap2)
        _WinAPI_DeleteObject ($hBitmap2)
        _GDIPlus_ShutDown ()
        Return $aPosMsg
        Exit
EndFunc
        ;一般来说,当$hBitmap2是小图时$iType=1, 较大时$iType=0, 如果已知$array2哪个元素进行搜索比较合理,直接指定为$iY
        Func _ArrayComp($array1, $array2, $SearchAll=False, $iMax=0, $iY=0, $iType=0)
                        Local $iExtended=0
                        If $iMax>0 And $iY=0 Then
                                        Local $t=TimerInit()
                                        Local $sBmpData1 = ""
                                        For $i = 0 To UBound($array1)-1;相当于_ArrayToString或_myReadBitmapMsg($hBitmap,0),但更快
                                                        $sBmpData1 &= $array1[$i]
                                        Next;===>当多个$array2搜索时,这个内容考虑在udf外执行,_ArrayComp($sBmpData1, $array1, $array2, $SearchAll=False, $iMax=0, $iY=0, $iType=0)
                                        For $i = 0 To UBound($array2)-1
                                                        If StringReplace($array2[$i], StringLeft($array2[$i],6),"")="" Then ContinueLoop
                                                        Select
                                                                        Case $iType = 0
                                                                                        $iY = $i
                                                                                        ExitLoop
                                                                        #cs
                                                                        Case $iType = 1 And Not @AutoItX64 ;pusofalse提供的多线程方法
                                                                                        ;该方法中映射文件到内容部分 $pBaseAddress 暂用本段代替
                                                                                        Local $tBuff = DllStructCreate("char ["&StringLen($sBmpData1)+1&"]")
                                                                                        DllStructSetData($tBuff, 1, $sBmpData1)
                                                                                        $pBaseAddress = DllStructGetPtr($tBuff)
                                                                                        ;http://www.autoitx.com/thread-20592-1-3.html
                                                                        #ce
                                                                        Case Else
                                                                                        StringReplace($sBmpData1,$array2[$i],"",0, 1)
                                                                                        If $iExtended=0 Or $iExtended>@extended Then
                                                                                                        $iExtended=@extended
                                                                                                        If $iExtended=0 Then Return "";没有匹配的图块
                                                                                                        $iY = $i
                                                                                                        If $iExtended<=$iMax Then ExitLoop
                                                                                        EndIf
                                                        EndSelect
                                        Next
                                        ;ConsoleWrite("获取最佳搜索串"&TimerDiff($t)&','&$iExtended&@CRLF)
                        EndIf

                        ;Local $t=TimerInit()
                        If UBound($array1)<UBound($array2) Then Return ""
                        Local $s_re="", $y, $y2, $iW2=StringLen($array2[$iY]), $iPos;, $iSearchPos
                        For $y = $iy To UBound($array1)-1
                                        $iPos = 0;$iSearchPos = 1
                                        While $y+UBound($array2)<=UBound($array1)
                                                        $iPos = StringInStr($array1[$y], $array2[$iY], 1, 1, $iPos+1);$iPos = StringInStr($array1[$y], $array2[$iY], 1, 1, $iSearchPos)
                                                        Select
                                                        Case $iPos = 0
                                                                        ContinueLoop(2)
                                                        Case Mod($iPos-1,6)<>0 ;Or $y<$iy
                                                                        ;$iSearchPos = $iPos+1
                                                                        ContinueLoop
                                                        EndSelect
                                                        For $y2 = $iY To UBound($array2)-1
                                                                        If StringMid($array1[$y+$y2-$iy], $iPos, $iW2)<>$array2[$y2] Then
                                                                                        ;$iSearchPos = $iPos+1
                                                                                        ContinueLoop(2)
                                                                        EndIf
                                                        Next
                                                        For $y2 = 0 To $iY-1
                                                                        If StringMid($array1[$y+$y2-$iy], $iPos, $iW2)<>$array2[$y2] Then
                                                                                        ;$iSearchPos = $iPos+1
                                                                                        ContinueLoop(2)
                                                                        EndIf
                                                        Next
                                                        $s_re &= ($iPos-1)/6&','&$y-$iy&','&$iW2/6&','&UBound($array2)&"|"
                                                        StringReplace($s_re,"|","")
                                                        If (Not $SearchAll) Or ($iExtended>0 And @extended>=$iExtended) Then ExitLoop(2)
                                                        ;$iSearchPos = $iPos+1
                                        WEnd
                        Next
                        If StringRight($s_re,1)="|" Then $s_re = StringTrimRight($s_re,1)
                        ;ConsoleWrite(TimerDiff($t)&@CRLF);18
                        Return $s_re
        EndFunc

        Func _myReadBitmapMsg($hBitmap, $iType=1);
                        Local $aBmpData
                $aBmpData = _GDIPlus_ImageGetWidth ($hBitmap)
                $aBmpData = _GDIPlus_ImageGetHeight ($hBitmap)
                        Local $BitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $aBmpData, $aBmpData, $GDIP_ILMREAD, $GDIP_PXF24RGB)
                        $aBmpData = Abs(DllStructGetData($BitmapData, "Stride"))
                        $aBmpData = DllStructGetData($BitmapData, "Scan0");
                        _GDIPlus_BitmapUnlockBits($hBitmap, $BitmapData)
                        Local $tBuff, $iH
                        Switch $iType;已测试比StringRegExp快
                                        Case -1
                                                        Return $aBmpData
                                        Case 0
                                                        For $iH = 1 To $aBmpData
                                                                        $tBuff = DllStructCreate("byte[" & ($aBmpData*3) & "]", $aBmpData + ($iH-1)*$aBmpData)
                                                                        $aBmpData &= StringTrimLeft(DllStructGetData($tBuff, 1),2)
                                                        Next
                                                        Return $aBmpData
                                        Case Else
                                                        Local $aRet[$aBmpData]
                                                        For $iH = 1 To $aBmpData
                                                                        $tBuff = DllStructCreate("byte[" & ($aBmpData*3) & "]", $aBmpData + ($iH-1)*$aBmpData)
                                                                        $aRet[$iH-1] = StringTrimLeft(DllStructGetData($tBuff, 1), 2)
                                                        Next
                                                        Return $aRet
                        EndSwitch
        EndFunc
但是我后期要实现屏幕循环找图,这样的话读写太大了,磁盘和cpu都占用较高,虽然现在利用虚拟磁盘实现了不读写硬盘,但是还是希望能找到直接不读写的办法。
参考了另外一个au3官方论坛的一个找图代码之后,把现在代码中的_ScreenCapture_Capture ( "z:\Source.bmp",$x1,$y1,$x2,$y2 ,false)

        $hBitmap1 = _GDIPlus_BitmapCreateFromFile("z:\Source.bmp")
改为以下代码$hBMP = _ScreenCapture_Capture("",$x1,$y1,$x2,$y2, False)
               $hBitmap1 = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)运行就不能找图,看了帮助也没有找到答案,调试的话发现运行到$tBuff = DllStructCreate("byte[" & ($aBmpData*3) & "]", $aBmpData + ($iH-1)*$aBmpData)
就退出了,请各位帮忙看看,要如何进行改造源码实现无临时文件找图!

fybhwsx 发表于 2017-8-20 06:03:10

AU3找图,识字,太弱了,没有几个实用高效的例子,帮顶,期待高手出手!

y21song 发表于 2017-8-20 10:17:40

回复 2# fybhwsx

au3这方面确实弱一些,但是阿福的这个效率确实挺高,文字也可以当作图片来找,满足基本需求够用了,官方出个找图的udf就好了,也测试了一下其他的au3找图代码,效率低。不过阿福这个有时候奇怪,同样一个图,有时候会找不到,重新刷新一下又找到!不知道是啥问题。

y21song 发表于 2017-8-20 10:18:17

回复 2# fybhwsx

au3这方面确实弱一些,但是阿福的这个效率确实挺高,文字也可以当作图片来找,满足基本需求够用了,官方出个找图的udf就好了,也测试了一下其他的au3找图代码,效率低。不过阿福这个有时候奇怪,同样一个图,有时候会找不到,重新刷新一下又找到!不知道是啥问题。

zch11230 发表于 2017-8-20 14:04:34

http://www.autoitx.com/forum.php?mod=redirect&goto=findpost&ptid=51580&pid=649009&fromuid=7653433
之前给这位网友回复过,我也是修改改的阿福的代码.
$array2和$array1的格式是一样的,只是多了最后一行位图的宽度信息,在对比前也是把最后一行给删除了的.
$array1是大图,也就是源图转成的数组,$array2是小图,需要搜索的图转成的数组,因为需要搜索的小图内容是不变的,所以我就预先放到search.txt中,在脚本运行的时候读取一次就行了,这样就不用每次读文件了.
search.txt内容就是阿福原版中$array2的内容,用_arraytostring转的,再加上一行位图宽度$iMax的值.用的时候再正则还原成的数组.

y21song 发表于 2017-8-21 15:24:43

本帖最后由 y21song 于 2017-8-21 15:31 编辑

回复 5# zch11230

感谢提点,您的代码我看了,虽然没有完全看懂,不过还是给了一些提示,研究之后又调试了一下,终于解决问题了。而且您的代码如果用在同一个窗口或者控件方面,应该效率会更高一些,只抓图控件窗口部分,我的需求不固定,还是只能全屏找图。再次感谢!

y21song 发表于 2017-8-21 15:28:40

下面是已经重新调试好的,测试可用,请各位参考,中间可能有一些调试时候留下的代码,我已经注释掉了!其实重点就是加了:$hBitmap1=_GDIPlus_BitmapCloneArea($hB1,$x1,$y1,$x2,$y2,$GDIP_PXF24RGB)这句#include <GdiPlus.au3>
;#include <myOcrFunc.au3>
#include <ScreenCapture.au3>
;#include <array.au3>
;#include <File.au3>


Global $x1,$y1,$x2,$y2,$pic,$aPosMsg
Local $hBMP, $hImage1, $aReturn, $error,$hBitmap1
Func search_pic($x1,$y1,$x2,$y2,$pic) ;当前屏幕找图,说明:$x1:屏幕上的左上角X坐标,$y1:屏幕上的左上角Y坐标,$x2:屏幕上的右下角X坐标,$y2:屏幕上的右下角Y坐标,$pic:要找图的路径及名称,$aPosMsg:返回坐标值(X坐标,Y坐标,长,高)
        _GDIPlus_Startup ()
        $s_dir = @ScriptDir
        ;_ScreenCapture_Capture ( $s_dir & "\Source.bmp",$x1,$y1,$x2,$y2 )
        ;_ScreenCapture_Capture ( "z:\Source.bmp",$x1,$y1,$x2,$y2 ,false)

        ;$hBitmap1 = _GDIPlus_BitmapCreateFromFile("z:\Source.bmp")
        ;$hBitmap1 = _GDIPlus_BitmapCreateFromFile($s_dir & "\Source.bmp")

        $hBMP = _ScreenCapture_Capture("",$x1,$y1,$x2,$y2, False)
        $hB1 = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)
        $hBitmap1=_GDIPlus_BitmapCloneArea($hB1,$x1,$y1,$x2,$y2,$GDIP_PXF24RGB)

        $array1 = _myReadBitmapMsg($hBitmap1, 1)
        $search_pic=$pic
        $hBitmap2 = _GDIPlus_BitmapCreateFromFile($search_pic)
        $array2 = _myReadBitmapMsg($hBitmap2, 1)

;_FileWriteFromArray("d:\3.txt", $array1)
;_FileWriteFromArray("d:\4.txt", $array2)

        $iMax = _GDIPlus_ImageGetWidth ($hBitmap1) / _GDIPlus_ImageGetWidth ($hBitmap2)

       $t=TimerInit()
       $aPosMsg = _ArrayComp($array1, $array2, False, $iMax)
       ;ConsoleWrite("找到一个图片块用去时间:"&TimerDiff($t)&'毫秒,位置信息(为空未找到):'&$aPosMsg&@CRLF)

        ;$t=TimerInit()
        ;$aPosMsg = _ArrayComp($array1, $array2, True, $iMax)
        ;ConsoleWrite("找到所有图片块用去时间:"&TimerDiff($t)&'毫秒,位置信息(为空未找到):'&$aPosMsg&@CRLF)

        ;~ If $aPosMsg<>"" Then
        ;~         Local $aPos, $i, $hGraphics = _GDIPlus_ImageGetGraphicsContext ($hBitmap1)
        ;~         $aPosMsg = StringSplit($aPosMsg,"|",2)
        ;~         For $i = 0 To UBound($aPosMsg)-1
        ;~               $aPos = StringSplit($aPosMsg[$i],",",2)
        ;~               _GDIPlus_GraphicsDrawRect($hGraphics, $aPos, $aPos, $aPos, $aPos)
        ;~         Next
        ;~         ;_GDIPlus_ImageSaveToFile($hBitmap1, $s_dir&"\Target.bmp")
        ;~         _GDIPlus_GraphicsDispose ($hGraphics)
        ;~ EndIf

        ;开始清理临时资源
;~         _GDIPlus_ImageDispose ($hBMP)
        _WinAPI_DeleteObject ($hBMP)
        _GDIPlus_ImageDispose ($hB1)
;~         _WinAPI_DeleteObject ($hB1)

        _GDIPlus_ImageDispose ($hBitmap1)
        _WinAPI_DeleteObject ($hBitmap1)
        _GDIPlus_ImageDispose ($hBitmap2)
        _WinAPI_DeleteObject ($hBitmap2)
        _GDIPlus_ShutDown ()
        Return $aPosMsg
        Exit
EndFunc
        ;一般来说,当$hBitmap2是小图时$iType=1, 较大时$iType=0, 如果已知$array2哪个元素进行搜索比较合理,直接指定为$iY
        Func _ArrayComp($array1, $array2, $SearchAll=False, $iMax=0, $iY=0, $iType=0)
                        Local $iExtended=0
                        If $iMax>0 And $iY=0 Then
                                        Local $t=TimerInit()
                                        Local $sBmpData1 = ""
                                        For $i = 0 To UBound($array1)-1;相当于_ArrayToString或_myReadBitmapMsg($hBitmap,0),但更快
                                                        $sBmpData1 &= $array1[$i]
                                        Next;===>当多个$array2搜索时,这个内容考虑在udf外执行,_ArrayComp($sBmpData1, $array1, $array2, $SearchAll=False, $iMax=0, $iY=0, $iType=0)
                                        For $i = 0 To UBound($array2)-1
                                                        If StringReplace($array2[$i], StringLeft($array2[$i],6),"")="" Then ContinueLoop
                                                        Select
                                                                        Case $iType = 0
                                                                                        $iY = $i
                                                                                        ExitLoop
                                                                        #cs
                                                                        Case $iType = 1 And Not @AutoItX64 ;pusofalse提供的多线程方法
                                                                                        ;该方法中映射文件到内容部分 $pBaseAddress 暂用本段代替
                                                                                        Local $tBuff = DllStructCreate("char ["&StringLen($sBmpData1)+1&"]")
                                                                                        DllStructSetData($tBuff, 1, $sBmpData1)
                                                                                        $pBaseAddress = DllStructGetPtr($tBuff)
                                                                                        ;http://www.autoitx.com/thread-20592-1-3.html
                                                                        #ce
                                                                        Case Else
                                                                                        StringReplace($sBmpData1,$array2[$i],"",0, 1)
                                                                                        If $iExtended=0 Or $iExtended>@extended Then
                                                                                                        $iExtended=@extended
                                                                                                        If $iExtended=0 Then Return "";没有匹配的图块
                                                                                                        $iY = $i
                                                                                                        If $iExtended<=$iMax Then ExitLoop
                                                                                        EndIf
                                                        EndSelect
                                        Next
                                        ;ConsoleWrite("获取最佳搜索串"&TimerDiff($t)&','&$iExtended&@CRLF)
                        EndIf

                        ;Local $t=TimerInit()
                        If UBound($array1)<UBound($array2) Then Return ""
                        Local $s_re="", $y, $y2, $iW2=StringLen($array2[$iY]), $iPos;, $iSearchPos
                        For $y = $iy To UBound($array1)-1
                                        $iPos = 0;$iSearchPos = 1
                                        While $y+UBound($array2)<=UBound($array1)
                                                        $iPos = StringInStr($array1[$y], $array2[$iY], 1, 1, $iPos+1);$iPos = StringInStr($array1[$y], $array2[$iY], 1, 1, $iSearchPos)
                                                        Select
                                                        Case $iPos = 0
                                                                        ContinueLoop(2)
                                                        Case Mod($iPos-1,6)<>0 ;Or $y<$iy
                                                                        ;$iSearchPos = $iPos+1
                                                                        ContinueLoop
                                                        EndSelect
                                                        For $y2 = $iY To UBound($array2)-1
                                                                        If StringMid($array1[$y+$y2-$iy], $iPos, $iW2)<>$array2[$y2] Then
                                                                                        ;$iSearchPos = $iPos+1
                                                                                        ContinueLoop(2)
                                                                        EndIf
                                                        Next
                                                        For $y2 = 0 To $iY-1
                                                                        If StringMid($array1[$y+$y2-$iy], $iPos, $iW2)<>$array2[$y2] Then
                                                                                        ;$iSearchPos = $iPos+1
                                                                                        ContinueLoop(2)
                                                                        EndIf
                                                        Next
                                                        $s_re &= ($iPos-1)/6&','&$y-$iy&','&$iW2/6&','&UBound($array2)&"|"
                                                        StringReplace($s_re,"|","")
                                                        If (Not $SearchAll) Or ($iExtended>0 And @extended>=$iExtended) Then ExitLoop(2)
                                                        ;$iSearchPos = $iPos+1
                                        WEnd
                        Next
                        If StringRight($s_re,1)="|" Then $s_re = StringTrimRight($s_re,1)
                        ;ConsoleWrite(TimerDiff($t)&@CRLF);18
                        Return $s_re
        EndFunc

        Func _myReadBitmapMsg($hBitmap, $iType=1);
                        Local $aBmpData
                $aBmpData = _GDIPlus_ImageGetWidth ($hBitmap)
                $aBmpData = _GDIPlus_ImageGetHeight ($hBitmap)
                        Local $BitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $aBmpData, $aBmpData, $GDIP_ILMREAD, $GDIP_PXF24RGB)
                        $aBmpData = Abs(DllStructGetData($BitmapData, "Stride"))
                        $aBmpData = DllStructGetData($BitmapData, "Scan0");
                        _GDIPlus_BitmapUnlockBits($hBitmap, $BitmapData)
                        Local $tBuff, $iH
                        Switch $iType;已测试比StringRegExp快
                                        Case -1
                                                        Return $aBmpData
                                        Case 0
                                                        For $iH = 1 To $aBmpData
                                                                        $tBuff = DllStructCreate("byte[" & ($aBmpData*3) & "]", $aBmpData + ($iH-1)*$aBmpData)
                                                                        $aBmpData &= StringTrimLeft(DllStructGetData($tBuff, 1),2)
                                                        Next
                                                        Return $aBmpData
                                        Case Else
                                                        Local $aRet[$aBmpData]
                                                        For $iH = 1 To $aBmpData
                                                                        $tBuff = DllStructCreate("byte[" & ($aBmpData*3) & "]", $aBmpData + ($iH-1)*$aBmpData)
                                                                        $aRet[$iH-1] = StringTrimLeft(DllStructGetData($tBuff, 1), 2)
                                                        Next
                                                        Return $aRet
                        EndSwitch
        EndFunc

fybhwsx 发表于 2017-8-21 15:55:47

感谢楼主分享解决源码,已收藏!

zch11230 发表于 2017-8-21 21:34:01

回复 6# y21song


    抓图这部分,ScreenCapture可以指定窗口或控件的句柄,不用抓全屏,不过是前台抓图,指定句柄也是先通过查找句柄控件或窗口所在位置和大小,抓这个座标范围的图而已。

lin6051 发表于 2018-2-19 12:03:07

回复 7# y21song


    为毛 我一运行 就出错啊
页: [1]
查看完整版本: 【已解决】如何实现屏幕找图不生成临时文件