我正在学习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
}
}
我认为擦除项目后重新排列矢量项目是使我的代码运行缓慢的原因(如果删除addresss.erase,代码将快速运行。
是否有快速方法最多可以一次从集合(或数字范围)中选择每个随机项目?
也:我很确定这种效果已经存在。有人知道它的名字吗?
此答案假设您具有随机位生成器功能,因为std::random_shuffle
要求这样做。我不知道xor128
的工作方式,所以我将使用<random>
库的功能。
[如果我们有N
个商品,并且我们希望从该人口中随机选择大小为j
和k
的组,并且没有重叠,我们可以记下卡片上每个商品的索引,随机排列在甲板上,先抓j
卡,然后再抓k
卡。剩下的一切都将被丢弃。我们可以使用<random>
库来实现。有关如何合并自定义PRNG(如您使用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 }
另一种方法
不是从甲板上重新排列索引和抽签卡,而是假设每个像素都有切换为白色,切换为黑色或保持不变的随机概率。如果您的数量为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
您每次不会获得相同数量的白色/黑色像素,但这是随机的!无论如何,我们都会为改组算法为每个像素生成一个随机数。除非我弄错,否则这具有相同的复杂性。
也:我很确定这种效果已经存在。有人知道它的名字吗?
我也发布了一个简单的代码: