2016-02-14 11 views
5

próbuję załadować plik JPEG i usunąć wszystkie czarne i białe piksele z obrazuNET Bitmap.Load metoda produkować inny wynik na różnych komputerach

C# Kod:

... 
    m_SrcImage = new Bitmap(imagePath); 

    Rectangle r = new Rectangle(0, 0, m_SrcImage.Width, m_SrcImage.Height); 
    BitmapData bd = m_SrcImage.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); 

    //Load Colors 
    int[] colours = new int[m_SrcImage.Width * m_SrcImage.Height]; 
    Marshal.Copy(bd.Scan0, colours, 0, colours.Length); 
    m_SrcImage.UnlockBits(bd); 

    int len = colours.Length; 

    List<Color> result = new List<Color>(len); 

    for (int i = 0; i < len; ++i) 
    { 
     uint w = ((uint)colours[i]) & 0x00FFFFFF; //Delete alpha-channel 
     if (w != 0x00000000 && w != 0x00FFFFFF) //Check pixel is not black or white 
     { 
      w |= 0xFF000000;      //Return alpha channel 
      result.Add(Color.FromArgb((int)w)); 
     } 
    } 
    ... 

Potem staram się znaleźć unikalne kolory na liście według tego kodu

result.Sort((a, b) => 
    { 
     return a.R != b.R ? a.R - b.R : 
       a.G != b.G ? a.G - b.G : 
       a.B != b.B ? a.B - b.B : 
       0; 
    }); 


    List<Color> uniqueColors = new List<Color>(result.Count); 

    Color rgbTemp = result[0]; 

    for (int i = 0; i < len; ++i) 
    { 
     if (rgbTemp == result[i]) 
     {  
       continue; 
     } 

     uniqueColors.Add(rgbTemp); 
     rgbTemp = result[i]; 
    } 
    uniqueColors.Add(rgbTemp); 

Ten kod daje różne wyniki na różnych maszynach na tym samym obrazie!

Na przykład this image produkuje:

  • 43.198 unikalnych kolorów na XP SP3 w wersji .NET 4
  • 43168 unikalnych kolorów na Win7 Ostatecznego z wersji NET 4,5

Minimalna projekt testowy można download here. Po prostu otwiera wybrany obraz i tworzy plik txt z unikalnymi kolorami.

Jeszcze jeden fakt. Niektóre piksele są odczytywane różnie na różnych komputerach. Porównuję pliki txt z notatnikiem ++ i pokazuje, że niektóre piksele mają różne komponenty RGB. Różnica wynosi 1 dla każdego składnika, np.

  • Win7 pikseli: 255 200 100
  • WinXP pikseli: 254 199 99

mam przeczytać ten post

stackoverflow.com/questions/2419598/why-might-different- komputery-oblicz-różne-arytmetyczne-wyniki-in-vb-net

(przepraszam, nie mam wystarczająco dużo raiting dla normalnego łącza).

... ale nie było informacji, jak to naprawić.


Projekt został skompilowany dla .NET 4 Client Profile na komputerze z systemem OS Windows 7 w VS 2015 Commumity Edition.

+0

Aby przesłać obrazy, należy wybrać usługę bezpłatnego przesyłania, która nie wymaga logowania się. – TaW

+0

@TaW Przenieś zdjęcie na imgur.com i przenieś projekt testowy na github –

+0

Dzięki. Jednak myślę, że odpowiedź Lasse jest poprawna: JPeg nie ma na celu reprodukcji obrazu z absolutną dokładnością. Ruszaj do PNG, jeśli naprawdę tego potrzebujesz! – TaW

Odpowiedz

3

Wikipedia has this to say about the accuracy requirements for JPEG Decoders:

Opis kodowania w standardzie JPEG nie określa dokładność potrzebną do wyjścia sprężonego obraz.Jednak standard JPEG (i podobne standardy MPEG) zawiera pewne wymagania dotyczące dokładności dekodowania, w tym wszystkie części procesu dekodowania (dekodowanie o zmiennej długości, odwrotny DCT, dekwantyzacja, renormalizacja wyjść); Wyjście z algorytmem odniesienia nie może przekraczać:

  • najwyżej jednego bitu różnicy dla każdego składnika piksela
  • niski średni kwadrat błędu na każdym 8 x 8 pikseli bloku
  • bardzo małej średniej błąd w ciągu każdego 8 × 8 pikseli bloku
  • bardzo niską średnią kwadratową błędów w całym obrazie
  • niezwykle niski średni błąd w całym obrazie

(podkreślenie moje)

Krótko mówiąc, nie ma po prostu dwa różne implementacje dekodera w grę tutaj, a oni produkują różne obrazy, w ramach wymogu dokładności (1 bit = +/- 1 w składniku wartości, jak zauważyłeś).

Takiego krótkiego użycia tego samego (niewbudowanego) dekodera jpeg można się tego spodziewać. Jeśli potrzebujesz dokładnie tego samego wyjścia, prawdopodobnie musisz przełączyć się do innego dekodera, takiego, który będzie taki sam, bez względu na to, która wersja .NET lub Windows to uruchomisz. Zgaduję, że GDI + jest winowajcą, ponieważ przeszedł on większe zmiany od czasu Windows XP.

+0

To smutna wiadomość ... Dziękuję za odpowiedź. Czy znasz bibliotekę, która może współpracować z formatami PNG, BMP, JPEG i TIFF? –

+0

Próbowałem dodać bibliotekę Libjpeg.NET Bitmiracle do projektu i uzyskać ten sam rezultat ... –

+1

Dlaczego potrzebujesz tego samego do tworzenia tych samych pikseli na platformach? Używasz stratnego formatu obrazu kompresji, i tak nie masz prawdziwej kontroli nad pikselami. –

0

mi rozwiązać mój problem dodając Libjpeg.NET do projektu i napisać ten kod:

 private Bitmap JpegToBitmap(JpegImage jpeg) 
     { 
      int width = jpeg.Width; 
      int height = jpeg.Height; 

      // Read the image into the memory buffer 
      int[] raster = new int[height * width]; 

      for(int i = 0; i < height; ++i) 
      { 
       byte[] temp = jpeg.GetRow(i).ToBytes(); 

       for (int j = 0; j < temp.Length; j += 3) 
       { 
        int offset = i*width + j/3; 
        raster[offset] = 0; 
        raster[offset] |= (((int)temp[j+2]) << 16); 
        raster[offset] |= (((int)temp[j+1]) << 8); 
        raster[offset] |= (int)temp[j]; 
       } 
      } 

      Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb); 
      Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); 
      BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 
      byte[] bits = new byte[bmpdata.Stride * bmpdata.Height]; 

      for (int y = 0; y < bmp.Height; y++) 
      { 
       int rasterOffset = y * bmp.Width; 
       int bitsOffset = (bmp.Height - y - 1) * bmpdata.Stride; 

       for (int x = 0; x < bmp.Width; x++) 
       { 
        int rgba = raster[rasterOffset++]; 
        bits[bitsOffset++] = (byte)((rgba >> 16) & 0xff); 
        bits[bitsOffset++] = (byte)((rgba >> 8) & 0xff); 
        bits[bitsOffset++] = (byte)(rgba & 0xff); 
       } 
      } 
      System.Runtime.InteropServices.Marshal.Copy(bits, 0, bmpdata.Scan0, bits.Length); 
      bmp.UnlockBits(bmpdata); 

      return bmp; 
     } 

Tak, to dla mnie za mało.

Powiązane problemy