2014-10-06 6 views
0

화면 공유 응용 프로그램을 개발 중입니다. 이 프로젝트에서는 인터넷을 통해 이미지를 전송해야합니다. 분명히, 나는 몇 초마다 인터넷을 통해 새로운 그림을 보낼 수 없다. 그것은 매우 느릴 것이다. 마지막 이미지 (클라이언트가 이미 가지고있는 이미지) 이후에 변경된 픽셀 만 보내는 새 그림을 보내는 대신 서버 화면의 이미지 하나를 클라이언트로 보내고 싶습니다.두 이미지의 차이점을 찾는 방법은 무엇입니까?

이 나는이 코드를 작성했습니다 :

private List<Color> CompareBitmaps(Image old, Image _new) 
{ 
    List<Color> returnList = new List<Color>(); 

    for(int i = 0; i < old.Width; i++) 
     for (int j = 0; j < old.Height; j++) 
     { 
      if (((Bitmap)old).GetPixel(i, j) != ((Bitmap)_new).GetPixel(i, j)) 
      { 
       returnList.Add(((Bitmap)_new).GetPixel(i, j)); 
      } 
     } 

return returnList; 
} 

그러나, 그것은 너무 느리게 작동합니다.

더 빠른 알고리즘, 더 나은 알고리즘을 찾고 있습니다.

참고 :이 작업을 수행하는 내장 라이브러리가 필요하지 않습니다. 알고리즘이 필요해.

+0

허용되는 크기의 가운데 사각형을 가져 와서 해당 영역 내에서 임의의 픽셀을 비교하는 방법은 어떻습니까? – PeteGO

+1

하나의 아이디어는 모든 [10th, 5th, 2nd] 픽셀을 확인하여 시작하고, 변경 사항이 확인되면 확인하는 영역을 수정하는 것입니다. – user1274820

+2

'GetPixel' 또한 매우 느린 방법입니다. 'UnlockBits' 접근법을 시도하십시오. 당신이 그것을 보았다면 많은 튜토리얼이 있으며, 훨씬 더 빠릅니다. –

답변

0

변경된 모든 픽셀을 반환해야하므로 복잡성은 m * n이어야합니다. .GetPixel (I, J)를 두 번 호출됩니다 _new)

  1. (비트 맵), 조금 더 좋을 수도 저장하기 위해 임시 값을 사용합니다.

  2. 픽셀에는 여러 값이 있어야합니다. comprareTwoPixel (색상 A, 색상 B)이라는 함수를 만들려고 할 수 있습니까? 모든 값을 하나씩 비교하십시오. 그 중 하나가 거짓이면 나머지를 비교할 필요가 없습니다. 거짓을 반환하십시오. (확실하지 않음이 만들 경우 빠르거나 아니지만.)

처럼 : 당신은 때로 믿을 수 있기 때문에

bool comprareTwoPixel(color A, color B) 
{ 
    if(A.a!=B.b) 
     return false; 
    if(A.b!=B.b) 
     return false; 
    if(A.c!=B.c) 
     return false; 

    return true; 
} 
0

나는,이 코드와 함께 일을하려고하는지 모르겠어요 't 변경된 인덱스를 저장 ...

당신은, 당신의 목록에 대한 다른 생성자를 사용할 수 있기 때문에 목록()는 L의 내부 버퍼를 다시 할당합니다 의미 haves a default capacity of 0

많은 픽셀이 변경된 경우 아마 여러 번 반복됩니다.

변경된 평균 픽셀 수를 기록하고 목록의 초기 용량을 해당 숫자로 설정하면 코드 속도가 빨라질 수 있습니다. 적어도 다음과 같이 말할 수 있습니다. 픽셀의 10 %는 모든 프레임마다 변경됩니다.

List<Color> returnList = new List<Color>((int)(0.10 * numberOfPixel)); 
0

이것은 완벽하게 작동 할 수도 있지만 그렇지 않을 수도 있습니다. AsParallel을 추가하면 적합하다고 생각되면 병렬화 할 수 있습니다.

여기에도 몇 줄을 복사하여 붙여 넣었으므로 오타 또는 불일치 변수가 있는지 알려주거나 수정 해주세요. 그러나 이것은 그것의 요지입니다. 이유 안에서 꽤 빨리해야합니다.

