Create VTKpolydata using x,y,z coordinates - visual-c++

I am beginner in VTK. I have a dataset as x,y,z points and the value of each point. I want to create a vtkpolydata set using the points and create a contour using values of each point.
Please tell me how to create a vtkPolyData set using a given set of points in c++.
Thanks.

This example shows how to do both parts of your question:
http://www.vtk.org/Wiki/VTK/Examples/Cxx/GeometricObjects/PolyLine

Here is a script adapted from this blog to plot 3D xyz data:
'''
Modified Python 3 VTK script to Display 3D xyz data
Credits to : https://sukhbinder.wordpress.com/2013/09/17/python-vtk-script-to-display-3d-xyz-data/
Script name: xyzviewer.py
'''
import vtk
from numpy import random,genfromtxt,size
class VtkPointCloud:
def __init__(self, zMin=-10.0, zMax=10.0, maxNumPoints=1e6):
self.maxNumPoints = maxNumPoints
self.vtkPolyData = vtk.vtkPolyData()
self.clearPoints()
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(self.vtkPolyData)
mapper.SetColorModeToDefault()
mapper.SetScalarRange(zMin, zMax)
mapper.SetScalarVisibility(1)
self.vtkActor = vtk.vtkActor()
self.vtkActor.SetMapper(mapper)
def addPoint(self, point):
if (self.vtkPoints.GetNumberOfPoints() < self.maxNumPoints):
pointId = self.vtkPoints.InsertNextPoint(point[:])
self.vtkDepth.InsertNextValue(point[2])
self.vtkCells.InsertNextCell(1)
self.vtkCells.InsertCellPoint(pointId)
else:
r = random.randint(0, self.maxNumPoints)
self.vtkPoints.SetPoint(r, point[:])
self.vtkCells.Modified()
self.vtkPoints.Modified()
self.vtkDepth.Modified()
def clearPoints(self):
self.vtkPoints = vtk.vtkPoints()
self.vtkCells = vtk.vtkCellArray()
self.vtkDepth = vtk.vtkDoubleArray()
self.vtkDepth.SetName('DepthArray')
self.vtkPolyData.SetPoints(self.vtkPoints)
self.vtkPolyData.SetVerts(self.vtkCells)
self.vtkPolyData.GetPointData().SetScalars(self.vtkDepth)
self.vtkPolyData.GetPointData().SetActiveScalars('DepthArray')
def load_data(filename,pointCloud):
data = genfromtxt(filename,dtype=float,usecols=[0,1,2])
for k in range(size(data,0)):
point = data[k] #20*(random.rand(3)-0.5)
pointCloud.addPoint(point)
return pointCloud
if __name__ == '__main__':
import sys
if (len(sys.argv) < 2):
print ('Usage: xyzviewer.py itemfile')
sys.exit()
pointCloud = VtkPointCloud()
pointCloud=load_data(sys.argv[1],pointCloud)
# Renderer
renderer = vtk.vtkRenderer()
renderer.AddActor(pointCloud.vtkActor)
#renderer.SetBackground(.2, .3, .4)
renderer.SetBackground(0.0, 0.0, 0.0)
renderer.ResetCamera()
# Render Window
renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer)
# Interactor
renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
# Begin Interaction
renderWindow.Render()
renderWindow.SetWindowName("XYZ Data Viewer"+sys.argv[1])
renderWindowInteractor.Start()
You could run it as follows:
python xyzviewer.py filename
filename is the file that contains the xyz data.

Related

Python 3 Multiprocessing and openCV problem with dictionary sharing between processor

