访问随机图像像素和最多一次的快速方法

Ami*_*ekh 2 c++ random opencv image-processing

我正在学习 OpenCV (C++),作为一个简单的实践,我设计了一个简单的效果,使一些图像像素变黑或变白。我希望每个像素最多编辑一次;所以我将所有像素的地址添加到向量中。但这让我的代码变得很慢;专门用于大图像或大量效果。这是我的代码:

void effect1(Mat& img, float amount)    // 100 ? amount ? 0
{
    vector<uchar*> addresses;
    int channels = img.channels();
    uchar* lastAddress = img.ptr<uchar>(0) + img.total() * channels;
    for (uchar* i = img.ptr<uchar>(0); i < lastAddress; i += channels) addresses.push_back(i);   //Fast Enough
    size_t count = img.total() * amount / 100 / 2;
    for (size_t i = 0; i < count; i++)
    {
        size_t addressIndex = xor128() % addresses.size();   //Fast Enough, xor128() is a fast random number generator
        for (size_t j = 0; j < channels; j++)
        {
            *(addresses[addressIndex] + j) = 255;
        }   //Fast Enough
        addresses.erase(addresses.begin() + addressIndex);    // MAKES CODE EXTREMELY SLOW
    }
    for (size_t i = 0; i < count; i++)
    {
        size_t addressIndex = xor128() % addresses.size();   //Fast Enough, xor128() is a fast random number generator
        for (size_t j = 0; j < channels; j++)
        {
            *(addresses[addressIndex] + j) = 0;
        }   //Fast Enough
        addresses.erase(addresses.begin() + addressIndex);    // MAKES CODE EXTREMELY SLOW
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为擦除项目后重新排列矢量项目会使我的代码变慢(如果我删除addresses.erase,代码将运行得很快)。

是否有任何快速方法可以最多一次从集合(或数字范围)中选择每个随机项目?

另外:我很确定这种效果已经存在。有谁知道它的名字?

Joh*_*eau 5

这个答案假设您有一个随机位生成器函数,因为std::random_shuffle需要它。我不知道如何xor128工作,所以我将使用<random>库的功能。

如果我们有一个群体N项目,我们要选择规模的团体j,并k随机从人口没有重叠,我们可以写下每一个项目的索引卡片上,洗牌,画j卡,然后画k卡。剩下的一切都被丢弃了。我们可以通过<random>库来实现这一点。关于如何合并像您使用xor128.

这假设random_device在您的系统上不起作用(许多编译器以一种总是返回相同序列的方式实现它),所以我们用当前时间为随机生成器播种,就像srand我们母亲过去制作的老式一样。

未经测试,因为我不知道如何使用 OpenCV。任何有这方面经验的人请酌情编辑。

#include <ctime>     // for std::time
#include <numeric>   // for std::iota
#include <random>
#include <vector>

void effect1(Mat& img, float amount, std::mt19937 g)    // 0.0 ? amount ? 1.00
{
    std::vector<cv::Size> ind(img.total());
    std::iota(ind.begin(), ind.end(), 0);   // fills with 0, 1, 2, ...
    std::random_shuffle(ind.begin(), ind.end(), g);
    cv::Size count = img.total() * amount;

    auto white = get_white<Mat>();  // template function to return this matrix' concept of white
                                    // could easily replace with cv::Vec3d(255,255,255) 
                                    // if all your matrices are 3 channel?
    auto black = get_black<Mat>();  // same but... opposite

    auto end = ind.begin() + count;
    for (auto it = ind.begin(), it != end; ++it)
    {
        img.at(*it) = white;
    }
    end = (ind.begin() + 2 * count) > ind.end() ?
               ind.end() : 
               ind.begin() + 2 * count;
    for (auto it = ind.begin() + count; it != end; ++it)
    {
        img.at(*it) = black;
    }
}

int main()
{
    std::mt19937 g(std::time(nullptr)); // you normally see this seeded with random_device
                                        // but that's broken on some implementations
                                        // adjust as necessary for your needs
    cv::Mat mat = ... // make your cv objects

    effect1(mat, 0.1, g);

    // display it here

}
Run Code Online (Sandbox Code Playgroud)

另一种方法

假设每个像素都有随机切换到白色、切换到黑色或保持不变的概率,而不是洗牌索引和从一副牌中抽牌。如果您的数量是 0.4,则选择 0.0 和 1.0 之间的随机数,0.0 和 0.4 之间的任何结果将像素翻转为黑色,0.4 和 0.8 之间的结果将其翻转为白色,否则保持不变。

通用算法:

given probability of flipping -> f
for each pixel in image -> p:
    get next random float([0.0, 1.0)) -> r
    if r < f
        then p <- BLACK
    else if r < 2*f
        then p <- WHITE
Run Code Online (Sandbox Code Playgroud)

你不会每次都得到相同数量的白/黑像素,但这是随机的!无论如何,我们正在为洗牌算法为每个像素生成一个随机数。除非我弄错了,否则这具有相同的复杂性。