Preparing images and resources for the app

Alongside RequestsUtils.py and ImageSearchSession.py, let's create another file called ResizeUtils.py with the following import statements:

import numpy
import cv2

For display in a GUI, images often must be resized. One popular mode of resizing is called aspect fill. This means that we want to preserve the image's aspect ratio while changing its larger dimension (width for a landscape image or height for a portrait image) to a certain value. OpenCV does not directly provide this resizing mode but it does provide a function, cv2.resize, which accepts an image, target dimensions, and optional arguments including an interpolation method. We can write our own function, cvResizeAspectFill, which accepts an image, maximum size, and preferred interpolation methods for upsizing and downsizing the images. It determines the appropriate arguments for cv2.resize and passes them along. Here is the implementation:

def cvResizeAspectFill(src, maxSize,
  upInterpolation=cv2.INTER_LANCZOS4,
  downInterpolation=cv2.INTER_AREA):
  h, w = src.shape[:2]
  if w > h:
    if w > maxSize:
      interpolation=downInterpolation
    else:
      interpolation=upInterpolation
    h = int(maxSize * h / float(w))
    w = maxSize
  else:
    if h > maxSize:
      interpolation=downInterpolation
    else:
      interpolation=upInterpolation
    w = int(maxSize * w / float(h))
    h = maxSize
  dst = cv2.resize(src, (w, h), interpolation=interpolation)
  return dst

Now, let's create another file called WxUtils.py with the following import statements:

import numpy
import cv2
import wx

OpenCV and wxPython use different image formats, so we will implement a conversion function, wxBitmapFromCvImage. OpenCV stores color channels in a BGR order, whereas wxPython expects an RGB order. We can use an OpenCV function, cv2.cvtColor, to reformat the image data accordingly. Then, we can use a wxPython function, wx.BitmapFromBuffer, to read the reformatted data into a wxPython bitmap, which we return. Here is the implementation:

def wxBitmapFromCvImage(image):
  image = cv2.cvtColor(image, cv2.cv.CV_BGR2RGB)
  h, w = image.shape[:2]
  bitmap = wx.BitmapFromBuffer(w, h, image)
  return bitmap

We have one more utility module to make. Let's create a file, PyInstallerUtils.py, with import statements for the os and sys modules from Python's standard library:

import os
import sys

When we bundle our application using PyInstaller, the paths to the resources will change. Thus, we need a function that correctly resolves paths regardless of whether our application has been bundled or not. Let's add a function, pyInstallerResourcePath, which resolves a given path relative to the app directory (the '_MEIPASS' attribute) or, failing that, the current working directory ('.'). This is implemented as follows:

def pyInstallerResourcePath(relativePath):
  basePath = getattr(sys, '_MEIPASS', os.path.abspath('.'))
  return os.path.join(basePath, relativePath)

Our utilities modules are done now and we can move on to implement the frontend of the Luxocator.