使用 OpenCV-Function calcHist 快速计算 v-disparity
Fast calculation of v-disparity with OpenCV-Function calcHist
基于被动立体相机系统的视差矩阵,我需要使用 OpenCV 计算用于障碍物检测的 v 视差表示。
一个可行的实现不是问题所在。问题是要快...
(一)v-Disparity 参考:Labayrade, R. 和 Aubert, D. 和 Tarel, J.P。通过 v 视差表示在非平坦道路几何体上进行立体视觉实时障碍物检测
基本简而言之,在视差矩阵的列上得到v视差(图1), is to analyze the rows of the disparity-matrix (figure 2) an represent the result as a histogram for each row over the disparity values. u-disparity (figure 3)是一样的。 (所有数字均为假色。)
我已经在 Python 和 C++ 中实现了 "same"。 Python 中的速度是可以接受的,但在 C++ 中,我得到 u 和 v 差异的时间大约为半秒(0.5 秒)。
(1. edit:由于是单独的时间测量,只有u直方图的计算比较耗时...)
这让我想到以下问题:
是否可以避免直方图逐行计算的循环?是否有一个 "trick" 可以通过从 OpenCV 调用 calcHist
-Function 来完成?也许与尺寸有关?
在 C++ 中是否只是编码错误并且运行时问题与用于计算的循环无关?
谢谢大家
Python 中的工作实施:
#!/usr/bin/env python2
#-*- coding: utf-8 -*-
#
# THIS SOURCE-CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED. IN NO EVENT WILL THE AUTHOR BE HELD LIABLE FOR ANY DAMAGES ARISING FROM
# THE USE OF THIS SOURCE-CODE. USE AT YOUR OWN RISK.
import cv2
import numpy as np
import time
def draw_object(image, x, y, width=50, height=100):
color = image[y, x]
image[y-height:y, x-width//2:x+width//2] = color
IMAGE_HEIGHT = 600
IMAGE_WIDTH = 800
while True:
max_disp = 200
# create fake disparity
image = np.zeros((IMAGE_HEIGHT, IMAGE_WIDTH), np.uint8)
for c in range(IMAGE_HEIGHT)[::-1]:
image[c, ...] = int(float(c) / IMAGE_HEIGHT * max_disp)
draw_object(image, 275, 175)
draw_object(image, 300, 200)
draw_object(image, 100, 350)
# calculate v-disparity
vhist_vis = np.zeros((IMAGE_HEIGHT, max_disp), np.float)
for i in range(IMAGE_HEIGHT):
vhist_vis[i, ...] = cv2.calcHist(images=[image[i, ...]], channels=[0], mask=None, histSize=[max_disp],
ranges=[0, max_disp]).flatten() / float(IMAGE_HEIGHT)
vhist_vis = np.array(vhist_vis * 255, np.uint8)
vblack_mask = vhist_vis < 5
vhist_vis = cv2.applyColorMap(vhist_vis, cv2.COLORMAP_JET)
vhist_vis[vblack_mask] = 0
# calculate u-disparity
uhist_vis = np.zeros((max_disp, IMAGE_WIDTH), np.float)
for i in range(IMAGE_WIDTH):
uhist_vis[..., i] = cv2.calcHist(images=[image[..., i]], channels=[0], mask=None, histSize=[max_disp],
ranges=[0, max_disp]).flatten() / float(IMAGE_WIDTH)
uhist_vis = np.array(uhist_vis * 255, np.uint8)
ublack_mask = uhist_vis < 5
uhist_vis = cv2.applyColorMap(uhist_vis, cv2.COLORMAP_JET)
uhist_vis[ublack_mask] = 0
image = cv2.applyColorMap(image, cv2.COLORMAP_JET)
cv2.imshow('image', image)
cv2.imshow('vhist_vis', vhist_vis)
cv2.imshow('uhist_vis', uhist_vis)
cv2.imwrite('disparity_image.png', image)
cv2.imwrite('v-disparity.png', vhist_vis)
cv2.imwrite('u-disparity.png', uhist_vis)
if chr(cv2.waitKey(0)&255) == 'q':
break
C++ 中的工作实现:
#include <iostream>
#include <stdlib.h>
#include <ctime>
#include <opencv2/opencv.hpp>
using namespace std;
void draw_object(cv::Mat image, unsigned int x, unsigned int y, unsigned int width=50, unsigned int height=100)
{
image(cv::Range(y-height, y), cv::Range(x-width/2, x+width/2)) = image.at<unsigned char>(y, x);
}
int main()
{
unsigned int IMAGE_HEIGHT = 600;
unsigned int IMAGE_WIDTH = 800;
unsigned int MAX_DISP = 250;
unsigned int CYCLE = 0;
//setenv("QT_GRAPHICSSYSTEM", "native", 1);
// === PREPERATIONS ==
cv::Mat image = cv::Mat::zeros(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8U);
cv::Mat uhist = cv::Mat::zeros(IMAGE_HEIGHT, MAX_DISP, CV_32F);
cv::Mat vhist = cv::Mat::zeros(MAX_DISP, IMAGE_WIDTH, CV_32F);
cv::Mat tmpImageMat, tmpHistMat;
float value_ranges[] = {(float)0, (float)MAX_DISP};
const float* hist_ranges[] = {value_ranges};
int channels[] = {0};
int histSize[] = {MAX_DISP};
struct timespec start, finish;
double elapsed;
while(1)
{
CYCLE++;
// === CLEANUP ==
image = cv::Mat::zeros(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8U);
uhist = cv::Mat::zeros(IMAGE_HEIGHT, MAX_DISP, CV_32F);
vhist = cv::Mat::zeros(MAX_DISP, IMAGE_WIDTH, CV_32F);
// === CREATE FAKE DISPARITY WITH OBJECTS ===
for(int i = 0; i < IMAGE_HEIGHT; i++)
image.row(i) = ((float)i / IMAGE_HEIGHT * MAX_DISP);
draw_object(image, 200, 500);
draw_object(image, 525 + CYCLE%100, 275);
draw_object(image, 500, 300 + CYCLE%100);
clock_gettime(CLOCK_MONOTONIC, &start);
// === CALCULATE V-HIST ===
for(int i = 0; i < IMAGE_HEIGHT; i++)
{
tmpImageMat = image.row(i);
vhist.row(i).copyTo(tmpHistMat);
cv::calcHist(&tmpImageMat, 1, channels, cv::Mat(), tmpHistMat, 1, histSize, hist_ranges, true, false);
vhist.row(i) = tmpHistMat.t() / (float) IMAGE_HEIGHT;
}
clock_gettime(CLOCK_MONOTONIC, &finish);
elapsed = (finish.tv_sec - start.tv_sec);
elapsed += (finish.tv_nsec - start.tv_nsec) * 1e-9;
cout << "V-HIST-TIME: " << elapsed << endl;
clock_gettime(CLOCK_MONOTONIC, &start);
// === CALCULATE U-HIST ===
for(int i = 0; i < IMAGE_WIDTH; i++)
{
tmpImageMat = image.col(i);
uhist.col(i).copyTo(tmpHistMat);
cv::calcHist(&tmpImageMat, 1, channels, cv::Mat(), tmpHistMat, 1, histSize, hist_ranges, true, false);
uhist.col(i) = tmpHistMat / (float) IMAGE_WIDTH;
}
clock_gettime(CLOCK_MONOTONIC, &finish);
elapsed = (finish.tv_sec - start.tv_sec);
elapsed += (finish.tv_nsec - start.tv_nsec) * 1e-9;
cout << "U-HIST-TIME: " << elapsed << endl;
// === PREPARE AND SHOW RESULTS ===
uhist.convertTo(uhist, CV_8U, 255);
cv::applyColorMap(uhist, uhist, cv::COLORMAP_JET);
vhist.convertTo(vhist, CV_8U, 255);
cv::applyColorMap(vhist, vhist, cv::COLORMAP_JET);
cv::imshow("image", image);
cv::imshow("uhist", uhist);
cv::imshow("vhist", vhist);
if ((cv::waitKey(1)&255) == 'q')
break;
}
return 0;
}
图 1:v-disparity
图 2:假视差矩阵
图3:u-disparity
- 编辑:
- u 和 v 差异的正确名称以及 c++ 示例中的单独时间测量
- 小错字
今天我有机会重新调查这个问题。记住 Mat
结构的 OpenCV 基础知识 (1) 以及仅一次计算需要大量时间的事实,我找到了解决方案。
在 OpenCV 中,图像的每一行都可以通过行指针到达。对于迭代列(在 u 差异计算中完成)我怀疑,OpenCV 需要解析每个行指针 + 列偏移量以构建直方图。
以某种方式更改代码,使 OpenCV 能够使用行指针,解决了我的问题。
| old code [s] | changed [s]
------------+--------------+-------------
V-HIST-TIME | 0.00351909 | 0.00334152
U-HIST-TIME | 0.600039 | 0.00449285
所以对于u-hist-loop,我转置图像并在循环后反转操作。现在可以通过行指针完成计算的行方式访问。
更改代码行:
// === CALCULATE U-HIST ===
image = image.t();
for(int i = 0; i < IMAGE_WIDTH; i++)
{
tmpImageMat = image.row(i);
uhist.col(i).copyTo(tmpHistMat);
cv::calcHist(&tmpImageMat, 1, channels, cv::Mat(), tmpHistMat, 1, histSize, hist_ranges, true, false);
uhist.col(i) = tmpHistMat / (float) IMAGE_WIDTH;
}
image = image.t();
我的第二个问题终于生效了,runtime-issue不属于循环。小于 5 毫秒的时间(目前)已经足够快了。
非常好的代码,非常说明性。它帮助我理解了 u-disparity。但是,您的 C/C++ 代码已损坏。我用这段代码修复了他:
cv::Mat uhist = cv::Mat::zeros(MAX_DISP, IMAGE_WIDTH, CV_32F);
cv::Mat vhist = cv::Mat::zeros(IMAGE_WIDTH, MAX_DISP, CV_32F);
基于被动立体相机系统的视差矩阵,我需要使用 OpenCV 计算用于障碍物检测的 v 视差表示。
一个可行的实现不是问题所在。问题是要快...
(一)v-Disparity 参考:Labayrade, R. 和 Aubert, D. 和 Tarel, J.P。通过 v 视差表示在非平坦道路几何体上进行立体视觉实时障碍物检测
基本简而言之,在视差矩阵的列上得到v视差(图1), is to analyze the rows of the disparity-matrix (figure 2) an represent the result as a histogram for each row over the disparity values. u-disparity (figure 3)是一样的。 (所有数字均为假色。)
我已经在 Python 和 C++ 中实现了 "same"。 Python 中的速度是可以接受的,但在 C++ 中,我得到 u 和 v 差异的时间大约为半秒(0.5 秒)。
(1. edit:由于是单独的时间测量,只有u直方图的计算比较耗时...)
这让我想到以下问题:
是否可以避免直方图逐行计算的循环?是否有一个 "trick" 可以通过从 OpenCV 调用
calcHist
-Function 来完成?也许与尺寸有关?在 C++ 中是否只是编码错误并且运行时问题与用于计算的循环无关?
谢谢大家
Python 中的工作实施:
#!/usr/bin/env python2
#-*- coding: utf-8 -*-
#
# THIS SOURCE-CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED. IN NO EVENT WILL THE AUTHOR BE HELD LIABLE FOR ANY DAMAGES ARISING FROM
# THE USE OF THIS SOURCE-CODE. USE AT YOUR OWN RISK.
import cv2
import numpy as np
import time
def draw_object(image, x, y, width=50, height=100):
color = image[y, x]
image[y-height:y, x-width//2:x+width//2] = color
IMAGE_HEIGHT = 600
IMAGE_WIDTH = 800
while True:
max_disp = 200
# create fake disparity
image = np.zeros((IMAGE_HEIGHT, IMAGE_WIDTH), np.uint8)
for c in range(IMAGE_HEIGHT)[::-1]:
image[c, ...] = int(float(c) / IMAGE_HEIGHT * max_disp)
draw_object(image, 275, 175)
draw_object(image, 300, 200)
draw_object(image, 100, 350)
# calculate v-disparity
vhist_vis = np.zeros((IMAGE_HEIGHT, max_disp), np.float)
for i in range(IMAGE_HEIGHT):
vhist_vis[i, ...] = cv2.calcHist(images=[image[i, ...]], channels=[0], mask=None, histSize=[max_disp],
ranges=[0, max_disp]).flatten() / float(IMAGE_HEIGHT)
vhist_vis = np.array(vhist_vis * 255, np.uint8)
vblack_mask = vhist_vis < 5
vhist_vis = cv2.applyColorMap(vhist_vis, cv2.COLORMAP_JET)
vhist_vis[vblack_mask] = 0
# calculate u-disparity
uhist_vis = np.zeros((max_disp, IMAGE_WIDTH), np.float)
for i in range(IMAGE_WIDTH):
uhist_vis[..., i] = cv2.calcHist(images=[image[..., i]], channels=[0], mask=None, histSize=[max_disp],
ranges=[0, max_disp]).flatten() / float(IMAGE_WIDTH)
uhist_vis = np.array(uhist_vis * 255, np.uint8)
ublack_mask = uhist_vis < 5
uhist_vis = cv2.applyColorMap(uhist_vis, cv2.COLORMAP_JET)
uhist_vis[ublack_mask] = 0
image = cv2.applyColorMap(image, cv2.COLORMAP_JET)
cv2.imshow('image', image)
cv2.imshow('vhist_vis', vhist_vis)
cv2.imshow('uhist_vis', uhist_vis)
cv2.imwrite('disparity_image.png', image)
cv2.imwrite('v-disparity.png', vhist_vis)
cv2.imwrite('u-disparity.png', uhist_vis)
if chr(cv2.waitKey(0)&255) == 'q':
break
C++ 中的工作实现:
#include <iostream>
#include <stdlib.h>
#include <ctime>
#include <opencv2/opencv.hpp>
using namespace std;
void draw_object(cv::Mat image, unsigned int x, unsigned int y, unsigned int width=50, unsigned int height=100)
{
image(cv::Range(y-height, y), cv::Range(x-width/2, x+width/2)) = image.at<unsigned char>(y, x);
}
int main()
{
unsigned int IMAGE_HEIGHT = 600;
unsigned int IMAGE_WIDTH = 800;
unsigned int MAX_DISP = 250;
unsigned int CYCLE = 0;
//setenv("QT_GRAPHICSSYSTEM", "native", 1);
// === PREPERATIONS ==
cv::Mat image = cv::Mat::zeros(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8U);
cv::Mat uhist = cv::Mat::zeros(IMAGE_HEIGHT, MAX_DISP, CV_32F);
cv::Mat vhist = cv::Mat::zeros(MAX_DISP, IMAGE_WIDTH, CV_32F);
cv::Mat tmpImageMat, tmpHistMat;
float value_ranges[] = {(float)0, (float)MAX_DISP};
const float* hist_ranges[] = {value_ranges};
int channels[] = {0};
int histSize[] = {MAX_DISP};
struct timespec start, finish;
double elapsed;
while(1)
{
CYCLE++;
// === CLEANUP ==
image = cv::Mat::zeros(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8U);
uhist = cv::Mat::zeros(IMAGE_HEIGHT, MAX_DISP, CV_32F);
vhist = cv::Mat::zeros(MAX_DISP, IMAGE_WIDTH, CV_32F);
// === CREATE FAKE DISPARITY WITH OBJECTS ===
for(int i = 0; i < IMAGE_HEIGHT; i++)
image.row(i) = ((float)i / IMAGE_HEIGHT * MAX_DISP);
draw_object(image, 200, 500);
draw_object(image, 525 + CYCLE%100, 275);
draw_object(image, 500, 300 + CYCLE%100);
clock_gettime(CLOCK_MONOTONIC, &start);
// === CALCULATE V-HIST ===
for(int i = 0; i < IMAGE_HEIGHT; i++)
{
tmpImageMat = image.row(i);
vhist.row(i).copyTo(tmpHistMat);
cv::calcHist(&tmpImageMat, 1, channels, cv::Mat(), tmpHistMat, 1, histSize, hist_ranges, true, false);
vhist.row(i) = tmpHistMat.t() / (float) IMAGE_HEIGHT;
}
clock_gettime(CLOCK_MONOTONIC, &finish);
elapsed = (finish.tv_sec - start.tv_sec);
elapsed += (finish.tv_nsec - start.tv_nsec) * 1e-9;
cout << "V-HIST-TIME: " << elapsed << endl;
clock_gettime(CLOCK_MONOTONIC, &start);
// === CALCULATE U-HIST ===
for(int i = 0; i < IMAGE_WIDTH; i++)
{
tmpImageMat = image.col(i);
uhist.col(i).copyTo(tmpHistMat);
cv::calcHist(&tmpImageMat, 1, channels, cv::Mat(), tmpHistMat, 1, histSize, hist_ranges, true, false);
uhist.col(i) = tmpHistMat / (float) IMAGE_WIDTH;
}
clock_gettime(CLOCK_MONOTONIC, &finish);
elapsed = (finish.tv_sec - start.tv_sec);
elapsed += (finish.tv_nsec - start.tv_nsec) * 1e-9;
cout << "U-HIST-TIME: " << elapsed << endl;
// === PREPARE AND SHOW RESULTS ===
uhist.convertTo(uhist, CV_8U, 255);
cv::applyColorMap(uhist, uhist, cv::COLORMAP_JET);
vhist.convertTo(vhist, CV_8U, 255);
cv::applyColorMap(vhist, vhist, cv::COLORMAP_JET);
cv::imshow("image", image);
cv::imshow("uhist", uhist);
cv::imshow("vhist", vhist);
if ((cv::waitKey(1)&255) == 'q')
break;
}
return 0;
}
- 编辑:
- u 和 v 差异的正确名称以及 c++ 示例中的单独时间测量
- 小错字
今天我有机会重新调查这个问题。记住 Mat
结构的 OpenCV 基础知识 (1) 以及仅一次计算需要大量时间的事实,我找到了解决方案。
在 OpenCV 中,图像的每一行都可以通过行指针到达。对于迭代列(在 u 差异计算中完成)我怀疑,OpenCV 需要解析每个行指针 + 列偏移量以构建直方图。
以某种方式更改代码,使 OpenCV 能够使用行指针,解决了我的问题。
| old code [s] | changed [s]
------------+--------------+-------------
V-HIST-TIME | 0.00351909 | 0.00334152
U-HIST-TIME | 0.600039 | 0.00449285
所以对于u-hist-loop,我转置图像并在循环后反转操作。现在可以通过行指针完成计算的行方式访问。
更改代码行:
// === CALCULATE U-HIST ===
image = image.t();
for(int i = 0; i < IMAGE_WIDTH; i++)
{
tmpImageMat = image.row(i);
uhist.col(i).copyTo(tmpHistMat);
cv::calcHist(&tmpImageMat, 1, channels, cv::Mat(), tmpHistMat, 1, histSize, hist_ranges, true, false);
uhist.col(i) = tmpHistMat / (float) IMAGE_WIDTH;
}
image = image.t();
我的第二个问题终于生效了,runtime-issue不属于循环。小于 5 毫秒的时间(目前)已经足够快了。
非常好的代码,非常说明性。它帮助我理解了 u-disparity。但是,您的 C/C++ 代码已损坏。我用这段代码修复了他:
cv::Mat uhist = cv::Mat::zeros(MAX_DISP, IMAGE_WIDTH, CV_32F);
cv::Mat vhist = cv::Mat::zeros(IMAGE_WIDTH, MAX_DISP, CV_32F);