-
Notifications
You must be signed in to change notification settings - Fork 486
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
還有辦法再加速嗎? #54
Comments
可以参考这里 |
使用OpenCL後,sobel耗時占比約4%而已,這個github主要加速sobel應該沒太大用處? 還是ComputeResponseMap、Hysteresis與Linearlize這三段有辦法用裡面的東西加速 |
opencl效果这么明显应该是用到并行了。 |
所有算子fuse起来->>是指sobel、phase、magnitude一起計算嗎? |
是的,因为都是局部读一块进去算,所以可以合并一下 |
能提示下用halide如何實現嗎? |
可以参考halide github上的教程,讲的挺详细的 |
因为计算phase前总要计算gaussian和sobel,所以按照fuse的思路,我尝试把gaussian算子和sobel算子先算卷积,得到融合的卷积核,再用它对原图进行卷积。 Mat src = imread("Hydrangeas.jpg", CV_8UC1);
int g_k_size = 3;
Mat sobel_x, sobel_y, phase_;
Mat kernelX = (Mat_<float>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
Mat kernelY = (Mat_<float>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
Mat g_kernel = getGaussianKernel(g_k_size, 1, CV_32F);
Mat g_kernel_2d = g_kernel * g_kernel.t();
Mat fused_kernel;
Mat blur_;
src.convertTo(src, CV_32F);
GaussianBlur(kernelX, fused_kernel, Size(g_k_size, g_k_size), 0, 0, BORDER_REPLICATE);
filter2D(src, sobel_x, CV_32F, fused_kernel, Point(-1, -1), 0, BORDER_REPLICATE);
Mat cv_sobel_x, cv_sobel_y, cv_phase, cv_blur;
GaussianBlur(src, cv_blur, Size(g_k_size, g_k_size), 1, 0, BORDER_REPLICATE);
Sobel(cv_blur, cv_sobel_x, CV_32F, 1, 0, 3, 1.0, 0.0, BORDER_REPLICATE);
//Sobel(src, cv_sobel_y, CV_8U, 0, 1);
//phase(cv_sobel_x, cv_sobel_y, cv_phase, true);
for (int row = 0; row < src.rows; row++)
{
for (int col = 0; col < src.cols; col++)
{
float fused = *sobel_x.ptr<float>(row, col);
float cv = *cv_sobel_x.ptr<float>(row, col);
if (fused != cv)
{
cout << fused << " " << cv << endl;
}
}
} 理论上来说,卷积乘法是可以先算后面的,再算前面的,但是我这个结果和原结果总是有偏差,不知道为什么。 |
GaussianBlur(kernelX, fused_kernel, Size(g_k_size, g_k_size), 0, 0, BORDER_REPLICATE); 这一句有些不对,想得到fused_kernel直接乘起来就好。 |
直接乘起来是什么意思? |
把两个kernel逐元素相乘,用GaussianBlur在边界的地方有问题 |
逐元素相乘是指矩阵点乘吗? |
不是点乘,直接对应位置相乘; |
對應位置相乘不就是點乘嗎? 我改了一下@zhengli233的程式碼
還是有誤差 (max ~= 10),有那裡錯誤嗎?懇請大神賜教 (抱歉不知道怎麼貼程式碼
|
@DennisLiu-elogic 你好, 可以看到,如果按正常顺序计算卷积,我们需要
这里实际上对原图展开了2次,而如果先计算后面2个卷积核,虽然也有2次展开,但是其中一次是对卷积核做的展开,因此丢失了一部分数据。 |
哦,之前我理解的有问题,fusion说的不是把卷积核都合成一个,因为这样会越来越大,比如两个3x3的合起来就变成5x5,虽然方法不明,但肯定不是直接一个filter2D另一个。 |
@zhengli233 但根據版主開示"提高局部性",這種方法應該不適用於此情況,然後我的算法在邊緣上還是有一些誤差,我猜是opencv Sobel的kernel可能不是 -1, 0, 1, -2, 0, 2, -1, 0, 1 |
卷积应该不满足交换律,从感受野来看,7x7 3x3卷积两次一定会扩大,变成(1+3)*2+1=9*9。 |
google查了前幾筆資料,都說捲積是有結合率的(a * b) * c = a * (b * c) |
推导了下,还真满足结合律,不过扩大成9x9也是应该的,改了下一点误差都没有了: Mat matSrc = imread ("", IMREAD_GRAYSCALE);
// modification 1: double type, otherwise GaussianBlur will output CV_8U type
matSrc.convertTo(matSrc, CV_64F);
Mat matSobelKernelX = (Mat_<double> (3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1), matSobelFlip;
Mat matGaussianKernelX = getGaussianKernel (7, 1, CV_64F);//7x1 matrix
Mat matGaussianKennelY = matGaussianKernelX.t ();
Mat matGaussianKernel = matGaussianKernelX * matGaussianKennelY;// make 7x7 Gaussian
// modification 2: enlarge fused kernel
Mat matGaussianKernel9(9, 9, CV_64F, Scalar::all (0.0));
matGaussianKernel.copyTo(matGaussianKernel9(Rect(1, 1, 7, 7)));
flip(matSobelKernelX, matSobelFlip, -1);//x, y flip
Mat matFusedKernel, matResult1;
// modification 3: BORDER_CONSTANT, padding by 0.
// Padding sobel kernel happens to have the same effect.
filter2D (matGaussianKernel9, matFusedKernel, CV_64F, matSobelFlip , Point (-1, -1), 0, BORDER_CONSTANT);
//Result
filter2D (matSrc, matResult1, CV_64F, matFusedKernel, Point (-1, -1), 0.0, BORDER_REPLICATE);
//opencv api result
Mat cv_sobel_x, cv_blur;
// BORDER_REPLICATE twice make it different from fused kernel around border
GaussianBlur(matSrc, cv_blur, Size (7, 7), 1, 0, BORDER_REPLICATE);
Sobel (cv_blur, cv_sobel_x, CV_64F, 1, 0, 3, 1.0, 0.0, BORDER_REPLICATE);
Mat matDiff = cv::abs(matResult1 - cv_sobel_x);
// matDiff.convertTo (matDiff, CV_8U);
imshow ("Diff", matDiff > 1e-12);
// no difference at all except border (limits of precision: float is 1e-3, double is 1e-12)
// original case has small difference because it's not 9x9 kernel,
// which means border of fused kernel are set to 0
double dMax;
minMaxLoc (matDiff, 0, &dMax, 0, 0);
std::cout << "dmax: " << dMax << endl; |
@meiqua 好像确实是这么个道理,kernel不应该合起来。 第二段中你说的提高局部性是指节省了写内存的开销吗?在计算量没有减少的情况下,写内存的开销与计算比起来应该占比很小吧。
我跑了一下这个代码,并不是没有偏差啊, |
您这是在做pyrDown的融合吗?结果怎么样?有没有实质性的效果? |
@zhengli233 是吗,我跑的图片边界地方是4,imshow出来中间全黑,说明差异比1e-12还小。边界地方特别大也是有可能的,因为BORDER_REPLICATE两次肯定跟一次不一样 |
@meiqua 可以看到,偏差还是挺大的 : ( |
@xinsuinizhuan 不是在做pyrDown的融合,是高斯和sobel的融合 |
打算所有算子fuse起来->>是指sobel、phase、magnitude一起計算嗎? |
@xinsuinizhuan phase, magnitude不是卷积计算,所以也就没有算子,谈不上融合 |
@zhengli233 见笑了。只是现在的速度黑白图也只有90ms左右,达不到要求,在寻求加速。看到说融合后可以达到halcon的水平,您又在做类似融合的东西。 |
@zhengli233 图片贴一下我试试?我看有可能是1e-12的阈值太小了 |
|
@zhengli233 试过了,中间还是全黑呀。dMax确实有74.2986 |
@meiqua
它是怎么缩短运行时间的? |
对,opencv里面算高斯 sobel是用可分离卷积算的。 |
@meiqua
halide会通过它的方法帮我们实现locality和parallel的优化,但代价就是要使用它的语法将计算组成pipeline。 但说实话,这个代价还挺大的,因为它的资料感觉挺少的,即使是opencv中常用kernel的组合,网上都找不到现成的例子。我自己尝试了halide的官方的例子,连编译都通不过。。。 另外,opencv似乎想要实现一个叫mini-halide的东西,但还只是在draft阶段。 |
可以参考我的思路走一走,进行加速。使用SSE优化soble gaussblur等函数进行优化,这个加速也是很明显的,只是不太懂SSE。参考博客:https://www.cnblogs.com/Imageshop/ |
@zhengli233 |
最近简单实现了整个fusion的过程,大家有兴趣可以跑跑看。 |
@xinsuinizhuan |
好的。我有空测试一下您的方案。 |
Hi meiqua
統計了各段落與各層金字塔的耗時
使用OpenCL後已經讓OpenCV的函數耗時下降不少,關於加速ComputeResponseMap、Hysteresis與Linearlize這三段,不知道meiqua你還有沒有什麼想法?
目前實作過這個方法,ComputeResponseMap中Create 8個Mat,改為一次Create大張的Mat
(size = Size (src.cols * 8, src.rows)),response_maps[i] = BigSrc (Rect)指向大張的ROI,但實作後發現__m128i *map_data = response_maps[ori].ptr<__m128i>(); 這行似乎不正常,導致__m128i res1 = _mm_shuffle_epi8(lut[2 * ori + 0], lsb4_data[i]);出錯
The text was updated successfully, but these errors were encountered: