最近打算做个类似的软件,今天研究了一天,只能做到分析2个图片是否相同,但不同的位置定位没做出来(以前没做过图像处理,正在恶补相关知识)。刚刚恰巧在网上看到类似功能的软件,但人家是做外包的,而且不一定用delphi,所以没好意思问,现在来论坛问问前辈们,给我个思路吧。还有相关的技术关键字,我好学习,然后自己搞。——非商业的,纯粹为了学习。效果图:

解决方案 »

  1.   


    问下面红色代码什么意思,为什么不用bmp1.Width来循环?我看到很多例子是用高和宽循环的,这里为什么这样做?
    function IsBmpSame(bmp1,bmp2: TBitmap): Boolean;
    var
      i,j: Integer;
      ScanLine1,ScanLine2: PByteArray;
      Count: Integer;
    begin
      Result := (bmp1.Height = bmp2.Height) and
                (bmp1.Width = bmp2.Width) and
                (bmp1.PixelFormat = bmp2.PixelFormat);
      if Result then
      begin
        i := Integer(bmp1.PixelFormat);
        if i < 4 then
          i := 4
        else if i = 4 then
          inc(i);
        Count := (i - 3) * bmp1.Width - 1;    for i:=0 to bmp1.Height-1 do
        begin
          ScanLine1 := bmp1.ScanLine[i];
          ScanLine2 := bmp2.ScanLine[i];
          for j := 0 to Count do
            if ScanLine1[j] <> ScanLine2[j] then
            begin
              Result := False;
              Exit;
            end;
        end;
      end;
    end;
      

  2.   

    撸过
    用height循环也一样嘛,反正都是x*y
    btw,这程序的默认图标不是c#的吗
      

  3.   


       TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);    i := Integer(bmp1.PixelFormat);//这里的i返回的是像素值;一般我们用的bmp是pf24bit,也就是等于6;
        if i < 4 then
          i := 4
        else if i = 4 then
          inc(i);
        Count := (i - 3) * bmp1.Width - 1;    pf24bit表示一个像素点占24位,即3Byte,所以Count=3* bmp1.Width - 1;
      

  4.   


    其实我想问的重点是,用bmp1.Width循环和用这个COUNT循环的差别,因为他们的值不同啊!
      

  5.   


    如果你的图像是pf24bit的,执行ScanLine1 := bmp1.ScanLine[i];后,Length(ScanLine1) = 3* bmp1.Width;
    如果你的图像是其他格式的,执行ScanLine1 := bmp1.ScanLine[i];后,Length(ScanLine1)就不一定是3* bmp1.Width;
      

  6.   


    下面的代码已经统一了一个标准。确保了每一行像素数组的长度范围。可是,我想问的是,正常来说,Count(应该叫Length才对),应该比 bmp1.Width大。也就是说,增加了循环的范围。
     i := Integer(bmp1.PixelFormat);
        if i < 4 then
          i := 4
        else if i = 4 then
          inc(i);
        Count := (i - 3) * bmp1.Width - 1;
    这里如果用Count循环,应该是对这个数组(PBYTEARRAY)的每一个元素进行比对。
     for j := 0 to Count do
       if ScanLine1[j] <> ScanLine2[j] then
    而,比对有些例子用的是bmp1.Width,我是想问,既然用WIDTH也可以,用length也可以,那么为啥要用COUNT,因为更精确?还是其他原因?
     for j := 0 to bmp1.Width -1 do
       if ScanLine1[j] <> ScanLine2[j] then
    感谢你的回答,好吧,我承认我表达有问题。
      

  7.   

    如果图像格式是pf24bit的,用bmp1.Width是一定对的,只比了图像的前1/3像素点;
    用length应该可以适用所有的图像格式;
    Count是计算的长度,length(ScanLine1)应该等于Count,你可以测试下。
      

  8.   

    更正:
    如果图像格式是pf24bit的,用bmp1.Width是不对的,只比了图像的前1/3像素点;
      

  9.   


    感谢大虾的帮忙。我明白了。现在我想求大虾指点一下,怎么找到不一样的像素的位置。
    我做的代码如下,目前貌似没啥错误。请指点!var
      BMPA,BMPB:TBitMap;
      I,X,Y,Z : Integer;
      W,H,Count : Integer;
      p1,p2: PByteArray;
    begin
      BMPA:=TBitMap.Create;
      BMPB:=TBitMap.Create;  BMPA.LoadFromFile(PathA);
      BMPB.LoadFromFile(PathB);
      i := Integer(BMPA.PixelFormat);
      if i < 4 then
        i := 4
      else if i = 4 then
        inc(i);
      Count := (i - 3) * BMPA.Width - 1;
      
      H:=BMPA.Height - 1;  for Y := 0 to H do
      begin
        p1:=BMPA.ScanLine[Y];
        p2:=BMPB.ScanLine[Y];
        for X := 0 to Count do
        begin
          if p1[X]<>p2[X] then
          begin
            z:=x DIV (i-3);//转换回像素坐标
            ShowMessage('找到像素不同的位置['+IntToStr(z)+','+IntTostr(y)+']');
            BMPA.Canvas.Pixels[z,y]:=clRed;//把2张图片不同的位置用特殊颜色标记出来
          end;
        end;
      end;
      //显示图片,红色代表不同的地方
      self.imgComPare.Picture.Bitmap.Assign(BMPA);  BMPA.Free;
      BMPB.Free;
    end;
      

  10.   


    还有点问题,因为这里的COUNT问题,我刚刚测试了一下。事实上,这2个图品只有2个像素点不同。但是,if p1[X]<>p2[X] then位置的判断却是出现5次,这个很尴尬,虽然问题不大,但我觉得怎样才能完美点呢!
      

  11.   


    应该是6次,1个像素点占3Byte,2*3=6;你改为这样:
       for X := 0 to BMPA.Width - 1 do
        begin
          if (p1[X]<>p2[X]) or (p1[X+1]<>p2[X+1]) or (p1[X+2]<>p2[X+2]) then
          begin
            z:=x;
            ShowMessage('找到像素不同的位置['+IntToStr(z)+','+IntTostr(y)+']');
            BMPA.Canvas.Pixels[z,y]:=clRed;//把2张图片不同的位置用特殊颜色标记出来
          end;
        end;
      

  12.   

    Sorry,应该是:
     if (p1[3*X]<>p2[3*X]) or (p1[3*X+1]<>p2[3*X+1]) or (p1[3*X+2]<>p2[3*X+2]) then
      

  13.   


    呵呵 正好解决我的困惑,因为我之前的比较是这样的。for X := 0 to BMPA.Width - 1 do
    begin
      if p1[X]<>p2[X] then ---------->因为这里只扫描了1/3的像素点,所以,就没有达到预期目的。
      begin
        P[X*3]  :=0;
        p[X*3+1]:=0;
        p[X*3+2]:=0;
      end
      .........感谢大虾的耐心帮助。感觉真好!!