2013-04-19 36 views
5

Tôi có mảng byte với dữ liệu yuv420.Chuyển đổi từ yuv 420 thành hình ảnh <Bgr,byte>

byte[] yuv420;//yuv data 

Tôi làm cách nào để chuyển đổi số này thành Image<Bgr, byte>?

Tôi đã tìm thấy công thức toán học để chuyển đổi thành RGB và sau đó là Image<Bgr, byte> nhưng nó rất chậm. Có cách nào để chuyển đổi nhanh hơn không?

Có một lớp học trong Emgu để chuyển đổi

COLOR_CONVERSION(enum CV_YUV2RGB Convert YUV color to RGB) 

nhưng tôi không thể hiểu làm thế nào sử dụng lớp này. Có ai giúp được không?

static Bitmap ConvertYUV2RGB(byte[] yuvFrame, byte[] rgbFrame, int width, int height) 
{ 
    int uIndex = width * height; 
    int vIndex = uIndex + ((width * height) >> 2); 
    int gIndex = width * height; 
    int bIndex = gIndex * 2; 

    int temp = 0; 


    //图片为pic1,RGB颜色的二进制数据转换得的int r,g,b; 
    Bitmap bm = new Bitmap(width, height); 

    int r = 0; 
    int g = 0; 
    int b = 0; 


    for (int y = 0; y < height; y++) 
    { 
     for (int x = 0; x < width; x++) 
     { 
      // R分量 
      temp = (int)(yuvFrame[y * width + x] + (yuvFrame[vIndex + (y/2) * (width/2) + x/2] - 128) * YUV2RGB_CONVERT_MATRIX[0, 2]); 
      rgbFrame[y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp)); 
      // G分量 
      temp = (int)(yuvFrame[y * width + x] + (yuvFrame[uIndex + (y/2) * (width/2) + x/2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 1] + (yuvFrame[vIndex + (y/2) * (width/2) + x/2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 2]); 
      rgbFrame[gIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp)); 
      // B分量 
      temp = (int)(yuvFrame[y * width + x] + (yuvFrame[uIndex + (y/2) * (width/2) + x/2] - 128) * YUV2RGB_CONVERT_MATRIX[2, 1]); 
      rgbFrame[bIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp)); 
      Color c = Color.FromArgb(rgbFrame[y * width + x], rgbFrame[gIndex + y * width + x], rgbFrame[bIndex + y * width + x]); 
      bm.SetPixel(x, y, c); 
     } 
    } 
    return bm; 

} 

static double[,] YUV2RGB_CONVERT_MATRIX = new double[3, 3] { { 1, 0, 1.4022 }, { 1, -0.3456, -0.7145 }, { 1, 1.771, 0 } }; 
static byte clamp(float input) 
{ 
    if (input < 0) input = 0; 
    if (input > 255) input = 255; 
    return (byte)Math.Abs(input); 
} 

Trả lời

7

Bạn đang gặp may vì tôi đã giải quyết chính xác vấn đề này trước đây. Có một số liên kết trong mã để biết thêm thông tin.

Nói chung, luôn cố gắng sử dụng con trỏ khi thực hiện xử lý hình ảnh và tránh các chức năng gọi trong các vòng lồng nhau. Trong mã của tôi, kích thước so sánh là phần chậm nhất nhưng không may là cần thiết (thử tắt nó đi bằng cách sử dụng công tắc tiền xử lý).

Tôi phải nói rằng mặc dù cuối cùng tôi không bao giờ sử dụng chức năng này vì nó quá chậm, tôi đã chọn thực hiện nó trong C++ và gọi nó từ C# bằng cách sử dụng lệnh p.