I would like to use multiprocessing to compute the SIFT extraction and SIFT matching for object detection.
For now, I have a problem with the return value of the function that does not insert data in the dictionary.
I'm using Manager class and image that are open inside the function. But does not work.
Finally, my idea is:
Computer the keypoint for every reference image, use this keypoint as a parameter of a second function that compares and match with the keypoint and descriptors of the test image.
My code is:
# %% Import Section
import cv2
import numpy as np
from matplotlib import pyplot as plt
import os
from datetime import datetime
from multiprocessing import Process, cpu_count, Manager, Lock
import argparse
# %% path section
tests_path = 'TestImages/'
references_path = 'ReferenceImages2/'
result_path = 'ResultParametrizer/'
#%% Number of processor
cpus = cpu_count()
# %% parameter section
eps = 1e-7
useTwo = False # using the m and n keypoint better with False
# good point parameters
distanca_coefficient = 0.75
# gms parameter
gms_thresholdFactor = 3
gms_withRotation = True
gms_withScale = True
# flann parameter
flann_trees = 5
flann_checks = 50
#%% Locker
lock = Lock()
# %% function definition
def keypointToDictionaries(keypoint):
x, y = keypoint.pt
pt = float(x), float(y)
angle = float(keypoint.angle) if keypoint.angle is not None else None
size = float(keypoint.size) if keypoint.size is not None else None
response = float(keypoint.response) if keypoint.response is not None else None
class_id = int(keypoint.class_id) if keypoint.class_id is not None else None
octave = int(keypoint.octave) if keypoint.octave is not None else None
return {
'point': pt,
'angle': angle,
'size': size,
'response': response,
'class_id': class_id,
'octave': octave
}
def dictionariesToKeypoint(dictionary):
kp = cv2.KeyPoint()
kp.pt = dictionary['pt']
kp.angle = dictionary['angle']
kp.size = dictionary['size']
kp.response = dictionary['response']
kp.octave = dictionary['octave']
kp.class_id = dictionary['class_id']
return kp
def rootSIFT(dictionary, image_name, image_path,eps=eps):
# SIFT init
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
sift = cv2.xfeatures2d.SIFT_create()
keypoints, descriptors = sift.detectAndCompute(image, None)
descriptors /= (descriptors.sum(axis=1, keepdims=True) + eps)
descriptors = np.sqrt(descriptors)
print('Finito di calcolare, PID: ', os.getpid())
lock.acquire()
dictionary[image_name]['keypoints'] = keypoints
dictionary[image_name]['descriptors'] = descriptors
lock.release()
def featureMatching(reference_image, reference_descriptors, reference_keypoints, test_image, test_descriptors,
test_keypoints, flann_trees=flann_trees, flann_checks=flann_checks):
# FLANN parameter
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=flann_trees)
search_params = dict(checks=flann_checks) # or pass empty dictionary
flann = cv2.FlannBasedMatcher(index_params, search_params)
flann_matches = flann.knnMatch(reference_descriptors, test_descriptors, k=2)
matches_copy = []
for i, (m, n) in enumerate(flann_matches):
if m.distance < distanca_coefficient * n.distance:
matches_copy.append(m)
gsm_matches = cv2.xfeatures2d.matchGMS(reference_image.shape, test_image.shape, keypoints1=reference_keypoints,
keypoints2=test_keypoints, matches1to2=matches_copy,
withRotation=gms_withRotation, withScale=gms_withScale,
thresholdFactor=gms_thresholdFactor)
#%% Starting reference list file creation
reference_init = datetime.now()
print('Start reference file list creation')
reference_image_process_list = []
manager = Manager()
reference_image_dictionary = manager.dict()
reference_image_list = manager.list()
for root, directories, files in os.walk(references_path):
for file in files:
if file.endswith('.DS_Store'):
continue
reference_image_path = os.path.join(root, file)
reference_name = file.split('.')[0]
image = cv2.imread(reference_image_path, cv2.IMREAD_GRAYSCALE)
reference_image_dictionary[reference_name] = {
'image': image,
'keypoints': None,
'descriptors': None
}
proc = Process(target=rootSIFT, args=(reference_image_list, reference_name, reference_image_path))
reference_image_process_list.append(proc)
proc.start()
for proc in reference_image_process_list:
proc.join()
reference_end = datetime.now()
reference_time = reference_end - reference_init
print('End reference file list creation, time required: ', reference_time)
I faced pretty much the same error. It seems that the code hangs at detectAndCompute in my case, not when creating the dictionary. For some reason, sift feature extraction is not multi-processing safe (to my understanding, it is the case in Macs but I am not totally sure.)
I found this in a github thread. Many people say it works but I couldn't get it worked. (Edit: I tried this later which works fine)
Instead I used multithreading which is pretty much the same code and works perfectly. Of course you need to take multithreading vs multiprocessing into account

How to generate heat map on the Whole Slide Images (.svs format) using some probability values?

I am trying to generate heat map, or probability map, for Whole Slide Images (WSIs) using probability values. I have coordinate points (which determine areas on the WSIs) and corresponding probability values.
Basic Introduction on WSI: WSIs are large is size (almost 100000 x 100000 pixels). Hence, can't open these images using normal image viewer. The WSIs are processed using OpenSlide software.
I have seen previous posts in Stack Overflow on related to heat map, but as WSIs are processed in a different way, I am unable to figure out how to apply these solutions. Some examples that I followed: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, etc.
To generate heat map on WSIs, follow below instructions:
First of all Extract image patches and save the coordinates. Use below code for patch extraction. The code require some changes as per the requirements. The code has been copied from: patch extraction code link
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import logging
try:
import Image
except:
from PIL import Image
import math
import numpy as np
import openslide
import os
from time import strftime,gmtime
parser = argparse.ArgumentParser(description='Extract a series of patches from a whole slide image')
parser.add_argument("-i", "--image", dest='wsi', nargs='+', required=True, help="path to a whole slide image")
parser.add_argument("-p", "--patch_size", dest='patch_size', default=299, type=int, help="pixel width and height for patches")
parser.add_argument("-b", "--grey_limit", dest='grey_limit', default=0.8, type=float, help="greyscale value to determine if there is sufficient tissue present [default: `0.8`]")
parser.add_argument("-o", "--output", dest='output_name', default="output", help="Name of the output file directory [default: `output/`]")
parser.add_argument("-v", "--verbose",
dest="logLevel",
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
default="INFO",
help="Set the logging level")
args = parser.parse_args()
if args.logLevel:
logging.basicConfig(level=getattr(logging, args.logLevel))
wsi=' '.join(args.wsi)
""" Set global variables """
mean_grey_values = args.grey_limit * 255
number_of_useful_regions = 0
wsi=os.path.abspath(wsi)
outname=os.path.abspath(args.output_name)
basename = os.path.basename(wsi)
level = 0
def main():
img,num_x_patches,num_y_patches = open_slide()
logging.debug('img: {}, num_x_patches = {}, num_y_patches: {}'.format(img,num_x_patches,num_y_patches))
for x in range(num_x_patches):
for y in range(num_y_patches):
img_data = img.read_region((x*args.patch_size,y*args.patch_size),level, (args.patch_size, args.patch_size))
print_pics(x*args.patch_size,y*args.patch_size,img_data,img)
pc_uninformative = number_of_useful_regions/(num_x_patches*num_y_patches)*100
pc_uninformative = round(pc_uninformative,2)
logging.info('Completed patch extraction of {} images.'.format(number_of_useful_regions))
logging.info('{}% of the image is uninformative\n'.format(pc_uninformative))
def print_pics(x_top_left,y_top_left,img_data,img):
if x_top_left % 100 == 0 and y_top_left % 100 == 0 and x_top_left != 0:
pc_complete = round(x_top_left /img.level_dimensions[0][0],2) * 100
logging.info('{:.2f}% Complete at {}'.format(pc_complete,strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())))
exit()
img_data_np = np.array(img_data)
""" Convert to grayscale"""
grey_img = rgb2gray(img_data_np)
if np.mean(grey_img) < mean_grey_values:
logging.debug('Image grayscale = {} compared to threshold {}'.format(np.mean(grey_img),mean_grey_values))
global number_of_useful_regions
number_of_useful_regions += 1
wsi_base = os.path.basename(wsi)
wsi_base = wsi_base.split('.')[0]
img_name = wsi_base + "_" + str(x_top_left) + "_" + str(y_top_left) + "_" + str(args.patch_size)
#write_img_rotations(img_data_np,img_name)
logging.debug('Saving {} {} {}'.format(x_top_left,y_top_left,np.mean(grey_img)))
save_image(img_data_np,1,img_name)
def gen_x_and_y(xlist,ylist,img):
for x in xlist:
for y in ylist:
img_data = img.read_region((x*args.patch_size,y*args.patch_size),level, (args.patch_size, args.patch_size))
yield (x, y,img_data)
def open_slide():
"""
The first level is always the main image
Get width and height tuple for the first level
"""
logging.debug('img: {}'.format(wsi))
img = openslide.OpenSlide(wsi)
img_dim = img.level_dimensions[0]
"""
Determine what the patch size should be, and how many iterations it will take to get through the WSI
"""
num_x_patches = int(math.floor(img_dim[0] / args.patch_size))
num_y_patches = int(math.floor(img_dim[1] / args.patch_size))
remainder_x = img_dim[0] % num_x_patches
remainder_y = img_dim[1] % num_y_patches
logging.debug('The WSI shape is {}'.format(img_dim))
logging.debug('There are {} x-patches and {} y-patches to iterate through'.format(num_x_patches,num_y_patches))
return img,num_x_patches,num_y_patches
def validate_dir_exists():
if os.path.isdir(outname) == False:
os.mkdir(outname)
logging.debug('Validated {} directory exists'.format(outname))
if os.path.exists(wsi):
logging.debug('Found the file {}'.format(wsi))
else:
logging.debug('Could not find the file {}'.format(wsi))
exit()
def rgb2gray(rgb):
"""Converts an RGB image into grayscale """
r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
return gray
def save_image(img,j,img_name):
tmp = os.path.join(outname,img_name+"_"+str(j)+".png")
try:
im = Image.fromarray(img)
im.save(tmp)
except:
print('Could not print {}'.format(tmp))
exit()
if __name__ == '__main__':
validate_dir_exists()
main()
Secondly, generate the probability values of each patches.
Finally, replace all the pixel values within a coordinates with the corresponding probability values and display the results using color maps.
This is the basic idea of generating heat map on WSIs. You can modify the code and concept to get a heat map as per your wish.
We have developed a python package for processing whole-slide-images:
https://github.com/amirakbarnejad/PyDmed
Here is a tutorial for getting heatmaps for whole-slide-images:
https://amirakbarnejad.github.io/Tutorial/tutorial_section5.html.
Also here is a sample notebook that gets heatmaps for WSIs using PyDmed:
Link to the sample notebook.
The benefit of PyDmed is that it is multi-processed. The dataloader sends a stream of patches to GPU(s), and the StreamWriter writes to disk in a separate process. Therefore, it is highly efficient. The running time of course depends on the machine, the size of WSIs, etc. On a good machine with a good GPU, PyDmed can generate heatmaps for ~120 WSIs in one day.

How to use a slider callback to filter a ColumnDataSource in Bokeh using Python 3?

I'm trying to use a slider with a callback in Bokeh using Python 3 to filter the rows of my ColumnDataSource objects (which originate from a DataFrame). More specifically, if a slider with options of 0 to 10000000 (in multiples of 1 million) returns a value N of say 2000000, then I want my plot to only show the data for, in this case, US counties where the population is >= 2000000. Below is my code. Everything works as I want it to except for the slider callback.
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models import HoverTool, ColumnDataSource, Select, Slider
from bokeh.plotting import figure
TOOLS='pan,wheel_zoom,box_zoom,reset,tap,save,box_select,lasso_select'
source1 = ColumnDataSource(df[df.winner == 'Democratic'])
source2 = ColumnDataSource(df[df.winner == 'Republican'])
hover = HoverTool(
tooltips = [
('County Name', '#county'),
('Population', '#population'),
('Land Area', '#land_area'),
('Pop. Density', '#density'),
('Winning Party', '#winner'),
('Winning Vote %', '#winning_vote_pct'),
]
)
# Plot
plot = figure(plot_width=800, plot_height=450, tools=[hover, TOOLS],
title='2016 US Presidential Vote % vs. Population Density (by County)',
x_axis_label='Vote %', y_axis_label='Population Density (K / sq. mi.)')
y = 'density'
size = 'bokeh_size'
alpha = 0.5
c1 = plot.circle(x='pct_d', y=y, size=size, alpha=alpha, color='blue',
legend='Democratic-Won County', source=source1)
c2 = plot.circle(x='pct_r', y=y, size=size, alpha=alpha, color='red',
legend='Republican-Won County', source=source2)
plot.legend.location = 'top_left'
# Select widget
party_options = ['Show both parties', 'Democratic-won only', 'Republican-won only']
menu = Select(options=party_options, value='Show both parties')
# Slider widget
N = 2000000
slider = Slider(start=0, end=10000000, step=1000000, value=N, title='Population Cutoff')
# Select callback
def select_callback(attr, old, new):
if menu.value == 'Democratic-won only': c1.visible=True; c2.visible=False
elif menu.value == 'Republican-won only': c1.visible=False; c2.visible=True
elif menu.value == 'Show both parties': c1.visible=True; c2.visible=True
menu.on_change('value', select_callback)
# Slider callback
def slider_callback(attr, old, new):
N = slider.value
# NEED HELP HERE...
source1 = ColumnDataSource(df.loc[(df.winner == 'Democratic') & (df.population >= N)])
source2 = ColumnDataSource(df.loc[(df.winner == 'Republican') & (df.population >= N)])
slider.on_change('value', slider_callback)
# Arrange plots and widgets in layouts
layout = layout([menu, slider],
[plot])
curdoc().add_root(layout)
Here is a solution using CustomJSFilter and CDSView as suggest in the other answer by Alex. It does not directly use the data as supplied in the question, but is rather a general hint how this can be implemented:
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider, CustomJSFilter, CDSView
from bokeh.plotting import Figure, show
import numpy as np
# Create some data to display
x = np.arange(200)
y = np.random.random(size=200)
source = ColumnDataSource(data=dict(x=x, y=y))
plot = Figure(plot_width=400, plot_height=400)
# Create the slider that modifies the filtered indices
# I am just creating one that shows 0 to 100% of the existing data rows
slider = Slider(start=0., end=1., value=1., step=.01, title="Percentage")
# This callback is crucial, otherwise the filter will not be triggered when the slider changes
callback = CustomJS(args=dict(source=source), code="""
source.change.emit();
""")
slider.js_on_change('value', callback)
# Define the custom filter to return the indices from 0 to the desired percentage of total data rows. You could also compare against values in source.data
js_filter = CustomJSFilter(args=dict(slider=slider, source=source), code=f"""
desiredElementCount = slider.value * 200;
return [...Array(desiredElementCount).keys()];
""")
# Use the filter in a view
view = CDSView(source=source, filters=[js_filter])
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6, view=view)
layout = column(slider, plot)
show(layout)
I hope this helps anyone who stumbles upon this in the future! Tested in bokeh 1.0.2
A quick solution with minimal change to your code would be:
def slider_callback(attr, old, new):
N = new # this works also with slider.value but new is more explicit
new1 = ColumnDataSource(df.loc[(df.winner == 'Democratic') & (df.population >= N)])
new2 = ColumnDataSource(df.loc[(df.winner == 'Republican') & (df.population >= N)])
source1.data = new1.data
source2.data = new2.data
When updating data sources, you should replace the data, not the whole object. Here I still create new ColumnDataSource as shortcut. A more direct way (but more verbose too) would be to create the dictionary from the filtered df's columns:
new1 = {
'winner': filtered_df.winner.values,
'pct_d': filtered_df.pct_d.values,
...
}
new2 = {...}
source1.data = new1
source2.data = new2
Note that there's another solution which would make the callback local (not server based) by using a CDSView with a CustomJSFilter. You can also write the other callback with a CDSView as well make the plot completely server-independent.

