Lazerhorse
Lazerhorse

Reputation: 123

Rotating images on a matplotlib plot

I have this code, which I largely modified from Matplotlib: How to plot images instead of points?

See my output graph: Output

I would like to rotate each hamster picture with respect to the column "Rotation" in the dataframe. So that the pictures are orientated in the correct rotation.

How do I do this? I am struggling to understand the "offsetbox" guide for Matplotlib.

These are the first 10 rows on my dataframe.

import pandas as pd

df = pd.DataFrame([['21:21:00',0.1,0.0,10], ['21:21:01',0.1,0.0,20], ['21:21:02',0.1,0.0,28]\
                   ,['21:21:03',0.1,0.0,12], ['21:21:03',0.1,0.0,12], ['21:21:04',0.5,0.6,12]\
                   ,['21:21:05',3.7,4.4,10], ['21:21:06',6.8,8.1,10], ['21:21:07',9.9,11.9,20]\
                   ,['21:21:08',13.0,15.7,29], ['21:21:09',16.1,19.5,33]]\
                   ,columns=['Time', 'Northings', 'Eastings','Rotation'])

def main():
    x = df['Eastings'][::2]
    y = df['Northings'][::2]
    image_path = get_sample_data(r'C:\Users\j.smith.EA.000\Desktop\PYTHON\hamster.jpg')
    fig, ax = plt.subplots()
    imscatter(x, y, image_path, zoom=0.03, ax=ax)
    ax = df.plot(x = 'Eastings', y = "Northings", grid = True, figsize=(15,8), legend = False\
    , xlim = (-30,30), ylim = (-30,30), kind = 'line', ax=ax)

    plt.show()

def imscatter(x, y, image, ax=None, zoom=1):
    image = plt.imread(image)
    im = OffsetImage(image, zoom=zoom)
    x, y = np.atleast_1d(x, y)
    artists = []
    for x0, y0 in zip(x, y):
        ab = AnnotationBbox(im, (x0, y0), frameon=False,)
        artists.append(ax.add_artist(ab))
    return artists

main()

Upvotes: 1

Views: 1471

Answers (1)

corvo
corvo

Reputation: 724

Going through your code, in your imscatter() function the for loop is assigning each image to each datapoint. You are passing the image to ab = AnnotationBbox(im, (x0, y0), frameon=False,) where im is your image object. Here, I would suggest passing the image after rotating it to whatever degree you want. For ex:

im = rotate_image_by_angle(im, get_the_rotation_angle_from_colume)
ab = AnnotationBbox(im, (x0, y0), frameon=False,)
artists.append(ax.add_artist(ab))

This approach is implemented in the following code

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from matplotlib.cbook import get_sample_data
import cv2
import imutils


df = pd.DataFrame([['21:21:00',0.1,0.0,0], ['21:21:01',3.1,3.0,20], ['21:21:02',6.1,6.0,30]\
                   ,['21:21:03',9.1,9.0,40], ['21:21:03',12.1,12.0,50], ['21:21:04',15.1,15.2,60]\
                   ,['21:21:05',18.1,18.0,70], ['21:21:06',21.1,21.0,80], ['21:21:07',24.0,24.1,90]\
                   ,['21:21:08',27.0,27.1,100], ['21:21:09',30.0,30.1,110]]\
                   ,columns=['Time', 'Northings', 'Eastings','Rotation'])

def main():
    x = df['Eastings'][::2]
    y = df['Northings'][::2]
    z = df['Rotation'][::2]

    fig, ax = plt.subplots()
    imscatter(x, y, z, zoom=0.03, ax=ax)
    ax = df.plot(x = 'Eastings', y = "Northings", grid = True, figsize=(15,7), legend = False\
    , xlim = (-5,30), ylim = (-5,30), kind = 'line', ax=ax)

    plt.show()

def imscatter(x, y, z, ax=None, zoom=1):

    image = cv2.imread('image.png')
    im = OffsetImage(image, zoom=zoom)


    x, y, z = np.atleast_1d(x, y, z)
    artists = []
    for x0, y0, z0 in zip(x, y, z):
        rotated = rotate_bound(image, z0)
        im = OffsetImage(rotated, zoom=zoom)
        ab = AnnotationBbox(im, (x0, y0), frameon=False,)
        artists.append(ax.add_artist(ab))
    return artists

def rotate_bound(image, angle):
    # grab the dimensions of the image and then determine the
    # center
    (h, w) = image.shape[:2]
    (cX, cY) = (w // 2, h // 2)

    # grab the rotation matrix (applying the negative of the
    # angle to rotate clockwise), then grab the sine and cosine
    # (i.e., the rotation components of the matrix)
    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])

    # compute the new bounding dimensions of the image
    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))

    # adjust the rotation matrix to take into account translation
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY

    # perform the actual rotation and return the image
    return cv2.warpAffine(image, M, (nW, nH), borderValue=(255,255,255))

main()

I have made minor changes throughout the code and added a function rotate_bound(image, angle) which will rotate the image by a given angle. More details on how it was done, can be found here. The Output now looks like this... Output

Upvotes: 1

Related Questions