private static unsafe void YUV2RGBManaged(byte[] YUVData, byte[] RGBData, int width, int height) 
    { 

     //returned pixel format is 2yuv - i.e. luminance, y, is represented for every pixel and the u and v are alternated 
     //like this (where Cb = u , Cr = y) 
     //Y0 Cb Y1 Cr Y2 Cb Y3 

     /*http://msdn.microsoft.com/en-us/library/ms893078.aspx 
     * 
     * C = Y - 16 
     D = U - 128 
     E = V - 128 
     R = clip((298 * C   + 409 * E + 128) >> 8) 
     G = clip((298 * C - 100 * D - 208 * E + 128) >> 8) 
     B = clip((298 * C + 516 * D   + 128) >> 8) 

     * here are a whole bunch more formats for doing this... 
     * http://stackoverflow.com/questions/3943779/converting-to-yuv-ycbcr-colour-space-many-versions 
     */ 


     fixed(byte* pRGBs = RGBData, pYUVs = YUVData) 
     { 
      for (int r = 0; r < height; r++) 
      { 
       byte* pRGB = pRGBs + r * width * 3; 
       byte* pYUV = pYUVs + r * width * 2; 

       //process two pixels at a time 
       for (int c = 0; c < width; c += 2) 
       { 
        int C1 = pYUV[1] - 16; 
        int C2 = pYUV[3] - 16; 
        int D = pYUV[2] - 128; 
        int E = pYUV[0] - 128; 

        int R1 = (298 * C1 + 409 * E + 128) >> 8; 
        int G1 = (298 * C1 - 100 * D - 208 * E + 128) >> 8; 
        int B1 = (298 * C1 + 516 * D + 128) >> 8; 

        int R2 = (298 * C2 + 409 * E + 128) >> 8; 
        int G2 = (298 * C2 - 100 * D - 208 * E + 128) >> 8; 
        int B2 = (298 * C2 + 516 * D + 128) >> 8; 
#if true 
        //check for overflow 
        //unsurprisingly this takes the bulk of the time. 
        pRGB[0] = (byte)(R1 < 0 ? 0 : R1 > 255 ? 255 : R1); 
        pRGB[1] = (byte)(G1 < 0 ? 0 : G1 > 255 ? 255 : G1); 
        pRGB[2] = (byte)(B1 < 0 ? 0 : B1 > 255 ? 255 : B1); 

        pRGB[3] = (byte)(R2 < 0 ? 0 : R2 > 255 ? 255 : R2); 
        pRGB[4] = (byte)(G2 < 0 ? 0 : G2 > 255 ? 255 : G2); 
        pRGB[5] = (byte)(B2 < 0 ? 0 : B2 > 255 ? 255 : B2); 
#else 
        pRGB[0] = (byte)(R1); 
        pRGB[1] = (byte)(G1); 
        pRGB[2] = (byte)(B1); 

        pRGB[3] = (byte)(R2); 
        pRGB[4] = (byte)(G2); 
        pRGB[5] = (byte)(B2); 
#endif 

        pRGB += 6; 
        pYUV += 4; 
       } 
      } 
     } 
    } 

và trong trường hợp bạn quyết định thực hiện điều này trong C++

void YUV2RGB(void *yuvDataIn,void *rgbDataOut, int w, int h, int outNCh) 
{ 

    const int ch2 = 2 * outNCh; 

    unsigned char* pRGBs = (unsigned char*)rgbDataOut; 
    unsigned char* pYUVs = (unsigned char*)yuvDataIn; 

    for (int r = 0; r < h; r++) 
    { 
     unsigned char* pRGB = pRGBs + r * w * outNCh; 
     unsigned char* pYUV = pYUVs + r * w * 2; 

     //process two pixels at a time 
     for (int c = 0; c < w; c += 2) 
     { 
      int C1 = pYUV[1] - 16; 
      int C2 = pYUV[3] - 16; 
      int D = pYUV[2] - 128; 
      int E = pYUV[0] - 128; 

      int R1 = (298 * C1 + 409 * E + 128) >> 8; 
      int G1 = (298 * C1 - 100 * D - 208 * E + 128) >> 8; 
      int B1 = (298 * C1 + 516 * D + 128) >> 8; 

      int R2 = (298 * C2 + 409 * E + 128) >> 8; 
      int G2 = (298 * C2 - 100 * D - 208 * E + 128) >> 8; 
      int B2 = (298 * C2 + 516 * D + 128) >> 8; 

      //unsurprisingly this takes the bulk of the time. 
      pRGB[0] = (unsigned char)(R1 < 0 ? 0 : R1 > 255 ? 255 : R1); 
      pRGB[1] = (unsigned char)(G1 < 0 ? 0 : G1 > 255 ? 255 : G1); 
      pRGB[2] = (unsigned char)(B1 < 0 ? 0 : B1 > 255 ? 255 : B1); 

      pRGB[3] = (unsigned char)(R2 < 0 ? 0 : R2 > 255 ? 255 : R2); 
      pRGB[4] = (unsigned char)(G2 < 0 ? 0 : G2 > 255 ? 255 : G2); 
      pRGB[5] = (unsigned char)(B2 < 0 ? 0 : B2 > 255 ? 255 : B2); 

      pRGB += ch2; 
      pYUV += 4; 
     } 
    } 
} 
+0

Cảm ơn, rất nhiều người có một số ý tưởng khác? Vì func này không nhanh. – user1541069

+0

bạn đã thử phiên bản nào ?? phiên bản C++ nhanh gấp ba lần, vì vậy hãy sử dụng nó. nếu bạn cần càng nhanh càng tốt C# sẽ không hoạt động cho bạn. –

+0

làm thế nào để chuyển đổi byte [] RGBData thành bitmap? – user1541069

0

Người phạm tội lớn nhất trong mã đó là sử dụng Bitmap.SetPixel; nó là rất chậm để làm điều này trên mỗi vòng lặp lặp bên trong. Thay vào đó, sử dụng một mảng byte để lưu trữ các giá trị RGB của bạn và sau khi nó được lấp đầy, sao chép nó vào một bitmap as a single step.

Thứ hai, hiểu rằng y, u và v là byte và vì vậy chỉ có thể có 256 giá trị có thể. Do đó, hoàn toàn khả thi để xây dựng các bảng tra cứu cho r, g và b, do đó bạn không phải thực hiện bất kỳ phép tính nào trong vòng lặp bên trong của bạn.

Cuối cùng, nếu bạn thực sự muốn thực hiện, bạn sẽ phải viết điều này trong C++ bằng cách sử dụng số học con trỏ và biên dịch với tất cả các tối ưu hóa trên. Vòng lặp này cũng là một ứng cử viên rất tốt cho một parallel for vì mỗi lần lặp hoạt động trên dữ liệu độc lập. Bạn cũng có thể tối ưu hóa thêm điều này với SSE intrinsics, chuyển đổi nhiều pixel cho mỗi lệnh.

Hy vọng điều này sẽ giúp bạn bắt đầu.

+0

Thank, nhưng tôi không thể tin rằng không có dll để chuyển đổi yuv420 để RGB sử dụng dll Và những gì về chuyển đổi với lớp emgu COLOR_CONVERSION ??? – user1541069

+0

có thể có dll của những gì làm điều này. đó là một vấn đề phổ biến. Bây giờ tôi không thể nhớ tại sao tôi đã viết của riêng tôi, nhưng có lẽ vì tôi hoặc là không thể tìm thấy một thực hiện hoặc bởi vì tôi không muốn các biến chứng của liên kết trong opencv chỉ cho một chức năng. –

+0

Tôi sử dụng emgu trong dự án của mình. Và tốt nhất là chuyển đổi sang định dạng hình ảnh .i có 16 máy ảnh với khung hình 25 fps (704x576) gọi lại func return yuv420, chuyển đổi trong rgb phải càng nhanh càng tốt. – user1541069

0

tôi chỉ tìm thấy một mảnh cũ của mã mà có thể giúp bạn. YUV chuyển đổi sử dụng OpenCVSharp (Disclaimer: Tôi loại bỏ một số mã không cần thiết và chưa được thử nghiệm này!)

IplImage yuvImage = new IplImage(w, h, BitDepth.U8, 3); 
IplImage rgbImage = new IplImage(w, h, BitDepth.U8, 3); 

Cv.CvtColor(yuvImage, rgbImage, ColorConversion.CrCbToBgr); 

để trả lời câu hỏi khác của bạn - để chuyển đổi byte [] để việc sử dụng Bitmap này

int w= 100; 
int h = 200; 
int ch = 3; 