Matplotlib animation.FuncAnimation: Custom frame generator only yields once

I'm encountering a strange problem with the matplotlib animation. I'm trying to create a animated bar plot using the following code:
import os, time
from PIL import Image, ImageSequence
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path
import matplotlib.animation as animation
import blackxample
FILE_PREFIX = "cell-isotohyper"
FILE_SUFFIX = ".tif"
FILE_PATH = "./example-video"
XCUT = (91, 91+266)
YCUT = (646, 646+252)
LIMIT = 100
OFFSET = 0
Y_SCALE = 3000
NUM_OF_BINS = 37
BAR_WIDTH = 1.0
BAR_COLOR = 'b'
RANGE = range(0, NUM_OF_BINS//2+1)
def animate(i, fig, ax, bars):
# = np.random.randn(1000)
print(len(i))
for a in RANGE:
bars[a].set_height(i[a])
return (fig, ax, bars)
def main():
fig, ax = plt.subplots()
ax.set_ylim(0, Y_SCALE)
ax.set_xlim(0, NUM_OF_BINS//2+1)
bars = ax.bar(np.arange(NUM_OF_BINS), [i for i in range(NUM_OF_BINS)], BAR_WIDTH, color=BAR_COLOR)
ani = animation.FuncAnimation(fig, animate, xframes, fargs = (fig, ax, bars), interval=500)
plt.show()
This code snippet works completely fine if I'm using randomly generated data or constant via:
def xframes():
i = 0
while i < 100:
yield [2312.7094266223335, 27.238786592368257, 75.252063484372513, 13.678304922077643, 11.879804374653929, 21.900570139020687, 2.930771773796323, 11.945594479736741, 10.88517941461987, 4.4176609254771506, 4.1075871395528338, 1.248363771876285, 1.4798157379442216, 3.5285036346353564, 3.2583080973651732, 3.4640042567344267, 3.130503535456981, 0.67334205875304676, 0.71393606581800562]
#yield np.histogram(np.random.randn(1000), NUM_OF_BINS//2 + 1)[0]
i+=1
Using the function, aframes, instead, does only yield the first item if it is used together animation.FuncAnimation(). If aframe is iterated manually, however, the generator works completely fine.
def aframes():
list_of_files = []
for dirname, dirnames, filenames in os.walk(FILE_PATH):
for filename in filenames:
if filename.startswith(FILE_PREFIX) and filename.endswith(FILE_SUFFIX):
list_of_files.append(os.path.join(FILE_PATH, filename))
# Open every picture - in every file
count = 0
imagecount = 0
framecount = 0
skipped = 0
for file in list_of_files:
framecount = 0
a = Image.open(file)
for frame in ImageSequence.Iterator(a):
if count > OFFSET and count <= OFFSET+LIMIT:
# Cut image beforehand - probably faster
frame = frame.crop((XCUT[0], YCUT[0], XCUT[1], YCUT[1]))
# Load image intro Matrix
imageMatrix = blackxample.Matrix.fromPillow(frame)
try:
imageMatrix.findContour()
imageMatrix.calculateCentroid()
imageMatrix.transform(NUM_OF_BINS)
#yield imageMatrix.getTransform()
yield [2312.7094266223335, 27.238786592368257, 75.252063484372513, 13.678304922077643, 11.879804374653929, 21.900570139020687, 2.930771773796323, 11.945594479736741, 10.88517941461987, 4.4176609254771506, 4.1075871395528338, 1.248363771876285, 1.4798157379442216, 3.5285036346353564, 3.2583080973651732, 3.4640042567344267, 3.130503535456981, 0.67334205875304676, 0.71393606581800562]
except blackxample.NoConvergenceError:
skipped+=1
print("[", count ,"] done")
framecount+=1
count+=1
imagecount+=1
# Test for frame iterator - works fine
#for i in _frames():
# print(i)
Does someone has a clue what and why is happening? How can I fix it?
The generator also runs as expected if the three imageMatrix-lines inside the try-block are commented out which suggests that there is an error inside imageMatrix.findContour(). But what am I looking for? findContour doesn't do anything weird
Since I have not found any solutions regarding this problem, I've decided to save the result of aframes() in a file, then reading and animating it seperatly which works flawlessly without adjusting the animation code.

how to update a matplotlib heatmap plot without creating a new window

I have matrix class that inherits from list. This class can display itself as a matplotlib heatmap representation of the matrix.
I'm trying to have the class written such that when I change values in the matrix, I can call the matrix's method plot() and it'll update the plot to reflect the matrix changes in the heatmap.
However, every time I run the method plot(), it creates a new heatmap in a new window instead of updating the existing plot. How could I get it simply to update the existing plot?
In the code below, there are three main parts: the main function shows how an instance of the matrix class is created, plotted and updated; the matrix class is basically a list object, with some minor functionality (including plotting) bolted on; the function plotList() is the function the matrix class calls in order to generate the plot object initially.
import time
import random
import matplotlib.pyplot as plt
plt.ion()
import numpy as np
def main():
print("plot 2 x 2 matrix and display it changing in a loop")
matrix = Matrix(
numberOfColumns = 2,
numberOfRows = 2,
randomise = True
)
# Plot the matrix.
matrix.plot()
# Change the matrix, redrawing it after each change.
for row in range(len(matrix)):
for column in range(len(matrix[row])):
input("Press Enter to continue.")
matrix[row][column] = 10
matrix.plot()
input("Press Enter to terminate.")
matrix.closePlot()
class Matrix(list):
def __init__(
self,
*args,
numberOfColumns = 3,
numberOfRows = 3,
element = 0.0,
randomise = False,
randomiseLimitLower = -0.2,
randomiseLimitUpper = 0.2
):
# list initialisation
super().__init__(self, *args)
self.numberOfColumns = numberOfColumns
self.numberOfRows = numberOfRows
self.element = element
self.randomise = randomise
self.randomiseLimitLower = randomiseLimitLower
self.randomiseLimitUpper = randomiseLimitUpper
# fill with default element
for column in range(self.numberOfColumns):
self.append([element] * self.numberOfRows)
# fill with pseudorandom elements
if self.randomise:
random.seed()
for row in range(self.numberOfRows):
for column in range(self.numberOfColumns):
self[row][column] = random.uniform(
self.randomiseLimitUpper,
self.randomiseLimitLower
)
# plot
self._plot = plotList(
list = self,
mode = "return"
)
# for display or redraw plot behaviour
self._plotShown = False
def plot(self):
# display or redraw plot
self._plot.draw()
if self._plotShown:
#self._plot = plotList(
# list = self,
# mode = "return"
# )
array = np.array(self)
fig, ax = plt.subplots()
heatmap = ax.pcolor(array, cmap = plt.cm.Blues)
self._plot.draw()
else:
self._plot.show()
self._plotShown = True
def closePlot(self):
self._plot.close()
def plotList(
list = list,
mode = "plot" # plot/return
):
# convert list to NumPy array
array = np.array(list)
# create axis labels
labelsColumn = []
labelsRow = []
for rowNumber in range(0, len(list)):
labelsRow.append(rowNumber + 1)
for columnNumber in range(0, len(list[rowNumber])):
labelsColumn.append(columnNumber)
fig, ax = plt.subplots()
heatmap = ax.pcolor(array, cmap = plt.cm.Blues)
# display plot or return plot object
if mode == "plot":
plt.show()
elif mode == "return":
return(plt)
else:
Exception
if __name__ == '__main__':
main()
I'm using Python 3 in Ubuntu.
The method plot(self) creates a new figure in the line fig, ax = plt.subplots(). To use an existing figure you can give your figure a number or name when it's first created in plotList():
fig = plt.figure('matrix figure')
ax = fig.add_subplot(111)
then use
plt.figure('matrix figure')
ax = gca() # gets current axes
to make that the active figure and axes. Alternately, you might want to the figure and axis created in plotList and pass them to plot.

Resources