Python >> matplotlib: Get the number of lines on a current plot? - python-2.7

Is there a way to get the number of lines currently on a matplotlib plot? I find myself setting colors in a colormap using a counter and multiplier to step through the color values--which seems rather un-pythonic.

All the Line2D objects in an axes are stored into a list
ax.lines
If you use only simple line plots, the lenght of the above list is enough.
If you use plt.errorbar the situation is a bit more complicated, as it creates multiple Line2D objects (central lines, vertical and horizontal error bars and their caps).
If you want to automatise the colours to assign to lines you can create a cycle like this
import itertools as it
colors = it.cycle(list of colors)
and then call the next color with colors.next() and restart from the first after it gets to the last

Related

connected component labeling in python

How to implement connected component labeling in python with open cv?
This is an image example:
I need connected component labeling to separate objects on a black and white image.
The OpenCV 3.0 docs for connectedComponents() don't mention Python but it actually is implemented. See for e.g. this SO question. On OpenCV 3.4.0 and above, the docs do include the Python signatures, as can be seen on the current master docs.
The function call is simple: num_labels, labels_im = cv2.connectedComponents(img) and you can specify a parameter connectivity to check for 4- or 8-way (default) connectivity. The difference is that 4-way connectivity just checks the top, bottom, left, and right pixels and sees if they connect; 8-way checks if any of the eight neighboring pixels connect. If you have diagonal connections (like you do here) you should specify connectivity=8. Note that it just numbers each component and gives them increasing integer labels starting at 0. So all the zeros are connected, all the ones are connected, etc. If you want to visualize them, you can map those numbers to specific colors. I like to map them to different hues, combine them into an HSV image, and then convert to BGR to display. Here's an example with your image:
import cv2
import numpy as np
img = cv2.imread('eGaIy.jpg', 0)
img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1] # ensure binary
num_labels, labels_im = cv2.connectedComponents(img)
def imshow_components(labels):
# Map component labels to hue val
label_hue = np.uint8(179*labels/np.max(labels))
blank_ch = 255*np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])
# cvt to BGR for display
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
# set bg label to black
labeled_img[label_hue==0] = 0
cv2.imshow('labeled.png', labeled_img)
cv2.waitKey()
imshow_components(labels_im)
My adaptation of the CCL in 2D is:
1) Convert the image into a 1/0 image, with 1 being the object pixels and 0 being the background pixels.
2) Make a 2 pass CCL algorithm by implementing the Union-Find algorithm with pass compression. You can see more here.
In the First pass in this CCL implementation, you check the neighbor pixels, in the case your target pixel is an object pixel, and compare their label between them so that you can generate equivalences between them. You assign the least label, of those neighbor pixels which are objects pixels (label>0) to your target pixel. In this way, you are not only assigning an object label to your target pixesl (label>0) but also creating a list of equivalences.
2) In the second pass, you go through all the pixels, and change their previous label by the label of its parent label by just looking into the equivalent table stored in your Union-Find class.
3)I implemented an additional pass to make the labels follow a sequential order (1,2,3,4....) instead of a random order (23,45,1,...). That involves changing the labels "name" just for aesthetic purposes.

Creating a colorbar next to my matplotlib plot w/o using the colormap to plot the data

I'm having trouble creating a colorbar for my plot in Python using matplotlib. I am using a colormap, not to colour all the data that I plot but to extract a colour for a plot based on a value I'm not plotting. Hope this makes sense..
So I'm in a for loop, create a plot every time with a colour based on a certain parameter. Like this (the data is an example to create an mwe, my data is more complicated):
import matplotlib as mpl
from matplotlib import pyplot as plt
import numpy as np
xdata = np.array(range(10))
parameter = [0.5, 0.3, 0.78, 0.21, 0.45] #random parameter example
cmap = mpl.cm.get_cmap('jet')
for i in range(len(parameter)):
clr = cmap(parameter(i))
plt.plot(xdata,xdata**i,c=clr)
plt.show()
Now, what I would want is a colorbar on the side (or actually two, but that's another problem I think) that shows the jet colormap and according values. The values need to be scaled to a new min and max value.
So far I've found the following, but I don't understand it enough to apply it to my own problem:
Getting individual colors from a color map in matplotlib
Which told me how to extract the colour and shows how to create the normalized colormap
Colorbar only
Which should tell me how to add a colorbar without using the plotted data, but I don't understand enough of it. My problem is with the creation of the axes. I don't understand this part if I want to put the colorbar next to my plot. In the example they create a figure with handle fig, but in my case the figure is created when I do plt.imshow(image), since this is what I start with and then I'm plotting over the image. I cannot use the fig.add_axes here.
I hope you can help me out here. It would be great if I could also create a 'reversed' colorbar. So either the colours are in reverse direction, or the values next to the bar.
At any point in the script you can get the figure via fig = plt.gcf() and an axes via ax=plt.gca(). So, adding an axes may be done by plt.gcf().add_axes(...).
There is also nothing wrong with putting fig=plt.figure() before plotting anything.
Note that after creating a new axes, plt.gca() will return the new axes, so it is a good idea to create a reference to the main axes before adding a new one.
A convenient way to obtain a figure and an axes for later referencing is to create the figure via
fig, ax = plt.subplots()
Colormaps:
Every standard colormap has a reversed version, which has _r at the end of its name, e.g. you can use viridis_r instead of viridis.

RRD Graph - Change line colour by value

I have a RRD database with data:
"DS:pkts_transmitted:GAUGE:120:0:U",
"DS:pkts_received:GAUGE:120:0:U",
"DS:pkts_lost:GAUGE:120:0:U",
"DS:rtt_min:GAUGE:120:0:U",
"DS:rtt_avg:GAUGE:120:0:U",
"DS:rtt_max:GAUGE:120:0:U",
And I want that the Avg line change colour if I lose any package.
For example, if I lose 5 packets make the line blue, if I lose 10 make it red.
I see people doing it but I read the documentation and I can't find how to do this.
The way to do this is to actually have multiple lines defined (one of each colour) and hide the ones you don't want to see at any time, using calculations.
For example, say we have an RRD with two DSs:
DS:x:GAUGE:60:0:U
DS:y:GAUGE:60:0:1
Now, we want to show the line for x in red if y is 0, and blue if it is 1. To do this, we create two calculated values, x1 and x2.
CDEF:x1=y,0,EQ,x,UNKN,IF
CDEF:x2=y,1,EQ,x,UNKN,IF
Thus, x1 is active if y=0 and x2 if y=1. Yes, this could be simplified, but I'm showing it like this for the example.
Now, we can make lines using these:
LINE:x1#ff0000:MyLine
LINE:x2#0000ff
Note that the second line doesn't need a legend. Now, the line will appear to change colour depending on the value of the y metric, since at any time the other line will be UNKN and therefore not displayed.
You can extend this, of course, to have multiple colours and more complex thresholds.

Pyplot rotated labels offset by one

Just getting into matplot lib and running into odd problem - I'm trying to plot 10 items, and use their names on the x-axis. I followed this suggestion and it worked great, except that my label names are long and they were all scrunched up. So I found that you can rotate labels, and got the following:
plt.plot([x for x in range(len(df.columns))], df[df.columns[0]], 'ro',)
plt.xticks(range(10), df.columns, rotation=45)
The labels all seem to be off by a tick ("Arthrobacter" should be aligned with 0). So I thought my indexing was wrong, and tried a bunch of other crap to fix it, but it turns out it's just odd (at least to me) behavior of the rotation. If I do rotation='vertical', I get what I want:
I see now that the center of the labels are clearly aligned with the ticks, but I expected that they'd terminate on the ticks. Like this (done in photoshop):
Is there a way to get this done automatically?
The labels are not "off", labels are actually placed via their "center". In your second image, the corresponding tick is above the center of the label, not above its endpoint. You can change that by adding ha='right' which modifies the horizontal alignement of the label.
plt.plot([x for x in range(len(df.columns))], df[df.columns[0]], 'ro',)
plt.xticks(range(10), df.columns, rotation=45, ha='right')
See the comparison below :
1)
plt.plot(np.arange(4), np.arange(4))
plt.xticks(np.arange(4), ['veryverylongname']*4, rotation=45)
plt.tight_layout()
2)
plt.plot(np.arange(4), np.arange(4))
plt.xticks(np.arange(4), ['veryverylongname']*4, rotation=45, ha='right')
plt.tight_layout()

SimpleCV: Removing lines I previously drew?

I'm trying to do edge detection with SimpleCV on a RasPi by first finding all the lines in an image and then filtering items set based on location, intersect angle and color. I have the filtering figured out, but am having difficulty displaying the image with the filtered lines drawn in.
Currently I am can draw the full line set with
handle_lin = my_lines_full.draw()
handle_img = some_image.show()
and the filtered line set independently with
handle_lin = my_lines_filtered.draw()
handle_img = some_image.show()
but since this method also displays the full line set, no difference is seen when I do them in the same script. Whats the best way to erase the layer that stores the line drawings or selectively remove elements of the drawing?
Sovled(-ish):
Seems as though the some_lines.draw() command toggles line sets so by repeating the .draw() command before updating the lines set I can clear the layer that is displayed on the image.