기본적으로 이것은 이해하기가 약간 어려울 수 있으므로 비트를 "잠그고"해당 포인터를 사용하여 byte[]에 복사하는 것이 좋습니다. 그러면 효과적으로 RGB (A) 값을 모두 복사하여 실제로 쉽게 액세스 할 수 있습니다. 이것은 픽셀을 잡아내는 데 약간의 시간이 걸리기 때문에, 픽셀을 읽는 데 걸리는 시간은 GetPixel보다 빠릅니다.

일단 이들을 byte[] 개로 가져 오면 각 픽셀 좌표의 값을 비교하는 것만으로도 충분합니다. 필자는 LINQ를 사용하여 필요한 경우 병렬 처리가 쉽도록 선택했으나 실제로 구현할 것인지 선택하지 않을 수도 있습니다. 네가 필요가 있을지 모르겠다.

나는 당신의 구현이 모든 소스가 하나 인 이미지를 가지고있는 것처럼 들리므로 여기에 몇 가지 가정을하였습니다. 즉, 이미지의 크기와 형식이 같다고 가정합니다. 따라서 실제로 그렇지 않은 경우에는 여기에 몇 가지 추가 코드를 지정하여 해결할 수 있습니다.하지만 여전히 쉽습니다. 실제 구현에서

private byte[] UnlockBits(Bitmap bmp, out int stride) 
{ 
    BitmapData bmpData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat); 

    IntPtr ptr = bmpData.Scan0; 

    stride = bmpData.Stride; 

    int bytes = Math.Abs(bmpData.Stride) * bmp.Height; 

    byte[] ret = new byte[bytes]; 

    System.Runtime.InteropServices.Marshal.Copy(ptr, ret, 0, bytes); 

    bmp.UnlockBits(bmpData); 

    return ret; 
} 

private bool AreArraysEqual(byte[] a, byte[] b, int offset, int length) 
{ 
    for (int v = 0; v < length; v++) 
    { 
     int c = v + offset; 

     if (a[c] != b[c]) 
     { 
      return false; 
     } 
    } 
    return true; 
} 

private IEnumerable<KeyValuePair<Point, Tuple<Color, Color>>> GetDifferences(Bitmap a, Bitmap b) 
{ 
    if (a.PixelFormat != b.PixelFormat) 
     throw new ArgumentException("Unmatched formats!"); 

    if (a.Size != b.Size) 
     throw new ArgumentException("Unmatched length!"); 

    int stride; 
    byte[] rgbValuesA = UnlockBits(a, out stride); 
    byte[] rgbValuesB = UnlockBits(b, out stride); 

    if (rgbValuesA.Length != rgbValuesB.Length) 
     throw new ArgumentException("Unmatched array lengths (unexpected error)!"); 

    int bytesPerPixel = Image.GetPixelFormatSize(a.PixelFormat)/8; 

    return Enumerable.Range(0, a.Height).SelectMany(y => 
      Enumerable.Range(0, a.Width) 
        .Where(x => !AreArraysEqual(rgbValuesA, 
               rgbValuesB, 
               (y * stride) + (x * bytesPerPixel), 
               bytesPerPixel)) 
        .Select(x => 
          { 
           Point pt = new Point(x, y); 

           int pixelIndex = (y * stride) + (x * bytesPerPixel); 

           Color colorA = ReadPixel(rgbValuesA, pixelIndex, bytesPerPixel); 
           Color colorB = ReadPixel(rgbValuesB, pixelIndex, bytesPerPixel); 

           return new KeyValuePair<Point, Tuple<Color, Color>>(pt, colorA, colorB); 
          } 
} 

private Color ReadPixel(byte[] bytes, int offset, int bytesPerPixel) 
{ 
    int argb = BitConverter.ToInt32(pixelBytes, offset); 

    if (bytesPerPixel == 3) // no alpha 
     argb |= (255 << 24); 

    return Color.FromArgb(argb); 
} 

public IEnumerable<KeyValuePair<Point, Color>> GetNewColors(Bitmap _new, Bitmap old) 
{ 
    return GetDifferences(_new, old).Select(c => new KeyValuePair<Point, Color>(c.Key, c.Value.Item1)); 
} 

, 당신은 좀 더 철저하게 내가 가진 것보다 엔디 언 및 픽셀 형식에 대해 생각 할 수도 있지만,이 개념의 증거로 어느 정도 작동합니다, 나는 대부분을 처리 할 것으로 판단 실용적인 사례들.

@TaW가 의견에서 언급했듯이, 변경되지 않은 항목을 모두 블랭킹 (알파를 0으로 설정) 할 수도 있습니다. 또한 픽셀 잠금 해제를 통해 이익을 얻을 수 있습니다. 다시 말하지만, 어떻게하는지 알려주는 튜토리얼이 있습니다. 그러나 이것의 대부분은 동일하게 유지 될 수 있습니다.

1

이 루틴은 두 비트 맵 간의 차이점을 찾고 나머지는 거의 검은 색으로 거의 투명하게 설정하여 첫 번째 비트 맵에 반환합니다. 또한 이전 이미지로 결과를 다시 추가하여 원래의 두 번째 파일을 복원 할 수 있습니다 ..

나는 800MB 1o 12k의 스크린 샷을 축소했습니다. 그러나 Clocks의 손에 아주 작은 변화가있었습니다 ;-) 이미지는 많은 픽셀에서 다르지만 압축은 훌륭하지는 않을 것입니다. 그러나 나는 그것이 tranmitting을 위해 충분히 좋을 것이라고 믿습니다. 그리고 픽셀 단위의 어떤 것도 png 또는 jpg 파일 형식의 압축 루틴과 비교할 것입니다. 당신은 bmps를 전송하지 않는다, 나는 희망한다!)

