From 7255ede3af7bf3dcc0db841103d1e2e3b3a790d7 Mon Sep 17 00:00:00 2001 From: Daniil Osokin Date: Sun, 24 Feb 2013 21:50:08 +0400 Subject: [PATCH 1/3] Added implementation for RGB to YUV420p color conversion --- .../imgproc/include/opencv2/imgproc/types_c.h | 16 +++- modules/imgproc/src/color.cpp | 79 ++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc/types_c.h b/modules/imgproc/include/opencv2/imgproc/types_c.h index 0e4f0a2445..ba25af9f91 100644 --- a/modules/imgproc/include/opencv2/imgproc/types_c.h +++ b/modules/imgproc/include/opencv2/imgproc/types_c.h @@ -309,8 +309,22 @@ enum // alpha premultiplication CV_RGBA2mRGBA = 125, CV_mRGBA2RGBA = 126, + + CV_RGB2YUV_I420 = 127, + CV_BGR2YUV_I420 = 128, + CV_RGB2YUV_IYUV = CV_RGB2YUV_I420, + CV_BGR2YUV_IYUV = CV_BGR2YUV_I420, - CV_COLORCVT_MAX = 127 + CV_RGBA2YUV_I420 = 129, + CV_BGRA2YUV_I420 = 130, + CV_RGBA2YUV_IYUV = CV_RGBA2YUV_I420, + CV_BGRA2YUV_IYUV = CV_BGRA2YUV_I420, + CV_RGB2YUV_YV12 = 131, + CV_BGR2YUV_YV12 = 132, + CV_RGBA2YUV_YV12 = 133, + CV_BGRA2YUV_YV12 = 134, + + CV_COLORCVT_MAX = 135 }; diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index e85acea3f3..82c219568d 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -3076,6 +3076,58 @@ inline void cvtYUV420p2RGBA(Mat& _dst, int _stride, const uchar* _y1, const ucha converter(BlockedRange(0, _dst.rows/2)); } +///////////////////////////////////// RGB -> YUV420p ///////////////////////////////////// + +template +static void cvtRGBtoYUV420p(const Mat& src, Mat& dst) +{ + //const float coeffs[] = { 0.257f, 0.504f, 0.098f, + // -0.148f, -0.291f, 0.439f, + // -0.368f, -0.071f }; + const int coeffs[] = { 269484, 528482, 102760, + -155188, -305135, 460324, + -385875, -74448 }; + + const int w = src.cols; + const int h = src.rows; + + const int cn = src.channels(); + for( int i = 0; i < h / 2; i++ ) + { + const uchar* row0 = src.ptr(2*i); + const uchar* row1 = src.ptr(2*i + 1); + + uchar* y = dst.ptr(2*i); + uchar* u = dst.ptr(h + i/2) + (i % 2) * (w/2); + uchar* v = dst.ptr(h + (i + h/2)/2) + ((i + h/2) % 2) * (w/2); + if( uIdx == 2 ) std::swap(u, v); + + for( int j = 0, k = 0; j < w * cn; j += 2*cn, k++ ) + { + int r00 = row0[2-bIdx + j]; int g00 = row0[1 + j]; int b00 = row0[bIdx + j]; + int r01 = row0[2-bIdx + cn + j]; int g01 = row0[1 + cn + j]; int b01 = row0[bIdx + cn + j]; + int r10 = row1[2-bIdx + j]; int g10 = row1[1 + j]; int b10 = row1[bIdx + j]; + int r11 = row1[2-bIdx + cn + j]; int g11 = row1[1 + cn + j]; int b11 = row1[bIdx + cn + j]; + + int y00 = coeffs[0]*r00 + coeffs[1]*g00 + coeffs[2]*b00 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); + int y01 = coeffs[0]*r01 + coeffs[1]*g01 + coeffs[2]*b01 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); + int y10 = coeffs[0]*r10 + coeffs[1]*g10 + coeffs[2]*b10 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); + int y11 = coeffs[0]*r11 + coeffs[1]*g11 + coeffs[2]*b11 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); + + y[2*k + 0] = saturate_cast(y00 >> ITUR_BT_601_SHIFT); + y[2*k + 1] = saturate_cast(y01 >> ITUR_BT_601_SHIFT); + y[2*k + dst.step + 0] = saturate_cast(y10 >> ITUR_BT_601_SHIFT); + y[2*k + dst.step + 1] = saturate_cast(y11 >> ITUR_BT_601_SHIFT); + + int u00 = coeffs[3]*r00 + coeffs[4]*g00 + coeffs[5]*b00 + (1 << (ITUR_BT_601_SHIFT - 1)) + (128 << ITUR_BT_601_SHIFT); + int v00 = coeffs[5]*r00 + coeffs[6]*g00 + coeffs[7]*b00 + (1 << (ITUR_BT_601_SHIFT - 1)) + (128 << ITUR_BT_601_SHIFT); + + u[k] = saturate_cast(u00 >> ITUR_BT_601_SHIFT); + v[k] = saturate_cast(v00 >> ITUR_BT_601_SHIFT); + } + } +} + ///////////////////////////////////// YUV422 -> RGB ///////////////////////////////////// template @@ -3713,6 +3765,31 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) src(Range(0, dstSz.height), Range::all()).copyTo(dst); } break; + case CV_RGB2YUV_YV12: case CV_BGR2YUV_YV12: case CV_RGBA2YUV_YV12: case CV_BGRA2YUV_YV12: + case CV_RGB2YUV_IYUV: case CV_BGR2YUV_IYUV: case CV_RGBA2YUV_IYUV: case CV_BGRA2YUV_IYUV: + { + if (dcn <= 0) dcn = 1; + const int bIdx = (code == CV_BGR2YUV_IYUV || code == CV_BGRA2YUV_IYUV || code == CV_BGR2YUV_YV12 || code == CV_BGRA2YUV_YV12) ? 0 : 2; + const int uIdx = (code == CV_BGR2YUV_IYUV || code == CV_BGRA2YUV_IYUV || code == CV_RGB2YUV_IYUV || code == CV_RGBA2YUV_IYUV) ? 1 : 2; + + CV_Assert( (scn == 3 || scn == 4) && depth == CV_8U ); + CV_Assert( dcn == 1 ); + CV_Assert( sz.width % 2 == 0 && sz.height % 2 == 0 ); + + Size dstSz(sz.width, sz.height / 2 * 3); + _dst.create(dstSz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + switch(bIdx + uIdx*10) + { + case 10: cvtRGBtoYUV420p<0, 1>(src, dst); break; + case 12: cvtRGBtoYUV420p<2, 1>(src, dst); break; + case 20: cvtRGBtoYUV420p<0, 2>(src, dst); break; + case 22: cvtRGBtoYUV420p<2, 2>(src, dst); break; + default: CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" ); break; + }; + } + break; case CV_YUV2RGB_UYVY: case CV_YUV2BGR_UYVY: case CV_YUV2RGBA_UYVY: case CV_YUV2BGRA_UYVY: case CV_YUV2RGB_YUY2: case CV_YUV2BGR_YUY2: case CV_YUV2RGB_YVYU: case CV_YUV2BGR_YVYU: case CV_YUV2RGBA_YUY2: case CV_YUV2BGRA_YUY2: case CV_YUV2RGBA_YVYU: case CV_YUV2BGRA_YVYU: @@ -3795,7 +3872,7 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) CV_Error( CV_StsBadArg, "Unsupported image depth" ); } } - break; + break; default: CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" ); } From d8f538d67becd29df4f6953a5d7bb40f5a0fb1f2 Mon Sep 17 00:00:00 2001 From: Daniil Osokin Date: Sun, 24 Feb 2013 23:52:06 +0400 Subject: [PATCH 2/3] Added tests for RGB to YUV420p color conversion --- modules/imgproc/perf/perf_cvt_color.cpp | 32 +++ modules/imgproc/test/test_cvtyuv.cpp | 292 +++++++++++++++++++++--- 2 files changed, 290 insertions(+), 34 deletions(-) diff --git a/modules/imgproc/perf/perf_cvt_color.cpp b/modules/imgproc/perf/perf_cvt_color.cpp index 3919539ef5..6f09163f97 100644 --- a/modules/imgproc/perf/perf_cvt_color.cpp +++ b/modules/imgproc/perf/perf_cvt_color.cpp @@ -115,6 +115,9 @@ CV_ENUM(CvtMode2, CV_YUV2BGR_NV12, CV_YUV2BGRA_NV12, CV_YUV2RGB_NV12, CV_YUV2RGB COLOR_YUV2GRAY_420, CV_YUV2RGB_UYVY, CV_YUV2BGR_UYVY, CV_YUV2RGBA_UYVY, CV_YUV2BGRA_UYVY, CV_YUV2RGB_YUY2, CV_YUV2BGR_YUY2, CV_YUV2RGB_YVYU, CV_YUV2BGR_YVYU, CV_YUV2RGBA_YUY2, CV_YUV2BGRA_YUY2, CV_YUV2RGBA_YVYU, CV_YUV2BGRA_YVYU) +CV_ENUM(CvtMode3, CV_RGB2YUV_IYUV, CV_BGR2YUV_IYUV, CV_RGBA2YUV_IYUV, CV_BGRA2YUV_IYUV, + CV_RGB2YUV_YV12, CV_BGR2YUV_YV12, CV_RGBA2YUV_YV12, CV_BGRA2YUV_YV12) + struct ChPair { ChPair(int _scn, int _dcn): scn(_scn), dcn(_dcn) {} @@ -162,6 +165,8 @@ ChPair getConversionInfo(int cvtMode) case CV_BGR5652BGRA: case CV_BGR5652RGBA: return ChPair(2,4); case CV_BGR2GRAY: case CV_RGB2GRAY: + case CV_RGB2YUV_IYUV: case CV_RGB2YUV_YV12: + case CV_BGR2YUV_IYUV: case CV_BGR2YUV_YV12: return ChPair(3,1); case CV_BGR2BGR555: case CV_BGR2BGR565: case CV_RGB2BGR555: case CV_RGB2BGR565: @@ -204,6 +209,8 @@ ChPair getConversionInfo(int cvtMode) case CX_YUV2BGRA: case CX_YUV2RGBA: return ChPair(3,4); case CV_BGRA2GRAY: case CV_RGBA2GRAY: + case CV_RGBA2YUV_IYUV: case CV_RGBA2YUV_YV12: + case CV_BGRA2YUV_IYUV: case CV_BGRA2YUV_YV12: return ChPair(4,1); case CV_BGRA2BGR555: case CV_BGRA2BGR565: case CV_RGBA2BGR555: case CV_RGBA2BGR565: @@ -306,3 +313,28 @@ PERF_TEST_P(Size_CvtMode2, cvtColorYUV420, SANITY_CHECK(dst, 1); } + +typedef std::tr1::tuple Size_CvtMode3_t; +typedef perf::TestBaseWithParam Size_CvtMode3; + +PERF_TEST_P(Size_CvtMode3, cvtColorRGB2YUV420p, + testing::Combine( + testing::Values(szVGA, sz720p, sz1080p, Size(130, 60)), + testing::ValuesIn(CvtMode3::all()) + ) + ) +{ + Size sz = get<0>(GetParam()); + int mode = get<1>(GetParam()); + ChPair ch = getConversionInfo(mode); + + Mat src(sz, CV_8UC(ch.scn)); + Mat dst(sz.height + sz.height / 2, sz.width, CV_8UC(ch.dcn)); + + declare.time(100); + declare.in(src, WARMUP_RNG).out(dst); + + TEST_CYCLE() cvtColor(src, dst, mode, ch.dcn); + + SANITY_CHECK(dst, 1); +} diff --git a/modules/imgproc/test/test_cvtyuv.cpp b/modules/imgproc/test/test_cvtyuv.cpp index 1a2ab018bd..61bc9bd8d4 100644 --- a/modules/imgproc/test/test_cvtyuv.cpp +++ b/modules/imgproc/test/test_cvtyuv.cpp @@ -30,6 +30,16 @@ public: static YUVreader* getReader(int code); }; +class RGBreader +{ +public: + virtual ~RGBreader() {} + virtual RGB read(const Mat& rgb, int row, int col) = 0; + virtual int channels() = 0; + + static RGBreader* getReader(int code); +}; + class RGBwriter { public: @@ -56,6 +66,21 @@ public: static GRAYwriter* getWriter(int code); }; +class YUVwriter +{ +public: + virtual ~YUVwriter() {} + + virtual void write(Mat& yuv, int row, int col, const YUV& val) = 0; + virtual int channels() = 0; + virtual Size size(Size imgSize) = 0; + + virtual bool requiresEvenHeight() { return true; } + virtual bool requiresEvenWidth() { return true; } + + static YUVwriter* getWriter(int code); +}; + class RGB888Writer : public RGBwriter { void write(Mat& rgb, int row, int col, const RGB& val) @@ -99,6 +124,42 @@ class BGRA8888Writer : public RGBwriter int channels() { return 4; } }; +class YUV420pWriter: public YUVwriter +{ + int channels() { return 1; } + Size size(Size imgSize) { return Size(imgSize.width, imgSize.height + imgSize.height/2); } +}; + +class YV12Writer: public YUV420pWriter +{ + void write(Mat& yuv, int row, int col, const YUV& val) + { + int h = yuv.rows * 2 / 3; + + yuv.ptr(row)[col] = val[0]; + if( row % 2 == 0 && col % 2 == 0 ) + { + yuv.ptr(h + row/4)[col/2 + ((row/2) % 2) * (yuv.cols/2)] = val[2]; + yuv.ptr(h + (row/2 + h/2)/2)[col/2 + ((row/2 + h/2) % 2) * (yuv.cols/2)] = val[1]; + } + } +}; + +class I420Writer: public YUV420pWriter +{ + void write(Mat& yuv, int row, int col, const YUV& val) + { + int h = yuv.rows * 2 / 3; + + yuv.ptr(row)[col] = val[0]; + if( row % 2 == 0 && col % 2 == 0 ) + { + yuv.ptr(h + row/4)[col/2 + ((row/2) % 2) * (yuv.cols/2)] = val[1]; + yuv.ptr(h + (row/2 + h/2)/2)[col/2 + ((row/2 + h/2) % 2) * (yuv.cols/2)] = val[2]; + } + } +}; + class YUV420Reader: public YUVreader { int channels() { return 1; } @@ -212,6 +273,49 @@ class YUV888Reader : public YUVreader bool requiresEvenWidth() { return false; } }; +class RGB888Reader : public RGBreader +{ + RGB read(const Mat& rgb, int row, int col) + { + return rgb.at(row, col); + } + + int channels() { return 3; } +}; + +class BGR888Reader : public RGBreader +{ + RGB read(const Mat& rgb, int row, int col) + { + RGB tmp = rgb.at(row, col); + return RGB(tmp[2], tmp[1], tmp[0]); + } + + int channels() { return 3; } +}; + +class RGBA8888Reader : public RGBreader +{ + RGB read(const Mat& rgb, int row, int col) + { + Vec4b rgba = rgb.at(row, col); + return RGB(rgba[0], rgba[1], rgba[2]); + } + + int channels() { return 4; } +}; + +class BGRA8888Reader : public RGBreader +{ + RGB read(const Mat& rgb, int row, int col) + { + Vec4b rgba = rgb.at(row, col); + return RGB(rgba[2], rgba[1], rgba[0]); + } + + int channels() { return 4; } +}; + class YUV2RGB_Converter { public: @@ -237,6 +341,23 @@ public: } }; +class RGB2YUV_Converter +{ +public: + YUV convert(RGB rgb) + { + int r = rgb[0]; + int g = rgb[1]; + int b = rgb[2]; + + uchar y = saturate_cast((int)( 0.257f*r + 0.504f*g + 0.098f*b + 0.5f) + 16); + uchar u = saturate_cast((int)(-0.148f*r - 0.291f*g + 0.439f*b + 0.5f) + 128); + uchar v = saturate_cast((int)( 0.439f*r - 0.368f*g - 0.071f*b + 0.5f) + 128); + + return YUV(y, u, v); + } +}; + YUVreader* YUVreader::getReader(int code) { switch(code) @@ -295,6 +416,27 @@ YUVreader* YUVreader::getReader(int code) } } +RGBreader* RGBreader::getReader(int code) +{ + switch(code) + { + case CV_RGB2YUV_YV12: + case CV_RGB2YUV_I420: + return new RGB888Reader(); + case CV_BGR2YUV_YV12: + case CV_BGR2YUV_I420: + return new BGR888Reader(); + case CV_RGBA2YUV_I420: + case CV_RGBA2YUV_YV12: + return new RGBA8888Reader(); + case CV_BGRA2YUV_YV12: + case CV_BGRA2YUV_I420: + return new BGRA8888Reader(); + default: + return 0; + }; +} + RGBwriter* RGBwriter::getWriter(int code) { switch(code) @@ -355,6 +497,25 @@ GRAYwriter* GRAYwriter::getWriter(int code) } } +YUVwriter* YUVwriter::getWriter(int code) +{ + switch(code) + { + case CV_RGB2YUV_YV12: + case CV_BGR2YUV_YV12: + case CV_RGBA2YUV_YV12: + case CV_BGRA2YUV_YV12: + return new YV12Writer(); + case CV_RGB2YUV_I420: + case CV_BGR2YUV_I420: + case CV_RGBA2YUV_I420: + case CV_BGRA2YUV_I420: + return new I420Writer(); + default: + return 0; + }; +} + template void referenceYUV2RGB(const Mat& yuv, Mat& rgb, YUVreader* yuvReader, RGBwriter* rgbWriter) { @@ -375,6 +536,64 @@ void referenceYUV2GRAY(const Mat& yuv, Mat& rgb, YUVreader* yuvReader, GRAYwrite grayWriter->write(rgb, row, col, cvt.convert(yuvReader->read(yuv, row, col))); } +template +void referenceRGB2YUV(const Mat& rgb, Mat& yuv, RGBreader* rgbReader, YUVwriter* yuvWriter) +{ + convertor cvt; + + for(int row = 0; row < rgb.rows; ++row) + for(int col = 0; col < rgb.cols; ++col) + yuvWriter->write(yuv, row, col, cvt.convert(rgbReader->read(rgb, row, col))); +} + +struct ConversionYUV +{ + ConversionYUV( const int code ) + { + yuvReader_ = YUVreader :: getReader(code); + yuvWriter_ = YUVwriter :: getWriter(code); + rgbReader_ = RGBreader :: getReader(code); + rgbWriter_ = RGBwriter :: getWriter(code); + grayWriter_ = GRAYwriter:: getWriter(code); + } + + int getDcn() + { + return (rgbWriter_ != 0) ? rgbWriter_->channels() : ((grayWriter_ != 0) ? grayWriter_->channels() : yuvWriter_->channels()); + } + + int getScn() + { + return (yuvReader_ != 0) ? yuvReader_->channels() : rgbReader_->channels(); + } + + Size getSrcSize( const Size& imgSize ) + { + return (yuvReader_ != 0) ? yuvReader_->size(imgSize) : imgSize; + } + + Size getDstSize( const Size& imgSize ) + { + return (yuvWriter_ != 0) ? yuvWriter_->size(imgSize) : imgSize; + } + + bool requiresEvenHeight() + { + return (yuvReader_ != 0) ? yuvReader_->requiresEvenHeight() : ((yuvWriter_ != 0) ? yuvWriter_->requiresEvenHeight() : false); + } + + bool requiresEvenWidth() + { + return (yuvReader_ != 0) ? yuvReader_->requiresEvenWidth() : ((yuvWriter_ != 0) ? yuvWriter_->requiresEvenWidth() : false); + } + + YUVreader* yuvReader_; + YUVwriter* yuvWriter_; + RGBreader* rgbReader_; + RGBwriter* rgbWriter_; + GRAYwriter* grayWriter_; +}; + CV_ENUM(YUVCVTS, CV_YUV2RGB_NV12, CV_YUV2BGR_NV12, CV_YUV2RGB_NV21, CV_YUV2BGR_NV21, CV_YUV2RGBA_NV12, CV_YUV2BGRA_NV12, CV_YUV2RGBA_NV21, CV_YUV2BGRA_NV21, CV_YUV2RGB_YV12, CV_YUV2BGR_YV12, CV_YUV2RGB_IYUV, CV_YUV2BGR_IYUV, @@ -383,7 +602,8 @@ CV_ENUM(YUVCVTS, CV_YUV2RGB_NV12, CV_YUV2BGR_NV12, CV_YUV2RGB_NV21, CV_YUV2BGR_N CV_YUV2RGB_YUY2, CV_YUV2BGR_YUY2, CV_YUV2RGB_YVYU, CV_YUV2BGR_YVYU, CV_YUV2RGBA_YUY2, CV_YUV2BGRA_YUY2, CV_YUV2RGBA_YVYU, CV_YUV2BGRA_YVYU, CV_YUV2GRAY_420, CV_YUV2GRAY_UYVY, CV_YUV2GRAY_YUY2, - CV_YUV2BGR, CV_YUV2RGB); + CV_YUV2BGR, CV_YUV2RGB, CV_RGB2YUV_YV12, CV_BGR2YUV_YV12, CV_RGBA2YUV_YV12, + CV_BGRA2YUV_YV12, CV_RGB2YUV_I420, CV_BGR2YUV_I420, CV_RGBA2YUV_I420, CV_BGRA2YUV_I420); typedef ::testing::TestWithParam Imgproc_ColorYUV; @@ -392,31 +612,32 @@ TEST_P(Imgproc_ColorYUV, accuracy) int code = GetParam(); RNG& random = theRNG(); - YUVreader* yuvReader = YUVreader::getReader(code); - RGBwriter* rgbWriter = RGBwriter::getWriter(code); - GRAYwriter* grayWriter = GRAYwriter::getWriter(code); - - int dcn = (rgbWriter == 0) ? grayWriter->channels() : rgbWriter->channels(); + ConversionYUV cvt(code); + const int scn = cvt.getScn(); + const int dcn = cvt.getDcn(); for(int iter = 0; iter < 30; ++iter) { Size sz(random.uniform(1, 641), random.uniform(1, 481)); - if(yuvReader->requiresEvenWidth()) sz.width += sz.width % 2; - if(yuvReader->requiresEvenHeight()) sz.height += sz.height % 2; + if(cvt.requiresEvenWidth()) sz.width += sz.width % 2; + if(cvt.requiresEvenHeight()) sz.height += sz.height % 2; - Size ysz = yuvReader->size(sz); - Mat src = Mat(ysz.height, ysz.width * yuvReader->channels(), CV_8UC1).reshape(yuvReader->channels()); + Size srcSize = cvt.getSrcSize(sz); + Mat src = Mat(srcSize.height, srcSize.width * scn, CV_8UC1).reshape(scn); - Mat dst = Mat(sz.height, sz.width * dcn, CV_8UC1).reshape(dcn); - Mat gold(sz, CV_8UC(dcn)); + Size dstSize = cvt.getDstSize(sz); + Mat dst = Mat(dstSize.height, dstSize.width * dcn, CV_8UC1).reshape(dcn); + Mat gold(dstSize, CV_8UC(dcn)); random.fill(src, RNG::UNIFORM, 0, 256); - if(rgbWriter) - referenceYUV2RGB(src, gold, yuvReader, rgbWriter); - else - referenceYUV2GRAY(src, gold, yuvReader, grayWriter); + if(cvt.rgbWriter_) + referenceYUV2RGB (src, gold, cvt.yuvReader_, cvt.rgbWriter_); + else if(cvt.grayWriter_) + referenceYUV2GRAY(src, gold, cvt.yuvReader_, cvt.grayWriter_); + else if(cvt.yuvWriter_) + referenceRGB2YUV (src, gold, cvt.rgbReader_, cvt.yuvWriter_); cv::cvtColor(src, dst, code, -1); @@ -429,40 +650,41 @@ TEST_P(Imgproc_ColorYUV, roi_accuracy) int code = GetParam(); RNG& random = theRNG(); - YUVreader* yuvReader = YUVreader::getReader(code); - RGBwriter* rgbWriter = RGBwriter::getWriter(code); - GRAYwriter* grayWriter = GRAYwriter::getWriter(code); - - int dcn = (rgbWriter == 0) ? grayWriter->channels() : rgbWriter->channels(); + ConversionYUV cvt(code); + const int scn = cvt.getScn(); + const int dcn = cvt.getDcn(); for(int iter = 0; iter < 30; ++iter) { Size sz(random.uniform(1, 641), random.uniform(1, 481)); - if(yuvReader->requiresEvenWidth()) sz.width += sz.width % 2; - if(yuvReader->requiresEvenHeight()) sz.height += sz.height % 2; + if(cvt.requiresEvenWidth()) sz.width += sz.width % 2; + if(cvt.requiresEvenHeight()) sz.height += sz.height % 2; int roi_offset_top = random.uniform(0, 6); int roi_offset_bottom = random.uniform(0, 6); int roi_offset_left = random.uniform(0, 6); int roi_offset_right = random.uniform(0, 6); - Size ysz = yuvReader->size(sz); + Size srcSize = cvt.getSrcSize(sz); + Mat src_full(srcSize.height + roi_offset_top + roi_offset_bottom, srcSize.width + roi_offset_left + roi_offset_right, CV_8UC(scn)); - Mat src_full(ysz.height + roi_offset_top + roi_offset_bottom, ysz.width + roi_offset_left + roi_offset_right, CV_8UC(yuvReader->channels())); - Mat dst_full(sz.height + roi_offset_left + roi_offset_right, sz.width + roi_offset_top + roi_offset_bottom, CV_8UC(dcn), Scalar::all(0)); + Size dstSize = cvt.getDstSize(sz); + Mat dst_full(dstSize.height + roi_offset_left + roi_offset_right, dstSize.width + roi_offset_top + roi_offset_bottom, CV_8UC(dcn), Scalar::all(0)); Mat gold_full(dst_full.size(), CV_8UC(dcn), Scalar::all(0)); random.fill(src_full, RNG::UNIFORM, 0, 256); - Mat src = src_full(Range(roi_offset_top, roi_offset_top + ysz.height), Range(roi_offset_left, roi_offset_left + ysz.width)); - Mat dst = dst_full(Range(roi_offset_left, roi_offset_left + sz.height), Range(roi_offset_top, roi_offset_top + sz.width)); - Mat gold = gold_full(Range(roi_offset_left, roi_offset_left + sz.height), Range(roi_offset_top, roi_offset_top + sz.width)); + Mat src = src_full(Range(roi_offset_top, roi_offset_top + srcSize.height), Range(roi_offset_left, roi_offset_left + srcSize.width)); + Mat dst = dst_full(Range(roi_offset_left, roi_offset_left + dstSize.height), Range(roi_offset_top, roi_offset_top + dstSize.width)); + Mat gold = gold_full(Range(roi_offset_left, roi_offset_left + dstSize.height), Range(roi_offset_top, roi_offset_top + dstSize.width)); - if(rgbWriter) - referenceYUV2RGB(src, gold, yuvReader, rgbWriter); - else - referenceYUV2GRAY(src, gold, yuvReader, grayWriter); + if(cvt.rgbWriter_) + referenceYUV2RGB (src, gold, cvt.yuvReader_, cvt.rgbWriter_); + else if(cvt.grayWriter_) + referenceYUV2GRAY(src, gold, cvt.yuvReader_, cvt.grayWriter_); + else if(cvt.yuvWriter_) + referenceRGB2YUV (src, gold, cvt.rgbReader_, cvt.yuvWriter_); cv::cvtColor(src, dst, code, -1); @@ -475,7 +697,9 @@ INSTANTIATE_TEST_CASE_P(cvt420, Imgproc_ColorYUV, (int)CV_YUV2RGBA_NV12, (int)CV_YUV2BGRA_NV12, (int)CV_YUV2RGBA_NV21, (int)CV_YUV2BGRA_NV21, (int)CV_YUV2RGB_YV12, (int)CV_YUV2BGR_YV12, (int)CV_YUV2RGB_IYUV, (int)CV_YUV2BGR_IYUV, (int)CV_YUV2RGBA_YV12, (int)CV_YUV2BGRA_YV12, (int)CV_YUV2RGBA_IYUV, (int)CV_YUV2BGRA_IYUV, - (int)CV_YUV2GRAY_420)); + (int)CV_YUV2GRAY_420, (int)CV_RGB2YUV_YV12, (int)CV_BGR2YUV_YV12, (int)CV_RGBA2YUV_YV12, + (int)CV_BGRA2YUV_YV12, (int)CV_RGB2YUV_I420, (int)CV_BGR2YUV_I420, (int)CV_RGBA2YUV_I420, + (int)CV_BGRA2YUV_I420)); INSTANTIATE_TEST_CASE_P(cvt422, Imgproc_ColorYUV, ::testing::Values((int)CV_YUV2RGB_UYVY, (int)CV_YUV2BGR_UYVY, (int)CV_YUV2RGBA_UYVY, (int)CV_YUV2BGRA_UYVY, From cf5e272878a75c9819d0fee89bd11ce4fcf3f43c Mon Sep 17 00:00:00 2001 From: Daniil Osokin Date: Sun, 24 Feb 2013 23:17:40 +0400 Subject: [PATCH 3/3] Added multithreaded implementation for RGB to YUV420p color conversion --- modules/imgproc/src/color.cpp | 126 ++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 45 deletions(-) diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index 82c219568d..3799d435e3 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -2744,6 +2744,16 @@ const int ITUR_BT_601_CVG = -852492; const int ITUR_BT_601_CVR = 1673527; const int ITUR_BT_601_SHIFT = 20; +// Coefficients for RGB to YUV420p conversion +const int ITUR_BT_601_CRY = 269484; +const int ITUR_BT_601_CGY = 528482; +const int ITUR_BT_601_CBY = 102760; +const int ITUR_BT_601_CRU = -155188; +const int ITUR_BT_601_CGU = -305135; +const int ITUR_BT_601_CBU = 460324; +const int ITUR_BT_601_CGV = -385875; +const int ITUR_BT_601_CBV = -74448; + template struct YUV420sp2RGB888Invoker { @@ -3078,54 +3088,80 @@ inline void cvtYUV420p2RGBA(Mat& _dst, int _stride, const uchar* _y1, const ucha ///////////////////////////////////// RGB -> YUV420p ///////////////////////////////////// +template +struct RGB888toYUV420pInvoker: public ParallelLoopBody +{ + RGB888toYUV420pInvoker( const Mat& src, Mat* dst, const int uIdx ) + : src_(src), + dst_(dst), + uIdx_(uIdx) { } + + void operator()(const Range& rowRange) const + { + const int w = src_.cols; + const int h = src_.rows; + + const int cn = src_.channels(); + for( int i = rowRange.start; i < rowRange.end; i++ ) + { + const uchar* row0 = src_.ptr(2 * i); + const uchar* row1 = src_.ptr(2 * i + 1); + + uchar* y = dst_->ptr(2*i); + uchar* u = dst_->ptr(h + i/2) + (i % 2) * (w/2); + uchar* v = dst_->ptr(h + (i + h/2)/2) + ((i + h/2) % 2) * (w/2); + if( uIdx_ == 2 ) std::swap(u, v); + + for( int j = 0, k = 0; j < w * cn; j += 2 * cn, k++ ) + { + int r00 = row0[2-bIdx + j]; int g00 = row0[1 + j]; int b00 = row0[bIdx + j]; + int r01 = row0[2-bIdx + cn + j]; int g01 = row0[1 + cn + j]; int b01 = row0[bIdx + cn + j]; + int r10 = row1[2-bIdx + j]; int g10 = row1[1 + j]; int b10 = row1[bIdx + j]; + int r11 = row1[2-bIdx + cn + j]; int g11 = row1[1 + cn + j]; int b11 = row1[bIdx + cn + j]; + + const int shifted16 = (16 << ITUR_BT_601_SHIFT); + const int halfShift = (1 << (ITUR_BT_601_SHIFT - 1)); + int y00 = ITUR_BT_601_CRY * r00 + ITUR_BT_601_CGY * g00 + ITUR_BT_601_CBY * b00 + halfShift + shifted16; + int y01 = ITUR_BT_601_CRY * r01 + ITUR_BT_601_CGY * g01 + ITUR_BT_601_CBY * b01 + halfShift + shifted16; + int y10 = ITUR_BT_601_CRY * r10 + ITUR_BT_601_CGY * g10 + ITUR_BT_601_CBY * b10 + halfShift + shifted16; + int y11 = ITUR_BT_601_CRY * r11 + ITUR_BT_601_CGY * g11 + ITUR_BT_601_CBY * b11 + halfShift + shifted16; + + y[2*k + 0] = saturate_cast(y00 >> ITUR_BT_601_SHIFT); + y[2*k + 1] = saturate_cast(y01 >> ITUR_BT_601_SHIFT); + y[2*k + dst_->step + 0] = saturate_cast(y10 >> ITUR_BT_601_SHIFT); + y[2*k + dst_->step + 1] = saturate_cast(y11 >> ITUR_BT_601_SHIFT); + + const int shifted128 = (128 << ITUR_BT_601_SHIFT); + int u00 = ITUR_BT_601_CRU * r00 + ITUR_BT_601_CGU * g00 + ITUR_BT_601_CBU * b00 + halfShift + shifted128; + int v00 = ITUR_BT_601_CBU * r00 + ITUR_BT_601_CGV * g00 + ITUR_BT_601_CBV * b00 + halfShift + shifted128; + + u[k] = saturate_cast(u00 >> ITUR_BT_601_SHIFT); + v[k] = saturate_cast(v00 >> ITUR_BT_601_SHIFT); + } + } + } + + static bool isFit( const Mat& src ) + { + return (src.total() >= 320*240); + } + +private: + RGB888toYUV420pInvoker& operator=(const RGB888toYUV420pInvoker&); + + const Mat& src_; + Mat* const dst_; + const int uIdx_; +}; + template static void cvtRGBtoYUV420p(const Mat& src, Mat& dst) { - //const float coeffs[] = { 0.257f, 0.504f, 0.098f, - // -0.148f, -0.291f, 0.439f, - // -0.368f, -0.071f }; - const int coeffs[] = { 269484, 528482, 102760, - -155188, -305135, 460324, - -385875, -74448 }; - - const int w = src.cols; - const int h = src.rows; - - const int cn = src.channels(); - for( int i = 0; i < h / 2; i++ ) - { - const uchar* row0 = src.ptr(2*i); - const uchar* row1 = src.ptr(2*i + 1); - - uchar* y = dst.ptr(2*i); - uchar* u = dst.ptr(h + i/2) + (i % 2) * (w/2); - uchar* v = dst.ptr(h + (i + h/2)/2) + ((i + h/2) % 2) * (w/2); - if( uIdx == 2 ) std::swap(u, v); - - for( int j = 0, k = 0; j < w * cn; j += 2*cn, k++ ) - { - int r00 = row0[2-bIdx + j]; int g00 = row0[1 + j]; int b00 = row0[bIdx + j]; - int r01 = row0[2-bIdx + cn + j]; int g01 = row0[1 + cn + j]; int b01 = row0[bIdx + cn + j]; - int r10 = row1[2-bIdx + j]; int g10 = row1[1 + j]; int b10 = row1[bIdx + j]; - int r11 = row1[2-bIdx + cn + j]; int g11 = row1[1 + cn + j]; int b11 = row1[bIdx + cn + j]; - - int y00 = coeffs[0]*r00 + coeffs[1]*g00 + coeffs[2]*b00 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); - int y01 = coeffs[0]*r01 + coeffs[1]*g01 + coeffs[2]*b01 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); - int y10 = coeffs[0]*r10 + coeffs[1]*g10 + coeffs[2]*b10 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); - int y11 = coeffs[0]*r11 + coeffs[1]*g11 + coeffs[2]*b11 + (1 << (ITUR_BT_601_SHIFT - 1)) + (16 << ITUR_BT_601_SHIFT); - - y[2*k + 0] = saturate_cast(y00 >> ITUR_BT_601_SHIFT); - y[2*k + 1] = saturate_cast(y01 >> ITUR_BT_601_SHIFT); - y[2*k + dst.step + 0] = saturate_cast(y10 >> ITUR_BT_601_SHIFT); - y[2*k + dst.step + 1] = saturate_cast(y11 >> ITUR_BT_601_SHIFT); - - int u00 = coeffs[3]*r00 + coeffs[4]*g00 + coeffs[5]*b00 + (1 << (ITUR_BT_601_SHIFT - 1)) + (128 << ITUR_BT_601_SHIFT); - int v00 = coeffs[5]*r00 + coeffs[6]*g00 + coeffs[7]*b00 + (1 << (ITUR_BT_601_SHIFT - 1)) + (128 << ITUR_BT_601_SHIFT); - - u[k] = saturate_cast(u00 >> ITUR_BT_601_SHIFT); - v[k] = saturate_cast(v00 >> ITUR_BT_601_SHIFT); - } - } + RGB888toYUV420pInvoker colorConverter(src, &dst, uIdx); + if( RGB888toYUV420pInvoker::isFit(src) ) + parallel_for_(Range(0, src.rows/2), colorConverter); + else + colorConverter(Range(0, src.rows/2)); } ///////////////////////////////////// YUV422 -> RGB /////////////////////////////////////