byte[] imageData = new byte[w*h*ch]; //you image data here 
Bitmap bitmap  = new Bitmap(w,h,PixelFormat.Format24bppRgb); 
BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); 
IntPtr pNative  = bmData.Scan0; 
Marshal.Copy(imageData,0,pNative,w*h*ch); 
bitmap.UnlockBits(bmData); 
+0

thnks, cho câu trả lời của bạn) – user1541069

+0

Khi tôi sử dụng phương pháp chuyển đổi ConvertYUV2RGB cũ của tôi là rất tốt! Và khi tôi sử dụng phương pháp YUV2RGBQuản lý và bí mật để bmp như trong tập tin post-bmp của bạn là màu xanh lá cây !!! – user1541069

+0

Tôi có thể chuyển đổi bằng GCHandle handle = GCHandle.Alloc (fmain.ALLImage [channelNumber], GCHandleType.Pinned); sử dụng (Hình ảnh yuv420sp = Hình ảnh mới (704, (576 >> 1) * 3, 704, handle.AddrOfPinnedObject())) { ảnh image = ảnh mới (704.576); CvInvoke.cvCvtColor (yuv420sp, hình ảnh, Emgu.CV.CvEnum.COLOR_CONVERSION.CV_YUV420sp2BGR); // hình ảnh hiện chứa cùng một ảnh yuv trong không gian màu bgr } handle.Free(); Nhưng bmpfile là màu đen và trong khi, không phải trong màu sắc. Làm thế nào để giải quyết – user1541069

0

Một chế độ nhanh hơn.Hai mutiplication và hai thêm ít mỗi pixel:

private static unsafe void YUV2RGBManaged(byte[] YUVData, byte[] RGBData, int width, int height) 
{ 
    //returned pixel format is 2yuv - i.e. luminance, y, is represented for every pixel and the u and v are alternated 
    //like this (where Cb = u , Cr = y) 
    //Y0 Cb Y1 Cr Y2 Cb Y3 

    /*http://msdn.microsoft.com/en-us/library/ms893078.aspx 
    * 
    C = 298 * (Y - 16) + 128 
    D = U - 128 
    E = V - 128 
    R = clip((C   + 409 * E) >> 8) 
    G = clip((C - 100 * D - 208 * E) >> 8) 
    B = clip((C + 516 * D   ) >> 8) 

    * here are a whole bunch more formats for doing this... 
    * http://stackoverflow.com/questions/3943779/converting-to-yuv-ycbcr-colour-space-many-versions 
    */ 


    fixed(byte* pRGBs = RGBData, pYUVs = YUVData) 
    { 
     for (int r = 0; r < height; r++) 
     { 
      byte* pRGB = pRGBs + r * width * 3; 
      byte* pYUV = pYUVs + r * width * 2; 

      //process two pixels at a time 
      for (int c = 0; c < width; c += 2) 
      { 
       int C1 = 298 * (pYUV[1] - 16) + 128; 
       int C2 = 298 * (pYUV[3] - 16) + 128; 
       int D = pYUV[2] - 128; 
       int E = pYUV[0] - 128; 

       int R1 = (C1 + 409 * E) >> 8; 
       int G1 = (C1 - 100 * D - 208 * E) >> 8; 
       int B1 = (C1 + 516 * D) >> 8; 

       int R2 = (C2 + 409 * E) >> 8; 
       int G2 = (C2 - 100 * D - 208 * E) >> 8; 
       int B2 = (298 * C2 + 516 * D) >> 8; 

       //check for overflow 
       //unsurprisingly this takes the bulk of the time. 
       pRGB[0] = (byte)(R1 < 0 ? 0 : R1 > 255 ? 255 : R1); 
       pRGB[1] = (byte)(G1 < 0 ? 0 : G1 > 255 ? 255 : G1); 
       pRGB[2] = (byte)(B1 < 0 ? 0 : B1 > 255 ? 255 : B1); 

       pRGB[3] = (byte)(R2 < 0 ? 0 : R2 > 255 ? 255 : R2); 
       pRGB[4] = (byte)(G2 < 0 ? 0 : G2 > 255 ? 255 : G2); 
       pRGB[5] = (byte)(B2 < 0 ? 0 : B2 > 255 ? 255 : B2); 

       pRGB += 6; 
       pYUV += 4; 
      } 
     } 
    } 
}