how can I detect all the text that inside a block with Google Vision Api

狂风中的少年 提交于 2019-12-11 15:37:07

问题


I'm trying to extract text from an image with google vision api, it works. But I just want to detect part of the image to get certain text.

this is the image I used

I just want to extract all the text from maybank2u.com until From Account: I know there are some tutorials to do this trick by using block but those tutorials are different programming languages.

My code:

<div class="row">
    <div class="col-12">
        <ol>
            <?php foreach ($text as $key => $texts): ?> 
                <li><h6> <?php echo ucfirst($texts->info()['description']) ?></h6><<br><br> 
                </li>
            <?php endforeach ?>
        </ol>
    </div>
</div>

This code will getting all the text from image

Output:


回答1:


The code below works for me. I have one php file, test.php and one image file /images/UUIPXl.png.

To get each line of text, I iterate the text annotations from Google Vision, and create an array of row items. Each of these has an x position and a text value.

I then sort each row by x position and concatenate to create a line of text.

Finally we stop once we get the final desired line of text.

I get a result like so:

  • maybank2u.com
  • Open BillPayment
  • Status: Successful
  • Reference number: 2950211545
  • Transaction date: 01 Feb 2016 13:09:17
  • Amount: RM100.00
  • From Account 564155051577 WCA

The php code:

<?php 

    require 'vendor/autoload.php';
    use Google\Cloud\Vision\VisionClient;

    $config = ["keyFile" => json_decode(file_get_contents("./APIKey.json"), true) ];
    $vision = new VisionClient($config);

    $image = $vision->image(
        fopen('./images/UUIPXl.png', 'r'),
        ['TEXT_DETECTION']
    );

    $textAnnotations = $vision->annotate($image)->text();
    $rows = [];

    // Function used to sort our lines.
    function sortProc($a, $b)
    {
        if ($a["x"] === $b["x"]) {
            return 0;
        }
        return ($a["x"] < $b["x"]) ? -1 : 1;
    }

    // Remove first row (complete text).
    array_shift($textAnnotations);

    // We should calculate this, use a reasonable value to begin with.
    $lineHeight = 8;

    foreach ($textAnnotations as $text) {
        $key = round(((double)($text->info()["boundingPoly"]["vertices"][0]["y"]))/$lineHeight);
        $x = (int)$text->info()["boundingPoly"]["vertices"][0]["x"];
        $value = ["x" => $x, "text" => $text->description()];
        if (!isset($rows[$key])) {
            $rows[$key] = [];
        }
        $rows[$key][] = $value;
    }

    $text = [];
    foreach ($rows as $key => $value) {
        // Sort by x value.
        usort($value, "sortProc");

        // Concatenate each line
        $result = array_reduce($value, function($acc, $elem) {
            $acc .= " " . $elem["text"];
            return $acc;
        }, "");

        $text[] = $result;

        // Stop when we get here!
        if (preg_match("/from account/i", $result)) {
            break;
        }
    }

?>

<div class="row" style="padding: 20px;">
    <div class="col-12">
        <ul>
            <?php foreach ($text as $row): ?> 
                <li><h3> <?php echo ucfirst($row) ?></h3></li>
            <?php endforeach ?>
        </ul>
    </div>
</div>



回答2:


If you only want to limit the output and its every time the same string that should stop the execution, then do the following:

<div class="row">
    <div class="col-12">
        <ol>
            <?php foreach ($text as $key => $texts): ?> 
                <?php if (strpos($texts->info()['description'], 'From Account') !== false) break; ?>
                <li><h6> <?php echo ucfirst($texts->info()['description']) ?></h6><<br><br> 
                </li>
            <?php endforeach ?>
        </ol>
    </div>
</div>

Explanation:
If $texts->info()['description'] contains the text From Account it ends the execution of the foreach loop through break. If you need to check for multiple keywords read this.

An alternative solution would be to crop the image with imagecrop() before sending it to the API. But for this you need to be sure that it never changes the size / position of the texts.

P.S. are you sure everyone should see those private data in your screenshot?

Update1
As you asked. This would be the same code but using the alternative syntax for control structures:

<div class="row">
    <div class="col-12">
        <ol>
            <?php foreach ($text as $key => $texts): ?> 
                <?php if (strpos($texts->info()['description'], 'From Account') !== false): ?>
                <?php break; ?>
                <?php endif; ?>
                <li><h6> <?php echo ucfirst($texts->info()['description']) ?></h6><<br><br> 
                </li>
            <?php endforeach ?>
        </ol>
    </div>
</div>

Maybe this solves your problem as the same page includes this note:

Mixing syntaxes in the same control block is not supported.

Update2

After you updated your question its more clear now. The output does not contain one element per text line. Instead it contains multiple lines of texts. Because of that my first code did not echo anything as it finds From Account in the very first array element.

Because of that we need to search for the string From Account and cut the text line:

<div class="row">
    <div class="col-12">
        <ol>
            <?php foreach ($text as $key => $texts): ?> 
                <?php
                $text = $texts->info()['description'];
                // search for string
                $pos = strpos($texts->info()['description'], 'From Account');
                if ($pos !== false) {
                    // if the string was found cut the text
                    $text = substr($text, 0, $pos);
                }
                ?>
                <li><h6> <?php echo $text ?></h6><<br><br> 
                </li>
            <?php endforeach ?>
        </ol>
    </div>
</div>

Optionally you could add this before <?php endforeach ?> to skip all following array elements:

                <?php
                if ($pos !== false) {
                    break;
                }
                ?>

Note: @TerryLennox uses preg_match to find From Account. There is no difference between this and using strpos (most prefer avoiding regex). But his answer contains another good tip. He uses the text position information to add the text line by line to a new array. This could be really useful depending on your targets how to display/store the text.



来源:https://stackoverflow.com/questions/57817740/how-can-i-detect-all-the-text-that-inside-a-block-with-google-vision-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!