0

I'm trying to identify the width of a colored rectangle inside an image. For the image handling I'm using Imagick for PHP.

The idea was to convert the image into txt and search this txt with a regex for all pixels that have a specific color (#00FF00).

Here is an example of what that txt looks like (excerpt starting at pixel column 0 row 83):

0,83: (255,255,255,1)  #FFFFFF  graya(255,1)
1,83: (255,255,255,1)  #FFFFFF  graya(255,1)
2,83: (255,255,255,1)  #FFFFFF  graya(255,1)
3,83: (0,255,0,1)  #00FF00  graya(0,1)
4,83: (0,255,0,1)  #00FF00  graya(0,1)
5,83: (0,255,0,1)  #00FF00  graya(0,1)
6,83: (255,255,255,1)  #FFFFFF  graya(255,1)
7,83: (255,255,255,1)  #FFFFFF  graya(255,1)
8,83: (255,255,255,1)  #FFFFFF  graya(255,1)
0,84: (255,255,255,1)  #FFFFFF  graya(255,1)
1,84: (255,255,255,1)  #FFFFFF  graya(255,1)
2,84: (255,255,255,1)  #FFFFFF  graya(255,1)
3,84: (0,255,0,1)  #00FF00  graya(0,1)
4,84: (0,255,0,1)  #00FF00  graya(0,1)
5,84: (0,255,0,1)  #00FF00  graya(0,1)
....

Here is my Code:

$canvasImg = new Imagick("_sources/passepartout/". $deviceName .".png");

$canvasImg->setFormat(txt);

preg_match_all("/(\\d+),(\\d+): \\(0,255,0,1\\)/is", $canvasImg, $colorMatchAll);

$firstPixelX = reset($colorMatch[1]);
$lastPixelX = end($colorMatch[1]);

$canvasWidth = $lastPixelX - $firstPixelX;

This works fine so far, but its awfully slow as it finds all pixels in the colored rectangle including the full height of the y axis.

Now as I'm only interested in the width of this #00FF00 rectangle I thought it would be faster if the regex just finds the first pixel that is #00FF00 and then goes through the pixel row until the end (row 83 in my example) and stops as soon as it hits the 84th row.

Is there a modification I could do to my regex that it does what I'm looking for?

micharuss
  • 3
  • 1
  • 2
    Seriously, if you want more speed, use image manipulation functions instead of.... *this*. – Lucas Trzesniewski Jan 31 '16 at 15:25
  • I see you are just interested in the last and first ? – Yehia Awad Jan 31 '16 at 15:28
  • @яша yes, just the last and first – micharuss Jan 31 '16 at 15:29
  • @LucasTrzesniewski i tried, but found nothing that matched my needs – micharuss Jan 31 '16 at 15:33
  • http://php.net/manual/en/book.image.php – Lucas Trzesniewski Jan 31 '16 at 15:39
  • so if I understand you right for the whole process you will not the beginning and the end of each row ? for instance for row 83 you want the last and the end for row 84 you want the last and end and so on and so forth or you want just the beginning and end of the whole text? – Yehia Awad Jan 31 '16 at 15:39
  • @яша i want just the first and last of the first row this color appears. the rest of the rows don't matter. but i don't know where the first row starts. so basically find the first row with that color and look where it ends in that row. then stop. – micharuss Jan 31 '16 at 15:53
  • you are having this output as string type ? – Yehia Awad Jan 31 '16 at 15:55
  • @яша do you mean the txt file? – micharuss Jan 31 '16 at 16:20
  • First: do it as @LucasTrzesniewski said - use proper image functions. You could however anchor the regex to the start - your first approach takes 558 steps (according to https://regex101.com/r/pU7fL1/1), with an anchor it is only 58 - an improvement by 90% (see it here: https://regex101.com/r/pU7fL1/2) – Jan Jan 31 '16 at 16:46
  • Thanks guys, an image function would be totally preferable, but there seems to be none thats capable of what i want. But i figured out a regex that does what i wanted: https://regex101.com/r/pU7fL1/3 – micharuss Jan 31 '16 at 21:27
  • @micharuss Are you doing the same homework problem as this guy: http://stackoverflow.com/questions/34939555/php-how-to-find-coordinates-of-edges-in-an-image-using-imagick-or-other-librar/34956468#34956468 – Danack Jan 31 '16 at 23:23

1 Answers1

0

You can either use $imagick->trimImage() and then $imagick->getImagePage() to get the location if the rectangle is the only thing on the image, or you can iterate over the pixels, and do your own edge detection.

$imagick = new \Imagick(realpath($imagePath));
$imageIterator = $imagick->getPixelRegionIterator(0, 0, 
     $imagick->getImageWidth(),
     $imagick->getImageHeight(),
 );

/* Loop through pixel rows */
foreach ($imageIterator as $pixels) {
    /** @var $pixel \ImagickPixel */
    /* Loop through the pixels in the row (columns) */
    foreach ($pixels as $column => $pixel) {
        $red = $pixel->getColorValue(\Imagick::COLOR_RED);
        $green = $pixel->getColorValue(\Imagick::COLOR_GREEN);
        $blue = $pixel->getColorValue(\Imagick::COLOR_BLUE);
        //Do detection here
    }
    /* Sync the iterator, this is important to do on each iteration */
    $imageIterator->syncIterator();
}

$imageIterator->clear();
Danack
  • 24,939
  • 16
  • 90
  • 122
  • thanks, that worked for me! unfortunately, trimImage() didn't work because there is a lot on the image besides my colored rectangle... – micharuss Feb 01 '16 at 16:30