【问题标题】:How can I optimize this image "edge detection" algorithm?如何优化此图像“边缘检测”算法?
【发布时间】:2014-07-19 14:21:45
【问题描述】:

我有一个函数,给定一个透明背景的图像和一个未知对象,找到对象的顶部、左侧、右侧和底部边界。目的是让我可以简单地在对象的边界周围画一个框。我不是要检测物体的实际边缘——只是最上面、最下面等等。

我的函数运行良好,但速度很慢,因为它扫描图像中的每一个像素。

我的问题是:有没有一种更快、更有效的方法来检测图像中最上方、最左侧、最右侧和最底部的非透明像素,使用现有的 PHP/GD 功能?

有一个影响选项的问题:图像中的对象可能有透明部分。例如,如果它是未填充形状的图像。

public static function getObjectBoundaries($image)
{
    // this code looks for the first non white/transparent pixel
    // from the top, left, right and bottom

    $imageInfo = array();
    $imageInfo['width'] = imagesx($image);
    $imageInfo['height'] = imagesy($image);

    $imageInfo['topBoundary'] = $imageInfo['height'];
    $imageInfo['bottomBoundary'] = 0;
    $imageInfo['leftBoundary'] = $imageInfo['width'];
    $imageInfo['rightBoundary'] = 0;

    for ($x = 0; $x <= $imageInfo['width'] - 1; $x++) {
        for ($y = 0; $y <= $imageInfo['height'] - 1; $y++) {
            $pixelColor = imagecolorat($image, $x, $y);
            if ($pixelColor != 2130706432) {        // if not white/transparent
                $imageInfo['topBoundary'] = min($y, $imageInfo['topBoundary']);
                $imageInfo['bottomBoundary'] = max($y, $imageInfo['bottomBoundary']);
                $imageInfo['leftBoundary'] = min($x, $imageInfo['leftBoundary']);
                $imageInfo['rightBoundary'] = max($x, $imageInfo['rightBoundary']);
            }
        }
    }

    return $imageInfo;
}

【问题讨论】:

  • 一些非常有趣的答案值得进行适当的测试。我会在第二天左右做一些基准测试,并接受表现最好的。

标签: php image-processing gd edge-detection


【解决方案1】:

PHP 中的函数调用很昂贵。每个像素调用 imagecolorat() 绝对会破坏性能。 PHP 中的高效编码意味着找到一个可以以某种方式完成工作的内置函数。以下代码使用调色板 GD 函数。乍一看,它可能不直观,但逻辑实际上非常简单:代码不断地一次复制图像一行像素,直到它注意到它需要不止一种颜色来表示它们。

function getObjectBoundaries2($image) {
    $width = imagesx($image);
    $height = imagesy($image);

    // create a one-pixel high image that uses a PALETTE
    $line = imagecreate($width, 1);
    for($y = 0; $y < $height; $y++) {
        // copy a row of pixels into $line
        imagecopy($line, $image, 0, 0, 0, $y, $width, 1);

        // count the number of colors in $line
        // if it's one, then assume it's the transparent color
        $count = imagecolorstotal($line);
        if($count > 1) {
            // okay, $line has employed more than one color so something's there
            // look at the first color in the palette to ensure that our initial 
            // assumption was correct 
            $firstColor = imagecolorsforindex($line, 0);
            if($firstColor['alpha'] == 127) {
                $top = $y;
            } else {
                // it was not--the first color encountered was opaque
                $top = 0;
            }
            break;
        }
    }

    if(!isset($top)) {
        // image is completely empty
        return array('width' => $width, 'height' => $height);
    }

    // do the same thing from the bottom
    $line = imagecreate($width, 1);
    for($y = $height - 1; $y > $top; $y--) {
        imagecopy($line, $image, 0, 0, 0, $y, $width, 1);
        $count = imagecolorstotal($line);
        if($count > 1) {
            $firstColor = imagecolorsforindex($line, 0);
            if($firstColor['alpha'] == 127) {
                $bottom = $y;
            } else {
                $bottom = $height - 1;
            }
            break;
        }
    }
    $nonTransparentHeight = $bottom - $top + 1;

    // scan from the left, ignoring top and bottom parts known to be transparent
    $line = imagecreate(1, $nonTransparentHeight);
    for($x = 0; $x < $width; $x++) {
        imagecopy($line, $image, 0, 0, $x, $top, 1, $nonTransparentHeight);
        $count = imagecolorstotal($line);
        if($count > 1) {
            $firstColor = imagecolorsforindex($line, 0);
            if($firstColor['alpha'] == 127) {
                $left = $x;
            } else {
                $left = 0;
            }
            break;
        }
    }

    // scan from the right
    $line = imagecreate(1, $nonTransparentHeight);
    for($x = $width - 1; $x > $left; $x--) {
        imagecopy($line, $image, 0, 0, $x, $top, 1, $nonTransparentHeight);
        $count = imagecolorstotal($line);
        if($count > 1) {
            $firstColor = imagecolorsforindex($line, 0);
            if($firstColor['alpha'] == 127) {
                $right = $x;
            } else {
                $right = $width - 1;
            }
            break;
        }
    }

    return array('width' => $width, 'height' => $height, 'topBoundary' => $top, 'bottomBoundary' => $bottom, 'leftBoundary' => $left, 'rightBoundary' => $right);
}

