我已经实现了一个用于检查完美碰撞的像素掩码类。我正在使用SFML,所以实现非常简单:
循环遍历图像的每个像素,并根据其透明度值确定其是真还是假。这是我使用的代码:
// Create an Image from the given texture
sf::Image image(texture.copyToImage());
// measure the time this function takes
sf::Clock clock;
sf::Time time = sf::Time::Zero;
clock.restart();
// Reserve memory for the pixelMask vector to avoid repeating allocation
pixelMask.reserve(image.getSize().x);
// Loop through every pixel of the texture
for (unsigned int i = 0; i < image.getSize().x; i++)
{
// Create the mask for one line
std::vector<bool> tempMask;
// Reserve memory for the pixelMask vector to avoid repeating allocation
tempMask.reserve(image.getSize().y);
for (unsigned int j = 0; j < image.getSize().y; j++)
{
// If the pixel is not transparrent
if (image.getPixel(i, j).a > 0)
// Some part of the texture is there --> push back true
tempMask.push_back(true);
else
// The user can't see this part of the texture --> push back false
tempMask.push_back(false);
}
pixelMask.push_back(tempMask);
}
time = clock.restart();
std::cout << std::endl << "The creation of the pixel mask took: " << time.asMicroseconds() << " microseconds (" << time.asSeconds() << ")";
我使用了sf::Clock
的一个实例来确保时间。
我的问题是此功能需要较长时间(例如15秒)才能拍摄较大的图像(例如1280x720)。有趣的是,仅在调试模式下。编译发布版本时,相同的纹理/图像只需0.1秒或更短时间。
我尝试使用resize()方法减少内存分配,但它没有太大变化。我知道循环近100万像素的速度很慢,但它不应该慢15秒吗?
由于我想在调试模式下测试我的代码(出于显而易见的原因)并且我不想等待5分钟直到创建了所有像素掩码,我所寻找的基本上是一种方式:
感谢您的帮助!
答案 0 :(得分:3)
优化调试
优化调试版本通常是一个非常适得其反的想法。它甚至可以让您优化调试,不仅使维护代码更加困难,而且甚至可能减慢发布版本。一般来说,调试版本的运行速度要慢得多。即使使用我编写的最平坦的C代码,这对于优化器除了合理的寄存器分配和指令选择之外没有太大作用,因此调试构建需要花费20倍的时间才能完成操作。这只是接受而不是改变的东西。
那就是说,我有时可以理解这样做的诱惑。有时您只想为软件中的其他操作调试某一部分代码需要很长时间,这需要您等待很长时间才能获得您感兴趣的代码。我发现在这些情况下,如果可以的话,将调试模式输入大小与发布模式分开是有帮助的(例如:使调试模式仅适用于原始大小的1/10的输入)。这确实会导致发布和调试之间的差异为负,但积极有时会超过生产率角度的负面影响。另一个策略是在发布版本中构建部分代码,并调试您感兴趣的部分,例如在发布版本中针对宿主应用程序构建调试插件。
接近你自己的危险
除此之外,如果你真的想让你的调试版本运行得更快并接受所有相关的风险,那么主要的方法就是减少你的编译器优化工作量。这将是更平坦的代码,通常使用更简单的旧数据类型,更少的函数调用等等。
首先,您可能会花费大量时间在调试模式断言上以确保安全。查看已检查的迭代器以及如何禁用它们: https://msdn.microsoft.com/en-us/library/aa985965.aspx
对于您的情况,您可以轻松地将嵌套循环展平为单个循环。不需要为每条扫描线创建具有单独容器的这些像素掩码,因为您始终可以使用一些基本算法(y*image_width
或y*image_stride
)获取扫描线数据。所以最初我会把循环弄平。这对于发布模式甚至可能有所帮助。我不知道SFML API所以我将用伪代码来说明。
const int num_pixels = image.w * image.h;
vector<bool> pixelMask(num_pixels);
for (int j=0; j < num_pixels; ++j)
pixelMask[j] = image.pixelAlpha(j) > 0;
这已经可以帮到很多了。希望SFML允许您使用单个索引访问像素,而无需指定列和行(x
和y
)。如果你想更进一步,可能有助于从SFML中抓取指向像素数组的指针(也希望可能)并使用它:
vector<bool> pixelMask(image.w * image.h);
const unsigned int* pixels = image.getPixels();
for (int j=0; j < num_pixels; ++j)
{
// Assuming 32-bit pixels (should probably use uint32_t).
// Note that no right shift is necessary when you just want
// to check for non-zero values.
const unsigned int alpha = pixels[j] & 0xff000000;
pixelMask[j] = alpha > 0;
}
同样vector<bool>
将每个布尔值存储为单个位。这节省了内存,但转换为更多随机访问指令。有时你甚至可以通过使用更多内存来获得加速。我会仔细测试发布和调试时间,但你可以试试这个:
vector<char> pixelMask(image.w * image.h);
const unsigned int* pixels = image.getPixels();
char* pixelUsed = &pixelMask[0];
for (int j=0; j < num_pixels; ++j)
{
const unsigned int alpha = pixels[j] & 0xff000000;
pixelUsed[j] = alpha > 0;
}
答案 1 :(得分:1)
如果使用costants,循环速度会更快: 1. for(unsigned int i = 0; i&lt; image.getSize()。x; i ++)在循环之前得到这个image.getSize()。 2.从循环中获取一行的掩码并重用它。线条与我假设的长度相同。 std :: vector tempMask; 这会加快你的速度。 请注意,用于调试的编译提供了更多不同的机器代码。