Avatar billede driis Nybegynder
26. juli 2007 - 18:34 Der er 6 kommentarer og
1 løsning

RGB data, endianness - kan det gøres smartere ?

Jeg læser nogle billedfiler, der indeholder billeddata i råt RGB format.

Hvis jeg blot tager selve billeddataene jeg kan finde i filen, og kopierer til et System.Drawing.Bitmap; er der byttet rundt på farverne rød og blå. Jeg er ret sikker på at det skyldes at hardwaren filerne er lavet på kører big-endian.

Det betyder at jeg er nødt til at bytte rundt på de læste bytes, før jeg kan komme frem til et brugbart billede, hvilket jeg gør således:

        public static void ChangeTripletEndinanness(byte [] buffer)
        {
            if ( buffer == null )
                throw new ArgumentNullException("buffer");

            if ( buffer.Length % 3 != 0 )
                throw new ArgumentException("Buffer length must be a multiple of 3");

            byte b;
            for(int index = 0 ; index < buffer.Length ; index += 3)
            {
                b = buffer[index];
                buffer[index] = buffer[index + 2];
                buffer[index + 2] = b;
            }
        }

Spørgsmålet er nu: Kan det gøres nemmere / hurtigere ? Performance er et issue, så jeg er interesseret i at operationen skal tage så kort tid som muligt.

Jeg har antaget at det bedste er at indlæse en buffer fra filen, bytte rundt på bytes og skrive til mit resultat billede - fremfor at læse 3 bytes af gangen og lave ombytningen her.
Avatar billede arne_v Ekspert
26. juli 2007 - 19:17 #1
Du kunne sammenligne din metode med Array.Reverse for hver triplet.
Avatar billede driis Nybegynder
29. juli 2007 - 20:27 #2
Tak for forslaget arne. Jeg har kigget lidt på Array.Reverse; hvilket i dette tilfælde giver marginalt dårligere performance end min egen metode. Jeg antager at det skyldes at jeg skal kalde Array.Reverse for hver triplet; dvs. der er mere overhead til funktionskald.

Jeg har også kigget på Array.Reverse i Reflector, og den gør stort set det samme som min kode. Udover at den først forsøger at kalde en ekstern metode "TrySZReverse"; som i teorien kunne være en mere effektiv implementation - men jeg tvivler.

Andre forslag er velkomne, men ellers lukker jeg spørgsmålet i løbet af et par dage.
arne_v, du må gerne lægge et svar; så du kan få del i points.
Avatar billede md_craig Nybegynder
30. juli 2007 - 14:22 #3
Inden man begynder alt for meget at rode i bud osv... så vil det være rart at vide lidt mere om filen gennerelt...

Der må vel være noget HeaderData ect. i filen?... eller lign... for lurede lidt på om man kunne arve fra en BinaryReader og på den måde evt. spare nogle motodekald samt det at flytning af den buffer du arbejder med, og så bare arbejde direkte på den underligende Streams Buffer...

Men det vil kræve en grundig forståelse af hvordan filen er bygget op...
Avatar billede arne_v Ekspert
31. juli 2007 - 04:37 #4
Jeg har eksperimenteret lidt.

Jeg tror at du skal holde dig til din eksisterende kode.

using System;

namespace E
{
    public class Program
    {
        public static void Test1(byte[] b)
        {
            byte tmp;
            for(int i = 0 ; i < b.Length; i += 3)
            {
                tmp = b[i];
                b[i] = b[i + 2];
                b[i + 2] = tmp;
            }   
        }
        public static void Test2(byte[] b)
        {
            for(int i = 0 ; i < b.Length; i += 3)
            {
                Array.Reverse(b, i, 3);
            }   
        }
        public static void Test3(byte[] b)
        {
            unsafe
            {
                fixed(byte *p = &b[0])
                {
                    byte tmp;
                    for(int i = 0 ; i < b.Length; i += 3)
                    {
                        byte *p1 = p + i;
                        byte *p2 = p1 + 2;
                        tmp = *p1;
                        *p1 = *p2;
                        *p2 = tmp;
                    }
                }
            }
        }
        public static void Test4(byte[] b)
        {
            for(int i = 0 ; i < b.Length; i += 3)
            {
                b[i] ^= b[i + 2];
                b[i + 2] ^= b[i];
                b[i] ^= b[i + 2];
            }
           
        }
        public static void Test5(byte[] b)
        {
            unsafe
            {
                fixed(byte *p = &b[0])
                {
                    for(int i = 0 ; i < b.Length; i += 3)
                    {
                        byte *p1 = p + i;
                        byte *p2 = p1 + 2;
                        *p1 ^= *p2;
                        *p2 ^= *p1;
                        *p1 ^= *p2;
                    }
                }
            }
        }
        private const int N = 10000000;
        public static void Main(string[] args)
        {
            byte[] b = new byte[30];
            DateTime dt1 = DateTime.Now;
            for(int i = 0; i < N; i++)
            {
                Test1(b);
            }
            DateTime dt2 = DateTime.Now;
            Console.WriteLine("Original: " + (dt2 - dt1).TotalSeconds);
            DateTime dt3 = DateTime.Now;
            for(int i = 0; i < N; i++)
            {
                Test2(b);
            }
            DateTime dt4 = DateTime.Now;
            Console.WriteLine("Framework: " + (dt4 - dt3).TotalSeconds);
            DateTime dt5 = DateTime.Now;
            for(int i = 0; i < N; i++)
            {
                Test3(b);
            }
            DateTime dt6 = DateTime.Now;
            Console.WriteLine("Unsafe: " + (dt6 - dt5).TotalSeconds);
            DateTime dt7 = DateTime.Now;
            for(int i = 0; i < N; i++)
            {
                Test4(b);
            }
            DateTime dt8 = DateTime.Now;
            Console.WriteLine("Xor: " + (dt8 - dt7).TotalSeconds);
            DateTime dt9 = DateTime.Now;
            for(int i = 0; i < N; i++)
            {
                Test5(b);
            }
            DateTime dt10 = DateTime.Now;
            Console.WriteLine("Unsafe + Xor: " + (dt10 - dt9).TotalSeconds);
            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

giver:

Original: 0,625
Framework: 8,9375
Unsafe: 0,40625
Xor: 0,8125
Unsafe + Xor: 0,625

Men jeg vil ikke anbefale brug af unsafe kode.
Avatar billede arne_v Ekspert
31. juli 2007 - 04:37 #5
Og et svar.
Avatar billede driis Nybegynder
31. juli 2007 - 19:13 #6
Tak for testen, arne.
Avatar billede driis Nybegynder
31. juli 2007 - 19:16 #7
Jeg havde lidt luret på at en unsafe version ville være hurtigere, da man sparer bounds checket; men den er ikke så meget hurtigere at det kan forsvares, i mit tilfælde.

md_craig >> Godt forslag.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester