OpenCV Basics - Others

1. YUV && YUVI420

  1. YUV,是一种颜色编码方法, 考虑到人类的感知能力,允许降低色度的带宽。“Y”表示明亮度(Luminance)即灰度值,“U”和“V”则是色度、浓度(Chrominance)
  2. 分成两个存储格式:
    • 紧缩格式(packed formats):将Y、U、V值存储成Macro Pixels数组,和RGB的存放方式类似。
    • 平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中,方便取出不同的平面
  3. YUV中只传送Y通道信息,显示完整的黑白图像,兼容黑白电视
  4. 对于UV通道,可以降采样,从而降低带宽,有3种方式:
    • YUV 4:4:4采样,每一个Y对应一组UV分量,没有降低存储
    • YUV 4:2:2采样,每两个Y共用一组UV分量:YUYV或者UYVY顺序排列存储,存储变为2/3。另:YUV422P,即平面格式,存完所有的Y,再存U,最后V
    • YUV 4:2:0采样,每四个Y共用一组UV分量:YV12,YU12,只有Planar模式,Y00,01,10,11共用U00和V00,3个平面依次存储YYYYYYYY UU VV;NV12,NV21,2个平面,UV合为一个平面交替存储YYYYYYYY UVUV。存储变为1/2,即UV都是之前的1/4
// OpenCV中I420图片,便于处理,分割三个平面
cv::cvtColor(srcImg, yuvImg, CV_BGR2YUV_I420);
cv::cvtColor(yuvImg, rgbImg, CV_YUV2BGR_I420);
// 拆分YUV I420格式的图片:
width = srcImg.cols;
height = srcImg.rows;
stride_0 = yuvImg.step;
stride_1 = yuvImg.step / 2;  // 因为长宽都是一半
stride_2 = yuvImg.step / 2;
data_0 = yuvImg.data;  // Y
data_1 = yuvImg.data + srcImg.cols * srcImg.rows;  // U
data_2 = modelIn.data_1 + srcImg.cols * srcImg.rows / 4;  // V

2. OpenCV编译

a. OpenCV 3.3.1, world和static_lib不可同时enable

否则会报错:Error LNK1181 cannot open input file ‘opencv_calib3d.lib’

官方解释 Static opencv_world is not supported - this configuration make no sense (no improvements from regular static build). 就是说world是为了方便,如果是dll只拷贝一个即可,static lib本身不需要dll,编译后即打包进可执行文件,没必要整出一个world来

b. 编译OpenCV,删减版,体积是1/5

  1. BUILD_CUDA等非build_opencv开头的,都关掉,只保留BUILD_WITH_DEBUG_INFO
  2. BUILD_opencv开头的,只保留:calib3d,core,features2d,flann,highgui,imgcodecs,imgproc,shape,video,videoio
  3. WITH_开头的,只保留:JPEG,VFW(windows打开camera/video需要),WIN32UI。With_TBB可以不打勾,这样打包dll时都能打包进去,不需要依赖tbb.dll
  4. opencv_dnn开头的,全部关掉
  5. 参考配置opencv_config.cmake

c. BUILD_WITH_STATIC_CRT设置为false,否则编译static lib的话默认是true

d. build script

# build shared lib
function build_shared {
	mkdir -p build_opencv
	pushd build_opencv
	cmake -C ../opencv_config.cmake \
		-DBUILD_SHARED_LIBS=1 \
		-DCMAKE_BUILD_TYPE=Release \
		-DCMAKE_CXX_FLAGS='-fpic' \
		-DCMAKE_C_FLAGS='-fpic' \
		-DCMAKE_INSTALL_PREFIX="./install" \
		..
	make -j20 || exit 1
	make install
	popd
}

# build static lib
function build_static {
	mkdir -p build_opencv_static
	pushd build_opencv_static
	cmake -C ../opencv_config.cmake \
		-DBUILD_SHARED_LIBS=0 \
		-DCMAKE_BUILD_TYPE=Release \
		-DCMAKE_INSTALL_PREFIX="./install" \
		..
	make -j20 || exit 1
	make install
	popd
}

if [[ $1 == "static" ]] ; then
	build_static
else
	build_shared
fi

e. Mac和Linux编译OpenCV之后,OpenCVConfig.cmake没有默认安装到install根目录,OpenCV_DIR指向share/OpenCV/即可。cmake相关文件在那里,切不可自己从外边拷贝一份进去

f. 如果用高版本gcc(>=5)编译,需要在根目录的CMakelist.txt里加如下编译选项

set(CMAKE_CXX_STANDARD 11)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)

Note: 另外,同一个工程引用不同的第三方lib,都需要用同一个版本的gcc编译,否则会引入未知奇怪的错误!

其中:

  • _GLIBCXX_USE_CXX11_ABI: 对于link时报错undefined reference std::__cxx11::basic_string,gcc官方说明(gcc高于5默认打开了_GLIBCXX_USE_CXX11_ABI):

    If you get linker errors about undefined references to symbols that involve types in the std::__cxx11 namespace or the tag [abi:cxx11] then it probably indicates that you are trying to link together object files that were compiled with different values for the _GLIBCXX_USE_CXX11_ABI macro. This commonly happens when linking to a third-party library that was compiled with an older version of GCC. If the third-party library cannot be rebuilt with the new ABI then you will need to recompile your code with the old ABI.

  • set(CMAKE_CXX_STANDARD 11):对于link时报错undefined reference to ‘operator delete(void*, unsigned long)’,是因为gcc6/7 or higher,默认的delete函数是这样的形式,也就是c++14中的形式,强制都设置成c11能避免很多问题
  • ld程序版本不一致,也会有问题:unrecognized relocation。比如用ld 2.26编译好了opencv,给2.24ld的机器用,link的时候必然报错。解决方法是降级编译opencv的ld,或升级用opencv的机器的ld:

“unrecognized relocation” occurs when the relocation type is greater than the greatest known relocation type in your version of ld

g. opencv依赖protobuf,避免与其他库依赖的protobuf冲突ref

  • opencv中,opencv_dnn依赖与protobuf,可以直接关掉,干脆不依赖protobuf:-DBUILD_opencv_dnn=OFF -DBUILD_PROTOBUF=OFF
  • 如果确实需要,那就用本地同一套protobuf,opencv自己下载的protobuf源码可能是3.3.1,需要关掉:
    • -D BUILD_PROTOBUF=OFF PROTOBUF_UPDATE_FILES=ON
    • -D PROTOBUF_INCLUDE_DIR=… Protobuf_PROTOC_EXECUTABLE=… Protobuf_LIBRARY=…

3. OpenCV的CMake集成

set(OpenCV_DIR "3rdparty/OpenCV_Win/${CMAKE_ARCH}_static")  # x86 or x64
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
set(DEPENDENCIES ${DEPENDENCIES} ${OpenCV_LIBS})  # 用target_link_libraries加入${DEPENDENCIES}

4. FFMPEG视频编解码

  • On Linux and other Unix flavors, OpenCV uses default or user-built ffmpeg/libav libraries. If user builds ffmpeg/libav from source and wants OpenCV to stay BSD library, not GPL/LGPL, should use –enabled-shared and make sure that no GPL components are enabled.

  • If you do not want to use FFMPEG at all, regardless of whether it’s installed on your system or not, configure and build OpenCV using CMake with WITH_FFMPEG=OFF flag. OpenCV will then use AVFoundation (OSX), GStreamer (Linux) or other available backends supported by opencv_videoio module.

  • There is also our self-contained motion jpeg codec, which you can use without any worries. It handles CV_FOURCC(‘M’, ‘J’, ‘P’, ‘G’) streams within an AVI container (“.avi”).

5. Mat的浅copy问题

// 有问题的方法:1次构造函数,4次copy构造,是浅copy,mat.data地址相同,4个Mat会相互影响
std::vector<cv::Mat> a = std::vector<cv::Mat>(4, cv::Mat::zeros(256, 256, CV_8UC4));
// 下边2种方法都可以
//    std::vector<cv::Mat> a = std::vector<cv::Mat>(4);
//    for(int i=0; i<a.size(); i++) {
////        a[i].create(256, 356, CV_8UC4);
//        a[i] = cv::Mat::zeros(256, 256, CV_8UC4);  // 这样是4次构造
//    }
uchar *a1 = a[0].data;
uchar *a2 = a[1].data;
uchar *a3 = a[2].data;
uchar *a4 = a[3].data;
std::cout << std::hex << (void*)a1 << " " << (void*)a2 << " " << (void*)a3 << " " << (void*)a4 << std::endl;

a1[100] = 200;
std::cout << (int)a1[100] << " " << (int)a2[100] << " " << (int)a3[100] << " " << (int)a4[100] << std::endl;
Loading Disqus comments...
Table of Contents