Fix Android NDK camera's color format conversions

This commit is contained in:
Giles Payne 2021-04-04 12:39:29 +09:00
parent 125b9f6057
commit b9b65e9392

View File

@ -31,6 +31,13 @@ using namespace cv;
#define COLOR_FormatYUV420Planar 19 #define COLOR_FormatYUV420Planar 19
#define COLOR_FormatYUV420SemiPlanar 21 #define COLOR_FormatYUV420SemiPlanar 21
#define FOURCC_BGR CV_FOURCC_MACRO('B','G','R','3')
#define FOURCC_RGB CV_FOURCC_MACRO('R','G','B','3')
#define FOURCC_GRAY CV_FOURCC_MACRO('G','R','E','Y')
#define FOURCC_NV21 CV_FOURCC_MACRO('N','V','2','1')
#define FOURCC_YV12 CV_FOURCC_MACRO('Y','V','1','2')
#define FOURCC_UNKNOWN 0xFFFFFFFF
template <typename T> class RangeValue { template <typename T> class RangeValue {
public: public:
T min, max; T min, max;
@ -180,7 +187,7 @@ class AndroidCameraCapture : public IVideoCapture
bool sessionOutputAdded = false; bool sessionOutputAdded = false;
bool targetAdded = false; bool targetAdded = false;
// properties // properties
bool convertToRgb = false; uint32_t fourCC = FOURCC_UNKNOWN;
bool settingWidth = false; bool settingWidth = false;
bool settingHeight = false; bool settingHeight = false;
int desiredWidth = 640; int desiredWidth = 640;
@ -303,10 +310,17 @@ public:
if ( (uvPixelStride == 2) && (vPixel == uPixel + 1) && (yLen == frameWidth * frameHeight) && (uLen == ((yLen / 2) - 1)) && (vLen == uLen) ) { if ( (uvPixelStride == 2) && (vPixel == uPixel + 1) && (yLen == frameWidth * frameHeight) && (uLen == ((yLen / 2) - 1)) && (vLen == uLen) ) {
colorFormat = COLOR_FormatYUV420SemiPlanar; colorFormat = COLOR_FormatYUV420SemiPlanar;
if (fourCC == FOURCC_UNKNOWN) {
fourCC = FOURCC_NV21;
}
} else if ( (uvPixelStride == 1) && (vPixel = uPixel + uLen) && (yLen == frameWidth * frameHeight) && (uLen == yLen / 4) && (vLen == uLen) ) { } else if ( (uvPixelStride == 1) && (vPixel = uPixel + uLen) && (yLen == frameWidth * frameHeight) && (uLen == yLen / 4) && (vLen == uLen) ) {
colorFormat = COLOR_FormatYUV420Planar; colorFormat = COLOR_FormatYUV420Planar;
if (fourCC == FOURCC_UNKNOWN) {
fourCC = FOURCC_YV12;
}
} else { } else {
colorFormat = COLOR_FormatUnknown; colorFormat = COLOR_FormatUnknown;
fourCC = FOURCC_UNKNOWN;
LOGE("Unsupported format"); LOGE("Unsupported format");
return false; return false;
} }
@ -324,9 +338,41 @@ public:
} }
Mat yuv(frameHeight + frameHeight/2, frameWidth, CV_8UC1, buffer.data()); Mat yuv(frameHeight + frameHeight/2, frameWidth, CV_8UC1, buffer.data());
if (colorFormat == COLOR_FormatYUV420Planar) { if (colorFormat == COLOR_FormatYUV420Planar) {
cv::cvtColor(yuv, out, convertToRgb ? cv::COLOR_YUV2RGB_YV12 : cv::COLOR_YUV2BGR_YV12); switch (fourCC) {
case FOURCC_BGR:
cv::cvtColor(yuv, out, cv::COLOR_YUV2BGR_YV12);
break;
case FOURCC_RGB:
cv::cvtColor(yuv, out, cv::COLOR_YUV2RGB_YV12);
break;
case FOURCC_GRAY:
cv::cvtColor(yuv, out, cv::COLOR_YUV2GRAY_YV12);
break;
case FOURCC_YV12:
yuv.copyTo(out);
break;
default:
LOGE("Unexpected FOURCC value: %d", fourCC);
break;
}
} else if (colorFormat == COLOR_FormatYUV420SemiPlanar) { } else if (colorFormat == COLOR_FormatYUV420SemiPlanar) {
cv::cvtColor(yuv, out, convertToRgb ? COLOR_YUV2RGB_NV21 : cv::COLOR_YUV2BGR_NV21); switch (fourCC) {
case FOURCC_BGR:
cv::cvtColor(yuv, out, cv::COLOR_YUV2BGR_NV21);
break;
case FOURCC_RGB:
cv::cvtColor(yuv, out, cv::COLOR_YUV2RGB_NV21);
break;
case FOURCC_GRAY:
cv::cvtColor(yuv, out, cv::COLOR_YUV2GRAY_NV21);
break;
case FOURCC_NV21:
yuv.copyTo(out);
break;
default:
LOGE("Unexpected FOURCC value: %d", fourCC);
break;
}
} else { } else {
LOGE("Unsupported video format: %d", colorFormat); LOGE("Unsupported video format: %d", colorFormat);
return false; return false;
@ -341,14 +387,14 @@ public:
return isOpened() ? frameWidth : desiredWidth; return isOpened() ? frameWidth : desiredWidth;
case CV_CAP_PROP_FRAME_HEIGHT: case CV_CAP_PROP_FRAME_HEIGHT:
return isOpened() ? frameHeight : desiredHeight; return isOpened() ? frameHeight : desiredHeight;
case CV_CAP_PROP_CONVERT_RGB:
return convertToRgb ? 1 : 0;
case CAP_PROP_AUTO_EXPOSURE: case CAP_PROP_AUTO_EXPOSURE:
return autoExposure ? 1 : 0; return autoExposure ? 1 : 0;
case CV_CAP_PROP_EXPOSURE: case CV_CAP_PROP_EXPOSURE:
return exposureTime; return exposureTime;
case CV_CAP_PROP_ISO_SPEED: case CV_CAP_PROP_ISO_SPEED:
return sensitivity; return sensitivity;
case CV_CAP_PROP_FOURCC:
return fourCC;
default: default:
break; break;
} }
@ -377,9 +423,40 @@ public:
settingHeight = false; settingHeight = false;
} }
return true; return true;
case CV_CAP_PROP_CONVERT_RGB: case CV_CAP_PROP_FOURCC:
convertToRgb = (value != 0); {
return true; uint32_t newFourCC = cvRound(value);
if (fourCC == newFourCC) {
return true;
} else {
switch (newFourCC) {
case FOURCC_BGR:
case FOURCC_RGB:
case FOURCC_GRAY:
fourCC = newFourCC;
return true;
case FOURCC_YV12:
if (colorFormat == COLOR_FormatYUV420Planar) {
fourCC = newFourCC;
return true;
} else {
LOGE("Unsupported FOURCC conversion COLOR_FormatYUV420SemiPlanar -> COLOR_FormatYUV420Planar");
return false;
}
case FOURCC_NV21:
if (colorFormat == COLOR_FormatYUV420SemiPlanar) {
fourCC = newFourCC;
return true;
} else {
LOGE("Unsupported FOURCC conversion COLOR_FormatYUV420Planar -> COLOR_FormatYUV420SemiPlanar");
return false;
}
default:
LOGE("Unsupported FOURCC value: %d\n", fourCC);
return false;
}
}
}
case CAP_PROP_AUTO_EXPOSURE: case CAP_PROP_AUTO_EXPOSURE:
autoExposure = (value != 0); autoExposure = (value != 0);
if (isOpened()) { if (isOpened()) {
@ -394,14 +471,14 @@ public:
camera_status_t status = ACaptureRequest_setEntry_i64(captureRequest.get(), ACAMERA_SENSOR_EXPOSURE_TIME, 1, &exposureTime); camera_status_t status = ACaptureRequest_setEntry_i64(captureRequest.get(), ACAMERA_SENSOR_EXPOSURE_TIME, 1, &exposureTime);
return status == ACAMERA_OK; return status == ACAMERA_OK;
} }
break; return false;
case CV_CAP_PROP_ISO_SPEED: case CV_CAP_PROP_ISO_SPEED:
if (isOpened() && sensitivityRange.Supported()) { if (isOpened() && sensitivityRange.Supported()) {
sensitivity = (int32_t)value; sensitivity = (int32_t)value;
camera_status_t status = ACaptureRequest_setEntry_i32(captureRequest.get(), ACAMERA_SENSOR_SENSITIVITY, 1, &sensitivity); camera_status_t status = ACaptureRequest_setEntry_i32(captureRequest.get(), ACAMERA_SENSOR_SENSITIVITY, 1, &sensitivity);
return status == ACAMERA_OK; return status == ACAMERA_OK;
} }
break; return false;
default: default:
break; break;
} }