143 lines
4.8 KiB
Python
143 lines
4.8 KiB
Python
import numpy as np
|
|
|
|
from qtpy.QtGui import QPainter, QColor
|
|
from qtpy.QtWidgets import QWidget, QGridLayout, QFrame
|
|
|
|
from .util import histograms
|
|
|
|
|
|
class ColorHistogram(QWidget):
|
|
'''A Class which draws a scaling histogram in
|
|
a widget.
|
|
|
|
Where counts are the bin values in the histogram
|
|
and colormap is a tuple of (R, G, B) tuples the same length
|
|
as counts. These are the colors to apply to the histogram bars.
|
|
Colormap can also contain a single tuple (R, G, B), in which case this is
|
|
the color applied to all bars of that histogram.
|
|
|
|
The histogram assumes the bins were evenly spaced.
|
|
'''
|
|
|
|
def __init__(self, counts, colormap):
|
|
QWidget.__init__(self)
|
|
self._validate_input(counts, colormap)
|
|
self.counts = counts
|
|
self.n = np.sum(self.counts)
|
|
self.colormap = colormap
|
|
self.setMinimumSize(100, 50)
|
|
|
|
def _validate_input(self, counts, colormap):
|
|
if len(counts) != len(colormap):
|
|
if len(colormap) != 3:
|
|
msg = '''Colormap must be a list of 3-tuples the same
|
|
length as counts or a 3-tuple'''
|
|
raise ValueError(msg)
|
|
|
|
def paintEvent(self, evt):
|
|
# get the widget dimensions
|
|
orig_width = self.width()
|
|
orig_height = self.height()
|
|
|
|
# fill perc % of the widget
|
|
perc = 1
|
|
width = int(orig_width * perc)
|
|
height = int(orig_height * perc)
|
|
|
|
# get the starting origin
|
|
x_orig = int((orig_width - width) / 2)
|
|
# we want to start at the bottom and draw up.
|
|
y_orig = orig_height - int((orig_height - height) / 2)
|
|
|
|
# a running x-position
|
|
running_pos = x_orig
|
|
|
|
# calculate to number of bars
|
|
nbars = len(self.counts)
|
|
|
|
# calculate the bar widths, this compilcation is
|
|
# necessary because integer trunction severely cripples
|
|
# the layout.
|
|
remainder = width % nbars
|
|
bar_width = [int(width / nbars)] * nbars
|
|
for i in range(remainder):
|
|
bar_width[i] += 1
|
|
|
|
paint = QPainter()
|
|
paint.begin(self)
|
|
|
|
# determine the scaling factor
|
|
max_val = np.max(self.counts)
|
|
scale = 1. * height / max_val
|
|
|
|
# determine if we have a colormap and drop into the appropriate
|
|
# loop.
|
|
if hasattr(self.colormap[0], '__iter__'):
|
|
# assume we have a colormap
|
|
for i in range(len(self.counts)):
|
|
bar_height = self.counts[i]
|
|
r, g, b = self.colormap[i]
|
|
paint.setPen(QColor(r, g, b))
|
|
paint.setBrush(QColor(r, g, b))
|
|
paint.drawRect(running_pos, y_orig, bar_width[i],
|
|
-bar_height)
|
|
running_pos += bar_width[i]
|
|
|
|
else:
|
|
# we have a tuple
|
|
r, g, b = self.colormap
|
|
paint.setPen(QColor(r, g, b))
|
|
paint.setBrush(QColor(r, g, b))
|
|
for i in range(len(self.counts)):
|
|
bar_height = self.counts[i] * scale
|
|
paint.drawRect(running_pos, y_orig, bar_width[i],
|
|
-bar_height)
|
|
running_pos += bar_width[i]
|
|
|
|
paint.end()
|
|
|
|
def update_hist(self, counts, cmap):
|
|
self._validate_input(counts, cmap)
|
|
self.counts = counts
|
|
self.colormap = cmap
|
|
self.repaint()
|
|
|
|
|
|
class QuadHistogram(QFrame):
|
|
'''A class which uses ColorHistogram to draw
|
|
the 4 histograms of an image. R, G, B, and Value.
|
|
|
|
The 4 histograms are layout out in a grid,
|
|
and can be specified horizontal or vertical,
|
|
and in which order ie. ['R', 'G', 'B', 'V']
|
|
'''
|
|
|
|
def __init__(self, img, layout='vertical', order=['R', 'G', 'B', 'V']):
|
|
QFrame.__init__(self)
|
|
|
|
r, g, b, v = histograms(img, 100)
|
|
self.r_hist = ColorHistogram(r, (255, 0, 0))
|
|
self.g_hist = ColorHistogram(g, (0, 255, 0))
|
|
self.b_hist = ColorHistogram(b, (0, 0, 255))
|
|
self.v_hist = ColorHistogram(v, (0, 0, 0))
|
|
|
|
self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
|
|
self.layout = QGridLayout(self)
|
|
self.layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
order_map = {'R': self.r_hist, 'G': self.g_hist, 'B': self.b_hist,
|
|
'V': self.v_hist}
|
|
|
|
if layout == 'vertical':
|
|
for i in range(len(order)):
|
|
self.layout.addWidget(order_map[order[i]], i, 0)
|
|
elif layout == 'horizontal':
|
|
for i in range(len(order)):
|
|
self.layout.addWidget(order_map[order[i]], 0, i)
|
|
|
|
def update_hists(self, img):
|
|
r, g, b, v = histograms(img, 100)
|
|
self.r_hist.update_hist(r, (255, 0, 0))
|
|
self.g_hist.update_hist(g, (0, 255, 0))
|
|
self.b_hist.update_hist(b, (0, 0, 255))
|
|
self.v_hist.update_hist(v, (0, 0, 0))
|