Skip to content

Tag: tutorial

How to easily image search with python

image search with python

This is the second time that I’m writing on how to do image search with python. The first blog post. That I wrote about the subject got a lot of interest and even today I regularly get people commenting on it or coming to the github repo asking for help. So I figured it was time for a refresher.

Python imagesearch is now a pip-eable package

I have put a bit of work to put the library as a package. In order to allow you to just pip the library. This is a much better solution than me saying nonsense like “copy the file in your project”. Now it is as easy as doing :

pip3 install python-imageseach-drov0

The above will probably fail or you won’t be able to use the library as you need extra packages depending on your os :

Linux

sudo pip3 install python3-xlib
sudo apt-get install -y scrot -y
sudo apt-get install -y python3-tk
sudo apt-get install -y python3-dev
sudo apt-get install -y python3-opencv


MacOs

brew install opencv
pip3 install -U pyobjc-core
pip3 install -U pyobjc

Windows

No extra installation steps needed 🙂

Quick start

The simplest example to do image search with python is this:

from python_imagesearch.imagesearch import imagesearch

pos = imagesearch("./github.png")
if pos[0] != -1:
print("position : ", pos[0], pos[1])
else:
print("image not found")

Simply search for one occurrence of the image “github.png” on the screen and print its x/y position

Other functions

imagesearcharea

Performs an image search on a specific rectangle of the screen, it’s very useful to speed up searches as there will be less screen space to search.
It’s also useful to focus the search only on a specific part of the screen to reduce the chances of having a false positive.

pos = imagesearcharea("./github.png", 0, 0, 800, 600)
if pos[0] != -1:
    print("position : ", pos[0], pos[1])
else:
    print("image not found")

Input:
image : path to the image file (see opencv imread for supported types)
precision : the higher, the lesser tolerant and fewer false positives are found default is 0.8
im : a PIL image, usefull if you intend to search the same unchanging region for several elements

Output:
the top left corner coordinates of the element if found as an array [x,y] or [-1,-1] if not

region_grabber

Very useful to optimize imagesearcharea or imagesearch calls, by getting an already processed image you can perform multiple searches on it with great speed gains. Here’s an example

# non -optimized way :
time1 = time.clock()
for i in range(10):
    imagesearcharea("./github.png", 0, 0, 800, 600)
    imagesearcharea("./panda.png", 0, 0, 800, 600)
print(str(time.clock() - time1) + " seconds (non optimized)")

# optimized way :

time1 = time.clock()
im = region_grabber((0, 0, 800, 600))
for i in range(10):
    imagesearcharea("./github.png", 0, 0, 800, 600, 0.8, im)
    imagesearcharea("./panda.png", 0, 0, 800, 600, 0.8, im)
print(str(time.clock() - time1) + " seconds (optimized)")

# sample output :

# 1.6233619831305721 seconds (non optimized)
# 0.4075934110084374 seconds (optimized)

Input: a tuple containing the 4 coordinates of the region to capture tuple should contain coordinates of : topx, topy, bottomx, bottomy

Output: a PIL image of the area selected.

imagesearch_loop

Searches for an image on screen continuously until it’s found, useful to make a waiting script until x image appears. For instance waiting for the end of a loading screen.

from python_imagesearch.imagesearch import imagesearch_loop

pos = imagesearch_loop("./github.png", 1)
print("position : ", pos[0], pos[1])

Input:
image : path to the image file (see opencv imread for supported types)
time : Waiting time after failing to find the image (seconds)
precision : the higher, the lesser tolerant and fewer false positives are found default is 0.8

Output:
the top left corner coordinates of the element if found as an array [x,y]

imagesearch_numLoop

Searches for an image on screen continuously until it’s found or max number of samples reached.

from python_imagesearch.imagesearch import imagesearch_numLoop

pos = imagesearch_numLoop("./github.png", 1, 50)
if pos[0] != -1:
print("position : ", pos[0], pos[1])
else:
print("image not found")

Input:
image : path to the image file (see opencv imread for supported types)
time : Waiting time after failing to find the image
maxSamples: maximum number of samples before function times out.
precision : the higher, the lesser tolerant and fewer false positives are found default is 0.8

Output: the top left corner coordinates of the element if found as an array [x,y]

imagesearch_region_loop

Very similar to imagesearch_loop except it works with regions

from python_imagesearch.imagesearch import imagesearch_region_loop

pos = imagesearch_region_loop("./github.png", 1, 0, 0, 800, 600)
print("position : ", pos[0], pos[1])

Input:
image : path to the image file (see opencv imread for supported types)
time : Waiting time after failing to find the image
x1 : top left x value
y1 : top left y value
x2 : bottom right x value
y2 : bottom right y value
precision : the higher, the lesser tolerant and fewer false positives are found default is 0.8


Output:
the top left corner coordinates of the element as an array [x,y]

imagesearch_count

Counts how many occurrences there are of the image there are on the screen.

from python_imagesearch.imagesearch import imagesearch_count

count = imagesearch_count("./github.png")
print(count)

Input:
image : path to the target image file (see opencv imread for supported types)
precision : the higher, the lesser tolerant and fewer false positives are found default is 0.9

Output:
the number of times a given image appears on the screen.
optionally an output image with all the occurances boxed with a red outline.

imagesearch_from_folder

Performs an imagesearch on all the images in a folder. This function was done by kadusalles

from python_imagesearch.imagesearch import imagesearch_count

results = str(imagesearch_from_folder('./', 0.8))
print(results)

Input:
path: to the folder containing the images (supported image types are jpg, gif, png and jpeg)
precision : the higher, the lesser tolerant and fewer false positives are found default is 0.9

Output:
A dictionnary with all the images where the key is the image path and the value is it’s position

Conclusion

And that’s about it ! Now you should be able to easily perform Image search with python. If you are interested in the actual code or want to contribute feel free to head on over to the github repository : https://github.com/drov0/python-imagesearch and if you liked my article, come to see more at https://brokencode.io

How to use local go modules with golang with examples

Image result for golang modules"

When coding I like to put everything inside of folders depending on what they do. I almost always got specific directories for utils, parser, etc. So here’s a complete guide on how to use local go modules with golang

When I started using go modules I was migrating a project from dep which followed this architecture. I had an utils directory, and another directory which handled all parsing for my data input into structs. But then I ended up needing functions from the utils directory. I was stuck and couldn’t find how to make directories importable everywhere in the project (like dep used to). Because relative imports are a nightmare with go modules (removing the gopath has a toll after all).

So after quite a bit of fuming and searching all of the web for a solution that is elegant and not something like some crazy relative imports, I found this solution and since I struggled so much to find the solution I figured that I would share it with you all.

The example project

Here’s our example project directory structure :
├── go.mod
├── hello
│   ├── go.mod
│   └── hello.go
├── main.go
├── README.md
└── utils
├── go.mod
└── multigreet.go

You can also see the example code on my github repository here : https://github.com/drov0/GolangLocalModulesExample

The code is relatively straightforward : I have two directories, hello and utils that I want to import from the main.go file. And when things get a little tricky is that I want to import the hello directory inside of the utils directory.

The code for the hello.go file that we want to import is this super complicated function :

package hello

func Hello(name string) string {
   return "hello " + name
}

The code for the addAndGreet.go file is a bit more complex :

package utils

import (
"example.org/hello"
"strconv"
)

func AddAndGreet(name string, a, b int) string {
return hello.Hello(name) + " " + strconv.Itoa(a + b)
}

Notice how we are importing "example.org/hello".

Finally here’s the main.go file :

package main

import (
"example.org/hello"
"example.org/utils"
"fmt"
)

func main() {
fmt.Println(hello.Hello("martin"))
fmt.Println(utils.AddAndGreet("martin", 2, 3))
}

Obviously the modules example.org/hello or example.org/utils do not exist so these imports make zero sense to our compiler so let’s help him out a bit

Importing local modules in main.go

So first we simply have to convert all of our directories into go modules. For that we need to add a go.mod at the root of every directories.
Then inside of that go.mod give them whatever name that we want as module name. but bear in mind that it has to be an url. In my example I put this:

module example.org/hello in the go.mod for the hello directory
module example.org/utils in the go.mod for the utils directory

The import makes a bit more sense now huh ? but we are not done yet.

The replace keyword

This is where the magic happens, go.mod files have a few keywords that can be very useful, one of them is replace what replace does is that it takes a module path (eg : example.org/hello) and replaces it with a direct or relative path.

here’s the syntax for the replace keyword :

replace url.com/of/the/module => /direct/path/to/files

Note that replace also works with relative paths.

The main go.mod

module example.com/localmodexample

go 1.13

require (
example.org/hello v0.0.0
example.org/utils v0.0.0

)

replace (
example.org/hello => ./hello
example.org/utils => ./utils
)

Usuall go module dependencies work with versions, so to use local go modules with golang you have to set v0.0.0

Finally after the require, I just tell the compiler that those urls are local and can be found in the same directory under ./hello and ./utils. The great thing about this main go.mod file is that now even the utils module will know where to find the hello module because the url have been replaced.

Conclusion

And that’s all you need to know to use local go modules with golang. Hopefully this will save you all the hours I put into it. Keep in mind that you can find the complete code on my github : https://github.com/drov0/GolangLocalModulesExample

I know it’s been a while since my last post but I got quite busy. But now I should be able to make posts more often on this blog.

MPU-6050 easily get x y z values tutorial

Hello, a few weeks ago I started working on a time tracker project. And one of the vital components was an MPU-6050 gyroscope, I needed a tutorial to get the x y z values. And I found many tutorials on it. But whenever I tried them I had a lot of problems noticeably with drift. The drift means that the readings were not stable at all. After 30 seconds some values had quadrupled even though the card itself was still. So I found a lot of answers that told me “yeah this is a common problem”. But not really any help in solving it. So here’s a full MPU60-50 tutorial from start to finish that I wish I had when I wanted to use it.

Wiring

The wiring is pretty straightforward :

  • VCC to the 5V pin
  • GND to ground
  • SCL to the A4 pin
  • SDA to the A5 pin
  • INT to the 2 pin

The other pins are not useful for the most of the use cases. If you are interested nonetheless or want to know more about this chip I encourage you to read the documentation here : https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/

MPU 6050 wiring on an arduino UNO board
MPU 6050 wiring on an arduino UNO board

Reading the x y z values

Now that the wiring is done, it’s time to move onto the actual software. In order to get the x y z values we need to be able to read the raw values, there are a ton of tutorials on how to do that. But ultimately it’s not that useful as you need to make calculations on it for these values to really have meaning. After digging quite a while and trying out various tutorial like this one I realized that, while it’s quite interesting to make everything from scratch, when you want to build things quickly, it’s often better to use a library from someone who’s spent much more time than you on the problem.

Also none of those tutorials seemed to handle the part where you’re supposed to calculate offsets and calibrate the damn thing. And if you don’t calibrate it, you most likely will run into problems sooner or later.

The MPU6050 tockn library

Github user tockn created this awesome library called MPU6050_tockn (leave him a star if you can). Which does pretty much everything for you. Better yet, it’s available on the arduino library manager. So it’s extra easy to download it.

Screenshot of the arduino library manager with the MPU-6050_tockn library selected

Go ahead and download the library (I’m using 1.5.2). And now load up this code :

#include <MPU6050_tockn.h>
#include <Wire.h>

MPU6050 mpu6050(Wire);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  mpu6050.begin();
  mpu6050.calcGyroOffsets(true);
}

void loop() {
  mpu6050.update();
  Serial.print("angleX : ");
  Serial.print(mpu6050.getAngleX());
  Serial.print("\tangleY : ");
  Serial.print(mpu6050.getAngleY());
  Serial.print("\tangleZ : ");
  Serial.println(mpu6050.getAngleZ());
}

After a few seconds of loading you should get this :

Serial monitor with X Y Z data
Serial monitor with X Y Z data

Success ! Keep in mind that you might still encounter drift issues especially on the z parameter. Also once your offsets are calculated, use mpu6050.setGyroOffsets(x, y, z); rather than calculating them each time. It’ll allow for a faster startup and less chances of you messing up the offset calculations by unintentionally moving it.

And that’s it for this tutorial on how to get x y z values on the MPU-6050. Feel free to leave a comment if you have any questions. And if you want to dig deeper, there is another example with more data from all the elements on the board (accelerometer/gyroscope and temperature) here.