루틴은 LockBits를 사용하고 꽤 빠르다.

bool 매개 변수는 차이 비트 맵을 만들지 또는 변경된 비트 맵을 복원할지 여부를 결정합니다.

public static Bitmap Difference(Bitmap bmp0, Bitmap bmp1, bool restore) 
{ 
    int Bpp = 4; // assuming an effective pixelformat of 32bpp 
    var bmpData0 = bmp0.LockBits(
        new Rectangle(0, 0, bmp0.Width, bmp0.Height), 
        ImageLockMode.ReadWrite, bmp0.PixelFormat); 
    var bmpData1 = bmp1.LockBits(
        new Rectangle(0, 0, bmp1.Width, bmp1.Height), 
        ImageLockMode.ReadOnly, bmp1.PixelFormat); 

    int len = bmpData0.Height * bmpData0.Stride; 
    byte[] data0 = new byte[len]; 
    byte[] data1 = new byte[len]; 
    Marshal.Copy(bmpData0.Scan0, data0, 0, len); 
    Marshal.Copy(bmpData1.Scan0, data1, 0, len); 

    for (int i = 0; i < len; i += Bpp) 
    { 
     if (restore) 
     { 
      bool toberestored = (data1[i ] != 2 && data1[i+1] != 3 && 
           data1[i+2] != 7 && data1[i+2] != 42); 
      if (toberestored) 
      { 
       data0[i ] = data1[i]; // Blue 
       data0[i+1] = data1[i+1]; // Green 
       data0[i+2] = data1[i+2]; // Red 
       data0[i+3] = data1[i+3]; // Alpha 
      } 
     } 
     else 
     { 
      bool changed = ((data0[i ] != data1[i ]) || 
          (data0[i+1] != data1[i+1]) || (data0[i+2] != data1[i+2])); 
      data0[i ] = changed ? data1[i ] : (byte)2; // special markers 
      data0[i+1] = changed ? data1[i+1] : (byte)3; // special markers 
      data0[i+2] = changed ? data1[i+2] : (byte)7; // special markers 
      data0[i+3] = changed ? (byte)255 : (byte)42; // special markers 
     } 
    } 

    Marshal.Copy(data0, 0, bmpData0.Scan0, len); 
    bmp0.UnlockBits(bmpData0); 
    bmp1.UnlockBits(bmpData1); 
    return bmp0; 
} 

참고 : - 난 수신자에 복원해야하는 픽셀을 표시하기 위해 특별한 색상을 선택했습니다. 여기서는 alpha=42R=7; G=3; B=2;을 선택했습니다. 100 % 안전하지는 않지만 거의 안전합니다. 많은 픽셀이 누락되지 않습니다. 그리고 어쩌면 당신은 어쨌든 투명감이 없습니다 ..?

난 400KB 주위에 두 개의 작은 이미지 모두 PNG 형식을 추가 :

enter image description here

복원 된 이미지는이다

enter image description here enter image description here

이 차분 화상 (로 3kB) 인 두 번째 이미지와 동일합니다.

+0

코드를 약간 개선하고 오타를 수정했습니다. – TaW

관련 문제