关于 类似QQ 长截图的方案

-、屏幕截图:

   长截图时,整个截图界面其实是个异形窗体, 其大体思路是先创建一个半透明的窗体,然后根据截图区域来创建异形窗体。

  异形窗体:

       hRgn :=CreateRectRgn(rt.left,rt.top,rt.right,rt.bottom); //rt为截图区域

       hWndRect:= CreateRgn(0,0,self.width,self.height);

     if CombineRgn(hWndRect,hWndRect,hRgn,Rgn_Xor)<>Error then

      SetWindowRgn(Handle,hWndRect,False);

 

二、Hook windows消息

   (1)Hook  WH_MOUSE_LL

        其目的是控制滚动速度,方便截图。同时要将截图区域的滚动条禁用,以免影响连续截图。

     WH_MOUSE_LLProc(icode:integer;w:Wparam;l:Lparam):LRESULT;stdcall

     begin

               if (icode<0) or (icode<> hc_action) then

              begin

                      Result:= callNextHookEx(g_mouseWheel_hook,icode,w,l);

                     exit;

              end; 

            if(w<> WM_MOUSELWHEEL) and( w<>WM_LBUTTONDOWN) and (w<>WM_LBUTTONUP) then

          begin

                   Result:=callNextHookEx(g_mouseWheel_hook,icode,w,l);

                  exit(-1); //返回 <0的值 才能阻止消息进入windows 消息循环。

         end

         mslhook = PMSLLHOOKSTRUCT(l);

        if not assign(mslhook ) then

        begin

               

                    Result:=callNextHookEx(g_mouseWheel_hook,icode,w,l);

                  exit;

        end

         if( w<>WM_LBUTTOnDOWN) or(W<>WM_LBUttonUp) then exit(-1);

          

        iwheeldelta = short(HiWord(mslhook^.mouseData)); //根据iWheeldelta的值 便可知道 是向上滚动还是向下滚动.

 

        //控制滚动速度,根据需要,可自行调整

       if i>=3 then

       begin

                                  iTickcount :=GetTickCount;

                                if iTickount - FtickCount>300 then

                              begin

                                    //发送消息到主窗体,实现拼图功能

                                   PostMessage(Form.Handle, WM_User+100,iWheelDelta,0);

                                    FTickCount := iTickCount;

                                  i:=0;

                             end

                                  else

                                 Exit(-1);

      end

     else

     begin

              inc(i);

             exit(-1);  

end

      result :=CallnextHookEx(g_MouseWheel_Hook,icode,w,l);  

    end;

 *(2) 如果想做类似QQ的那种点击截图区域后,可自行滚动截图的功能,还需要Hook , WM_GetMessage.

         在此消息的HookProc  里面自行处理 WM_MouseWheel消息。

 

三、拼图

此部份是最重要的一部份,最简单的做法 是利用 opencv的surf、orb 等算法实现图片拼接。

参考网址:https://www.cnblogs.com/skyfsm/p/7411961.html

上述网址只是给出了两张图片的拼接,而且是左右拼接,而我们截图需要的上下拼接,最简单直接的做法就是先将图片旋转90度,拼接完成后再旋转回来

为了提高效率与准确率,最好使用 “增量拼接'的方式,也就是

 A 与B拼接后 生成Ab,

当与C拼接时,不要用AB与C拼接,而要使用 b与C拼接生成BC,然后将AB中的B扣掉 合并生成ABC.

上面的无论surf算法、还是orb算法,都存一个问题。如果 两张图片 有 2个以上的重叠区域时,最后合并生成的图片ABC会有问题。最简单的实验就是去截图带导航栏的门户网站,一试便知。

 

仔细分析,我们会发现 具有重叠区域的A、B两张图的拼接,转成数学问题,其本质就是求两个集合的最大交集。

所以 自写算法如下:

f b c d g --- list2
a 0 0 0 0 0
b 0 1 0 0 0
c 0 0 2 0 0
d 0 0 0 3 0
e 0 0 0 0 0
f 0 0 0 0 0
|
list1

void max_substring(const std::vector<float> &a1,const std::vector<float> &a2,
int & max1_index, int & max2_index, int & max_count)
{
int len1 = a1.size(); int len2 = a2.size();
std::vector< std::vector<int16_t> > mat(len1, std::vector<int16_t>(len2));
max_count = 0; max1_index = 0; max2_index = 0;

for(int i=0;i<len1;i++)
for(int j=0;j<len2;j++)
{
if(i==0 || j==0) { mat[i][j] = 0; continue; }

if(fabs(a1[i]-a2[j])<0.5)
{
int count = mat[i][j] = mat[i-1][j-1] + 1;
if(count>max_count) { max_count=count; max1_index=i-count+1; max2_index=j-count+1; }
}
else { mat[i][j] = 0; }

}

}

 

注明:上述算法也有些缺陷,并不满足所有的条件,当图片是 纯 白底、黑字的图片时,也会出现拼图错误,我想这也是QQ长截图有时候也不灵的原因吧。

 

posted @ 2021-03-29 12:01  呆球球  阅读(233)  评论(0编辑  收藏  举报