【问题标题】:How to find the pixel value that corresponds to a specific number of pixels?如何找到与特定像素数对应的像素值?
【发布时间】:2016-02-23 03:34:29
【问题描述】:

假设我在 OpenCV 中有一个灰度图像。

我想找到一个值,使图像中5% 像素的值大于它。

我可以遍历像素并找到具有相同值的像素数,然后从结果中找到像素的 %5 高于我的值的值,但我正在寻找一种更快的方法来做到这一点。 OpenCV中有没有这样的技术?

我认为直方图会有所帮助,但我不确定如何使用它。

【问题讨论】:

标签: c++ opencv image-processing opencv3.0


【解决方案1】:

你需要:

  1. 计算像素值的累积直方图
  2. 查找值大于总像素数 95% (100 - 5) 的 bin。

给定一个均匀随机生成的图像,您会得到如下直方图:

和累积直方图一样(你需要找到第一个值超过蓝线的bin):

然后你需要找到合适的垃圾箱。您可以使用std::lower_bound 函数找到正确的值,并使用std::distance 找到相应的 bin 编号(也就是您要查找的值)。 (请注意,使用lower_bound,您将找到其值为greater or equal 的元素到给定值。您可以使用upper_bound 找到其值严格大于然后给定值的元素值)

在这种情况下,它的结果是 242,这对于从 0 到 255 的均匀分布是有意义的,因为 255*0.95 = 242.25

查看完整代码:

#include <opencv2\opencv.hpp>
#include <vector>
#include <algorithm>

using namespace std;
using namespace cv;

void drawHist(const vector<int>& data, Mat3b& dst, int binSize = 3, int height = 0, int ref_value = -1)
{
    int max_value = *max_element(data.begin(), data.end());
    int rows = 0;
    int cols = 0;
    float scale = 1;
    if (height == 0) {
        rows = max_value + 10;
    }
    else {
        rows = height; 
        scale = float(height) / (max_value + 10);
    }
    cols = data.size() * binSize;
    dst = Mat3b(rows, cols, Vec3b(0, 0, 0));
    for (int i = 0; i < data.size(); ++i)
    {
        int h = rows - int(scale * data[i]);
        rectangle(dst, Point(i*binSize, h), Point((i + 1)*binSize - 1, rows), (i % 2) ? Scalar(0, 100, 255) : Scalar(0, 0, 255), CV_FILLED);
    }

    if (ref_value >= 0)
    {
        int h = rows - int(scale * ref_value);
        line(dst, Point(0, h), Point(cols, h), Scalar(255,0,0));
    }
}

int main()
{

    Mat1b src(100, 100);
    randu(src, Scalar(0), Scalar(255));

    int percent = 5; // percent % of pixel values are above a val
    int val;  // I need to find this value


    int n = src.rows * src.cols; // Total number of pixels
    int th = cvRound((100 - percent) / 100.f * n);  // Number of pixels below val

    // Histogram
    vector<int> hist(256, 0);
    for (int r = 0; r < src.rows; ++r) {
        for (int c = 0; c < src.cols; ++c) {
            hist[src(r, c)]++;
        }
    }

    // Cumulative histogram
    vector<int> cum = hist;
    for (int i = 1; i < hist.size(); ++i) {
        cum[i] = cum[i - 1] + hist[i];
    }

    // lower_bound returns an iterator pointing to the first element
    // that is not less than (i.e. greater or equal to) th.
    val =  distance(cum.begin(), lower_bound(cum.begin(), cum.end(), th));


    // Plot histograms
    Mat3b plotHist, plotCum;
    drawHist(hist, plotHist, 3, 300);
    drawHist(cum, plotCum, 3, 300, *lower_bound(cum.begin(), cum.end(), th));

    cout << "Value: " << val;

    imshow("Hist", plotHist);
    imshow("Cum", plotCum);
    waitKey();

    return 0;
}

注意

  • 直方图绘制功能是我发布here之前版本的升级
  • 您可以使用calcHist 来计算直方图,但我个人觉得上述方法更容易用于一维直方图。

【讨论】:

  • 我们可以使用Opencv直方图函数来加快进程吗?如果是,我们该怎么做?
  • @mans 您可以使用 calcHist 来计算直方图。我给你的链接中解释了如何做到这一点。但是我认为它不会比简单的 for 循环快得多。当然,我们可以使用指针并将 src 图像重塑为单行,但在这里我更喜欢可读性而不是速度。如果你有什么问题,请给我回电话。但是所有其他步骤都没有内置 OpenCV,所以你仍然需要按照我的方式进行操作。因此,如果您想获得 几毫秒,则可以对该代码进行一些优化。 calcHist 可能不会更快,但您需要进行分析。
  • @mans 但由于这个问题与性能无关(这实际上对这段代码也很好),所以我更喜欢让它保持可读性。告诉我。
【解决方案2】:

1) 确定图片的高度和宽度,hw

2) 确定总像素数的5%是多少(X)...

X = int(h * w * 0.05)

3) 从直方图中最亮的 bin 开始。设置总计T = 0。

4) 将此 bin 中的像素数添加到您的总数 T。如果T 大于X,你就完成了,你想要的值是当前直方图 bin 范围的下限。

3) 移动到直方图中下一个较暗的 bin。转到 4。

【讨论】:

    猜你喜欢
    • 2020-11-24
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 2020-11-15
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多