【讨论】:

  • 巧妙使用调色板功能!我会把这个想法留在我脑海中的某个地方;)
【解决方案2】:

我认为您可以一个接一个地测试 4 个面,一旦找到像素就停止。 对于顶部边界(未经测试的代码):

// false so we can test it's value
$bound_top = false;
// The 2 loops have 2 end conditions, if end of row/line, or pixel found
// Loop from top to bottom
for ($y = 0; $y < $img_height && $bound_top === false; $y++) {
    // Loop from left to right (right to left would work to)
    for ($x = 0; $x < $img_width && $bound_top === false; $x++) {
        if (imageColorAt($img, $x, $y) != 2130706432) {
            $bound_top = $y;
        }
    }
}

循环之后,如果$bound_top 仍然是false,不要费心检查其他方面,你检查了所有像素,图像是空的。如果没有,就对其他边做同样的事情。

【讨论】:

    【解决方案3】:

    并非每个像素都需要检查。下面的代码从左到右检查列得到leftBoundary,从右到左检查得到rightBoundary,从上到下检查行(不包括我们已经检查过的像素)得到topBoundary,bottomBoundary也是如此。

    function get_boundary($image)
    {
        $imageInfo = array();
        $imageInfo['width'] = imagesx($image);
        $imageInfo['height'] = imagesy($image);
    
        for ($x = 0; $x < $imageInfo['width']; $x++) {
            if (!is_box_empty($image, $x, 0, 1, $imageInfo['height'])) {
                $imageInfo['leftBoundary'] = $x;
                break;
            }
        }
    
        for ($x = $imageInfo['width']-1; $x >= 0; $x--) {
            if (!is_box_empty($image, $x, 0, 1, $imageInfo['height'])) {
                $imageInfo['rightBoundary'] = $x;
                break;
            }
        }
    
        for ($y = 0; $y < $imageInfo['height']; $y++) {
            if (!is_box_empty($image, $imageInfo['leftBoundary'], $y, $imageInfo['rightBoundary']-$imageInfo['leftBoundary']+1, 1)) {
                $imageInfo['topBoundary'] = $y;
                break;
            }
        }
    
        for ($y = $imageInfo['height']-1; $y >= 0; $y--) {
            if (!is_box_empty($image, $imageInfo['leftBoundary'], $y, $imageInfo['rightBoundary']-$imageInfo['leftBoundary']+1, 1)) {
                $imageInfo['bottomBoundary'] = $y;
                break;
            }
        }
    
        return $imageInfo;
    }
    
    function is_box_empty($image, $x, $y, $w, $h)
    {
        for ($i = $x; $i < $x+$w; $i++) {
            for ($j = $y; $j < $y+$h; $j++) {
                $pixelColor = imagecolorat($image, $i, $j);
                if ($pixelColor != 2130706432) {        // if not white/transparent
                    return false;
                }
            }
        }
    
        return true;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-03-30
      • 2013-11-19
      • 2010-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-07
      相关资源
      最近更新 更多