Automatic annotation for yolo not working - yolo

I am trying to generate some annotation for image files that I have created for training , I am pasting object image on the top of background image and getting the x,y coordinates of the location where the object image is pasted ,
The bounding box for the pasted object is calculated as (x, (x+w), y , (y+h))
box = (x, (w+w), y , (y+h)) # w,h are width and height of the object image
I am converting this to yolo annotation using this function :
def convert_boxes_to_yolo(box, frame):
# frame is a tuple containing background image width and height
# x = box[0][0]
# y = box[0][1]
# w = box[1][0] - box[0][0]
# h = box[1][1] - box[0][1]
x,y,w,h = box
print( frame.shape)
xc = float((x + w/2.0) / frame.shape[1])
yc = float((y + h/2.0) / frame.shape[0])
wc = float(w / frame.shape[1])
hc = float(h / frame.shape[0])
return (str(xc), str(yc), str(wc), str(hc))
and using this function to plot the bounding box , which looks correct :
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('Omen_6_image_generated.png')
dh, dw, _ = img.shape
#dh, dw = (35, 400)
fl = open('Omen_6_image_generated.txt', 'r')
data = fl.readlines()
fl.close()
for dt in data:
# Split string to float
_, x, y, w, h = map(float, dt.split())
# Taken from https://github.com/pjreddie/darknet/blob/810d7f797bdb2f021dbe65d2524c2ff6b8ab5c8b/src/image.c#L283-L291
# via https://stackoverflow.com/questions/44544471/how-to-get-the-coordinates-of-the-bounding-box-in-yolo-object-detection#comment102178409_44592380
l = int((x - w / 2) * dw)
r = int((x + w / 2) * dw)
t = int((y - h / 2) * dh)
b = int((y + h / 2) * dh)
if l < 0:
l = 0
if r > dw - 1:
r = dw - 1
if t < 0:
t = 0
if b > dh - 1:
b = dh - 1
cv2.rectangle(img, (l, t), (r, b), (0, 0, 255), 1)
image = Image.fromarray(img.astype('uint8'), 'RGB')
image.show()
The bounding box is plotted correctly but the online annotation tools are not able to parse the file.
For example the plotting code correctly plots the bounding box for the shared image and annotation file below but the AI annotation tool like https://www.makesense.ai/ is not able to parse it , also if you look the same image in labelImg results look wrong.
link to both image and yolo_file:
https://drive.google.com/drive/folders/13ZTVrzswtcvXRBo6kJAhiITxx-IzOi-_?usp=sharing

Related

I am not able to render 2D images of 3D point cloud

I am trying to render 2D images of point clouds from different viewpoints and save them as images.
I found a code online which does the same thing but for meshes. I tweaked it a little bit to import the 3D point cloud. But the code does not work and gives back black images. Please help me with this. I am open to use another library too if you know the solution. I just want to render the 2D images. Thank You
Code:
import os.path
import math
import sys
C = bpy.context
D = bpy.data
scene = D.scenes['Scene']
# cameras: a list of camera positions
# a camera position is defined by two parameters: (theta, phi),
# where we fix the "r" of (r, theta, phi) in spherical coordinate system.
# 5 orientations: front, right, back, left, top
cameras = [
(60, 0), (60, 90), (60, 180), (60, 270),
(0, 0)
]
# 12 orientations around the object with 30-deg elevation
# cameras = [(60, i) for i in range(0, 360, 30)]
render_setting = scene.render
# output image size = (W, H)
w = 500
h = 500
render_setting.resolution_x = w
render_setting.resolution_y = h
def main():
argv = sys.argv
argv = argv[argv.index('--') + 1:]
if len(argv) != 2:
print('phong.py args: <3d mesh path> <image dir>')
exit(-1)
model = argv[0]
image_dir = argv[1]
# blender has no native support for off files
# install_off_addon()
# init_camera()
fix_camera_to_origin()
do_model(model, image_dir)
def install_off_addon():
try:
# bpy.ops.preferences.addon_install(
# overwrite=False,
# filepath=os.path.dirname(__file__) +
# '/blender-off-addon/import_off.py'
# )
bpy.ops.preferences.addon_enable(module='import_off')
except Exception as e:
print(e)
print("""Import blender-off-addon failed.
Did you pull the blender-off-addon submodule?
$ git submodule update --recursive --remote
""")
exit(-1)
def init_camera():
cam = D.objects['Camera']
# select the camera object
scene.objects.active = cam
cam.select = True
# set the rendering mode to orthogonal and scale
C.object.data.type = 'ORTHO'
C.object.data.ortho_scale = 2.
def fix_camera_to_origin():
origin_name = 'Origin'
# create origin
try:
origin = D.objects[origin_name]
except KeyError:
bpy.ops.object.empty_add(type='SPHERE')
D.objects['Empty'].name = origin_name
origin = D.objects[origin_name]
origin.location = (0, 0, 0)
cam = D.objects['Camera']
# scene.objects.active = cam
# cam.select = True
if 'Track To' not in cam.constraints:
bpy.ops.object.constraint_add(type='TRACK_TO')
cam.constraints['Track To'].target = origin
cam.constraints['Track To'].track_axis = 'TRACK_NEGATIVE_Z'
cam.constraints['Track To'].up_axis = 'UP_Y'
def do_model(path, image_dir):
name = load_model(path)
center_model(name)
normalize_model(name)
image_subdir = os.path.join(image_dir, name)
for i, c in enumerate(cameras):
move_camera(c)
render()
save(image_subdir, '%s.%d' % (name, i))
# delete_model(name)
def load_model(path):
d = os.path.dirname(path)
ext = path.split('.')[-1]
name = os.path.basename(path).split('.')[0]
# handle weird object naming by Blender for stl files
if ext == 'stl':
name = name.title().replace('_', ' ')
if name not in D.objects:
print('loading :' + name)
if ext == 'stl':
bpy.ops.import_mesh.stl(filepath=path, directory=d,
filter_glob='*.stl')
elif ext == 'off':
bpy.ops.import_mesh.off(filepath=path, filter_glob='*.off')
elif ext == 'obj':
bpy.ops.import_scene.obj(filepath=path, filter_glob='*.obj')
else:
bpy.ops.import_mesh.ply(filepath=path, filter_glob='*.ply')
return name
def delete_model(name):
for ob in scene.objects:
if ob.type == 'MESH' and ob.name.startswith(name):
ob.select = True
else:
ob.select = False
bpy.ops.object.delete()
def center_model(name):
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN')
D.objects[name].location = (0, 0, 0)
def normalize_model(name):
obj = D.objects[name]
dim = obj.dimensions
print('original dim:' + str(dim))
if max(dim) > 0:
dim = dim / max(dim)
obj.dimensions = dim
print('new dim:' + str(dim))
def move_camera(coord):
def deg2rad(deg):
return deg * math.pi / 180.
r = 3.
theta, phi = deg2rad(coord[0]), deg2rad(coord[1])
loc_x = r * math.sin(theta) * math.cos(phi)
loc_y = r * math.sin(theta) * math.sin(phi)
loc_z = r * math.cos(theta)
D.objects['Camera'].location = (loc_x, loc_y, loc_z)
def render():
bpy.ops.render.render()
def save(image_dir, name):
path = os.path.join(image_dir, name + '.png')
D.images['Render Result'].save_render(filepath=path)
print('save to ' + path)
if __name__ == '__main__':
main()

vectorising images using matrix multiplication

I want to apply the homography matrix (3x3) to an image in order to convert it in the frame of reference of another image. so to create a panorama effect. Currently I have a working code that involves a nested for loop which iterates over all the pixel array and applies the transform the the vector of 3x1 pixels. I would like speed up the code and replace the nested for loop with a matrix multiplication so that multiply all the position indices by the homography matrix in one go.
This is my current code:
H = calcBestHomography(pts1b , pts3)
for i in range(im1.shape[1]):
for j in range(im1.shape[0]):
pt1 = np.array([i, j, 1])
pt3 = H # pt1.T
px = int(pt3[0] / pt3[2])
py = int(pt3[1] / pt3[2])
if (px > 0 and px < im3.shape[1] and py> 0 and py < im3.shape[0]):
im1[j , i , : ] = im3[py , px ,:]
thank you
this is my current attempt but my main issue is that since the homography matrix is a 3x3 I cannot pass a matrix that has more than 3 columns
ax0 = np.linspace(0, im1.shape[0]-1, im1.shape[0])
ax1 = np.linspace(0, im1.shape[1]-1, im1.shape[1])
ax2 = np.ones((2740 , 880))
ax2_3d = np.expand_dims(ax2, axis=2)
pts1 = np.meshgrid(ax0 , ax1 )
pts1 = np.stack(pts1 , axis=-1)
result = np.concatenate((pts1, ax2_3d), axis=2)
pt3 = (result) # H
px = (pt3[0] / pt3[2])
py = (pt3[1] / pt3[2])

How to use trained yolov7 best.pt file as "vehicle plate detection" application in python?

I have trained a model using yolov7 https://colab.research.google.com/drive/1X9A8odmK4k6l26NDviiT6dd6TgR-piOa#scrollTo=nD-uPyQ_2jiN colab file. After training I got a file named best.pt and I want to use it as a service/application in python. There is a yolov3 example which I created. This one was using .weights so how can I use .pt?
import cv2
import numpy as np
import time
import os
def getPhoto():
ROOT_DIR = os.path.dirname(__file__)
URL = "http://192.168.1.3:4747/video"
PC_CAM = 0
net = cv2.dnn.readNet(
f"{ROOT_DIR}\\yolov3_custom_final.weights",
f"{ROOT_DIR}\\yolov3_custom.cfg",
)
classes = []
with open(f"{ROOT_DIR}\\classes.txt", "r") as f:
classes = f.read().splitlines()
timeElapsed = 0
wCam, hCam = 640, 360
font = cv2.FONT_HERSHEY_PLAIN
colors = np.random.uniform(0, 255, size=(2, 3))
cap = cv2.VideoCapture(URL)
cap.set(3, wCam)
cap.set(4, hCam)
while True:
_, img = cap.read()
cv2.imshow("Detection Screen", img)
if cv2.waitKey(1) == ord("c"):
break
height, width, _ = img.shape
blob = cv2.dnn.blobFromImage(
img, 1 / 255, (416, 416), (0, 0, 0), swapRB=True, crop=False
)
net.setInput(blob)
output_layers_names = net.getUnconnectedOutLayersNames()
layerOutputs = net.forward(output_layers_names)
boxes = []
confidences = []
class_ids = []
for output in layerOutputs:
for detection in output:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.6:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append((float(confidence)))
class_ids.append(class_id)
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.2, 0.4)
if len(indexes) > 0:
for i in indexes.flatten():
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
confidence = str(round(confidences[i], 2))
color = colors[i]
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(
img,
label + " " + confidence,
(x, y + 20),
font,
2,
(255, 255, 0),
2,
)
cv2.imwrite(f"{ROOT_DIR}\\DetectedPhoto.jpg", img)
print("Image Saved")
getPhoto()
I want to detect vehicle plates using .pt file and cut vehicle plates and save them as .jpeg.
you can do this with detect.py insided yolov7 folder. Run this:
python detect.py --weights best.pt --source image.jpg
you can download yolov7 with this method:
git clone https://github.com/WongKinYiu/yolov7.git
in yolov7 you can see detect.py

Unravel Index loops forever

I am doing some work using image processing and sparse coding. Problem is, the following code works only on some images.
Here is the image that it works perfectly on:
And here is the image that it loops forever on:
Here is the code:
import cv2
import numpy as np
import networkx as nx
from preproc import Preproc
# From https://github.com/vicariousinc/science_rcn/blob/master/science_rcn/learning.py
def sparsify(bu_msg, suppress_radius=3):
"""Make a sparse representation of the edges by greedily selecting features from the
output of preprocessing layer and suppressing overlapping activations.
Parameters
----------
bu_msg : 3D numpy.ndarray of float
The bottom-up messages from the preprocessing layer.
Shape is (num_feats, rows, cols)
suppress_radius : int
How many pixels in each direction we assume this filter
explains when included in the sparsification.
Returns
-------
frcs : see train_image.
"""
frcs = []
img = bu_msg.max(0) > 0
while True:
r, c = np.unravel_index(img.argmax(), img.shape)
print(r, c)
if not img[r, c]:
break
frcs.append((bu_msg[:, r, c].argmax(), r, c))
img[r - suppress_radius:r + suppress_radius + 1,
c - suppress_radius:c + suppress_radius + 1] = False
return np.array(frcs)
if __name__ == '__main__':
img = cv2.imread('https://i.stack.imgur.com/Nb08A.png', 0)
img2 = cv2.imread('https://i.stack.imgur.com/2MW93.png', 0)
prp = Preproc()
bu_msg = prp.fwd_infer(img)
frcs = sparsify(bu_msg)
and the accompanying preprocessing code:
"""A pre-processing layer of the RCN model. See Sec S8.1 for details.
"""
import numpy as np
from scipy.ndimage import maximum_filter
from scipy.ndimage.filters import gaussian_filter
from scipy.signal import fftconvolve
class Preproc(object):
"""
A simplified preprocessing layer implementing Gabor filters and suppression.
Parameters
----------
num_orients : int
Number of edge filter orientations (over 2pi).
filter_scale : float
A scale parameter for the filters.
cross_channel_pooling : bool
Whether to pool across neighboring orientation channels (cf. Sec S8.1.4).
Attributes
----------
filters : [numpy.ndarray]
Kernels for oriented Gabor filters.
pos_filters : [numpy.ndarray]
Kernels for oriented Gabor filters with all-positive values.
suppression_masks : numpy.ndarray
Masks for oriented non-max suppression.
"""
def __init__(self,
num_orients=16,
filter_scale=2.,
cross_channel_pooling=False):
self.num_orients = num_orients
self.filter_scale = filter_scale
self.cross_channel_pooling = cross_channel_pooling
self.suppression_masks = generate_suppression_masks(filter_scale=filter_scale,
num_orients=num_orients)
def fwd_infer(self, img, brightness_diff_threshold=18.):
"""Compute bottom-up (forward) inference.
Parameters
----------
img : numpy.ndarray
The input image.
brightness_diff_threshold : float
Brightness difference threshold for oriented edges.
Returns
-------
bu_msg : 3D numpy.ndarray of float
The bottom-up messages from the preprocessing layer.
Shape is (num_feats, rows, cols)
"""
filtered = np.zeros((len(self.filters),) + img.shape, dtype=np.float32)
for i, kern in enumerate(self.filters):
filtered[i] = fftconvolve(img, kern, mode='same')
localized = local_nonmax_suppression(filtered, self.suppression_masks)
# Threshold and binarize
localized *= (filtered / brightness_diff_threshold).clip(0, 1)
localized[localized < 1] = 0
if self.cross_channel_pooling:
pooled_channel_weights = [(0, 1), (-1, 1), (1, 1)]
pooled_channels = [-np.ones_like(sf) for sf in localized]
for i, pc in enumerate(pooled_channels):
for channel_offset, factor in pooled_channel_weights:
ch = (i + channel_offset) % self.num_orients
pos_chan = localized[ch]
if factor != 1:
pos_chan[pos_chan > 0] *= factor
np.maximum(pc, pos_chan, pc)
bu_msg = np.array(pooled_channels)
else:
bu_msg = localized
# Setting background to -1
bu_msg[bu_msg == 0] = -1.
return bu_msg
#property
def filters(self):
return get_gabor_filters(
filter_scale=self.filter_scale, num_orients=self.num_orients, weights=False)
#property
def pos_filters(self):
return get_gabor_filters(
filter_scale=self.filter_scale, num_orients=self.num_orients, weights=True)
def get_gabor_filters(size=21, filter_scale=4., num_orients=16, weights=False):
"""Get Gabor filter bank. See Preproc for parameters and returns."""
def _get_sparse_gaussian():
"""Sparse Gaussian."""
size = 2 * np.ceil(np.sqrt(2.) * filter_scale) + 1
alt = np.zeros((int(size), int(size)), np.float32)
alt[int(size // 2), int(size // 2)] = 1
gaussian = gaussian_filter(alt, filter_scale / np.sqrt(2.), mode='constant')
gaussian[gaussian < 0.05 * gaussian.max()] = 0
return gaussian
gaussian = _get_sparse_gaussian()
filts = []
for angle in np.linspace(0., 2 * np.pi, num_orients, endpoint=False):
acts = np.zeros((size, size), np.float32)
x, y = np.cos(angle) * filter_scale, np.sin(angle) * filter_scale
acts[int(size / 2 + y), int(size / 2 + x)] = 1.
acts[int(size / 2 - y), int(size / 2 - x)] = -1.
filt = fftconvolve(acts, gaussian, mode='same')
filt /= np.abs(filt).sum() # Normalize to ensure the maximum output is 1
if weights:
filt = np.abs(filt)
filts.append(filt)
return filts
def generate_suppression_masks(filter_scale=4., num_orients=16):
"""
Generate the masks for oriented non-max suppression at the given filter_scale.
See Preproc for parameters and returns.
"""
size = 2 * int(np.ceil(filter_scale * np.sqrt(2))) + 1
cx, cy = size // 2, size // 2
filter_masks = np.zeros((num_orients, size, size), np.float32)
# Compute for orientations [0, pi), then flip for [pi, 2*pi)
for i, angle in enumerate(np.linspace(0., np.pi, num_orients // 2, endpoint=False)):
x, y = np.cos(angle), np.sin(angle)
for r in range(1, int(np.sqrt(2) * size / 2)):
dx, dy = round(r * x), round(r * y)
if abs(dx) > cx or abs(dy) > cy:
continue
filter_masks[i, int(cy + dy), int(cx + dx)] = 1
filter_masks[i, int(cy - dy), int(cx - dx)] = 1
filter_masks[num_orients // 2:] = filter_masks[:num_orients // 2]
return filter_masks
def local_nonmax_suppression(filtered, suppression_masks, num_orients=16):
"""
Apply oriented non-max suppression to the filters, so that only a single
orientated edge is active at a pixel. See Preproc for additional parameters.
Parameters
----------
filtered : numpy.ndarray
Output of filtering the input image with the filter bank.
Shape is (num feats, rows, columns).
Returns
-------
localized : numpy.ndarray
Result of oriented non-max suppression.
"""
localized = np.zeros_like(filtered)
cross_orient_max = filtered.max(0)
filtered[filtered < 0] = 0
for i, (layer, suppress_mask) in enumerate(zip(filtered, suppression_masks)):
competitor_maxs = maximum_filter(layer, footprint=suppress_mask, mode='nearest')
localized[i] = competitor_maxs <= layer
localized[cross_orient_max > filtered] = 0
return localized
The problem I found was that np.unravel_index returns all the positions of features for the first image, whereas it only returns (0, 0) continuously for the second. My hypothesis is that it is either a problem with the preprocessing code, or it is a bug in the np.unravel_index function itself, but I am not too sure.
Okay, so turns out there is an underlying problem when calling argmax on the image. I rewrote the sparsification script to not use argmax and it works exactly the same. It should now work with any image.
def sparsify(bu_msg, suppress_radius=3):
"""Make a sparse representation of the edges by greedily selecting features from the
output of preprocessing layer and suppressing overlapping activations.
Parameters
----------
bu_msg : 3D numpy.ndarray of float
The bottom-up messages from the preprocessing layer.
Shape is (num_feats, rows, cols)
suppress_radius : int
How many pixels in each direction we assume this filter
explains when included in the sparsification.
Returns
-------
frcs : see train_image.
"""
frcs = []
img = bu_msg.max(0) > 0
for (r, c), _ in np.ndenumerate(img):
if img[r, c]:
frcs.append((bu_msg[:, r, c].argmax(), r, c))
img[r - suppress_radius:r + suppress_radius + 1,
c - suppress_radius:c + suppress_radius + 1] = False
return np.array(frcs)

Is there any way to use bivariate colormaps in matplotlib?

In other words, I want to make a heatmap (or surface plot) where the color varies as a function of 2 variables. (Specifically, luminance = magnitude and hue = phase.) Is there any native way to do this?
Some examples of similar plots:
Several good examples of exactly(?) what I want to do.
More examples from astronomy, but with non-perceptual hue
Edit: This is what I did with it: https://github.com/endolith/complex_colormap
imshow can take an array of [r, g, b] entries. So you can convert the absolute values to intensities and phases - to hues.
I will use as an example complex numbers, because for it it makes the most sense. If needed, you can always add numpy arrays Z = X + 1j * Y.
So for your data Z you can use e.g.
imshow(complex_array_to_rgb(Z))
where (EDIT: made it quicker and nicer thanks to this suggestion)
def complex_array_to_rgb(X, theme='dark', rmax=None):
'''Takes an array of complex number and converts it to an array of [r, g, b],
where phase gives hue and saturaton/value are given by the absolute value.
Especially for use with imshow for complex plots.'''
absmax = rmax or np.abs(X).max()
Y = np.zeros(X.shape + (3,), dtype='float')
Y[..., 0] = np.angle(X) / (2 * pi) % 1
if theme == 'light':
Y[..., 1] = np.clip(np.abs(X) / absmax, 0, 1)
Y[..., 2] = 1
elif theme == 'dark':
Y[..., 1] = 1
Y[..., 2] = np.clip(np.abs(X) / absmax, 0, 1)
Y = matplotlib.colors.hsv_to_rgb(Y)
return Y
So, for example:
Z = np.array([[3*(x + 1j*y)**3 + 1/(x + 1j*y)**2
for x in arange(-1,1,0.05)] for y in arange(-1,1,0.05)])
imshow(complex_array_to_rgb(Z, rmax=5), extent=(-1,1,-1,1))
imshow(complex_array_to_rgb(Z, rmax=5, theme='light'), extent=(-1,1,-1,1))
imshow will take an NxMx3 (rbg) or NxMx4 (grba) array so you can do your color mapping 'by hand'.
You might be able to get a bit of traction by sub-classing Normalize to map your vector to a scaler and laying out a custom color map very cleverly (but I think this will end up having to bin one of your dimensions).
I have done something like this (pdf link, see figure on page 24), but the code is in MATLAB (and buried someplace in my archives).
I agree a bi-variate color map would be useful (primarily for representing very dense vector fields where your kinda up the creek no matter what you do).
I think the obvious extension is to let color maps take complex arguments. It would require specialized sub-classes of Normalize and Colormap and I am going back and forth on if I think it would be a lot of work to implement. I suspect if you get it working by hand it will just be a matter of api wrangling.
I created an easy to use 2D colormap class, that takes 2 NumPy arrays and maps them to an RGB image, based on a reference image.
I used #GjjvdBurg's answer as a starting point. With a bit of work, this could still be improved, and possibly turned into a proper Python module - if you want, feel free to do so, I grant you all credits.
TL;DR:
# read reference image
cmap_2d = ColorMap2D('const_chroma.jpeg', reverse_x=True) # , xclip=(0,0.9))
# map the data x and y to the RGB space, defined by the image
rgb = cmap_2d(data_x, data_y)
# generate a colorbar image
cbar_rgb = cmap_2d.generate_cbar()
The ColorMap2D class:
class ColorMap2D:
def __init__(self, filename: str, transpose=False, reverse_x=False, reverse_y=False, xclip=None, yclip=None):
"""
Maps two 2D array to an RGB color space based on a given reference image.
Args:
filename (str): reference image to read the x-y colors from
rotate (bool): if True, transpose the reference image (swap x and y axes)
reverse_x (bool): if True, reverse the x scale on the reference
reverse_y (bool): if True, reverse the y scale on the reference
xclip (tuple): clip the image to this portion on the x scale; (0,1) is the whole image
yclip (tuple): clip the image to this portion on the y scale; (0,1) is the whole image
"""
self._colormap_file = filename or COLORMAP_FILE
self._img = plt.imread(self._colormap_file)
if transpose:
self._img = self._img.transpose()
if reverse_x:
self._img = self._img[::-1,:,:]
if reverse_y:
self._img = self._img[:,::-1,:]
if xclip is not None:
imin, imax = map(lambda x: int(self._img.shape[0] * x), xclip)
self._img = self._img[imin:imax,:,:]
if yclip is not None:
imin, imax = map(lambda x: int(self._img.shape[1] * x), yclip)
self._img = self._img[:,imin:imax,:]
if issubclass(self._img.dtype.type, np.integer):
self._img = self._img / 255.0
self._width = len(self._img)
self._height = len(self._img[0])
self._range_x = (0, 1)
self._range_y = (0, 1)
#staticmethod
def _scale_to_range(u: np.ndarray, u_min: float, u_max: float) -> np.ndarray:
return (u - u_min) / (u_max - u_min)
def _map_to_x(self, val: np.ndarray) -> np.ndarray:
xmin, xmax = self._range_x
val = self._scale_to_range(val, xmin, xmax)
rescaled = (val * (self._width - 1))
return rescaled.astype(int)
def _map_to_y(self, val: np.ndarray) -> np.ndarray:
ymin, ymax = self._range_y
val = self._scale_to_range(val, ymin, ymax)
rescaled = (val * (self._height - 1))
return rescaled.astype(int)
def __call__(self, val_x, val_y):
"""
Take val_x and val_y, and associate the RGB values
from the reference picture to each item. val_x and val_y
must have the same shape.
"""
if val_x.shape != val_y.shape:
raise ValueError(f'x and y array must have the same shape, but have {val_x.shape} and {val_y.shape}.')
self._range_x = (np.amin(val_x), np.amax(val_x))
self._range_y = (np.amin(val_y), np.amax(val_y))
x_indices = self._map_to_x(val_x)
y_indices = self._map_to_y(val_y)
i_xy = np.stack((x_indices, y_indices), axis=-1)
rgb = np.zeros((*val_x.shape, 3))
for indices in np.ndindex(val_x.shape):
img_indices = tuple(i_xy[indices])
rgb[indices] = self._img[img_indices]
return rgb
def generate_cbar(self, nx=100, ny=100):
"generate an image that can be used as a 2D colorbar"
x = np.linspace(0, 1, nx)
y = np.linspace(0, 1, ny)
return self.__call__(*np.meshgrid(x, y))
Usage:
Full example, using the constant chroma reference taken from here as a screenshot:
# generate data
x = y = np.linspace(-2, 2, 300)
xx, yy = np.meshgrid(x, y)
ampl = np.exp(-(xx ** 2 + yy ** 2))
phase = (xx ** 2 - yy ** 2) * 6 * np.pi
data = ampl * np.exp(1j * phase)
data_x, data_y = np.abs(data), np.angle(data)
# Here is the 2D colormap part
cmap_2d = ColorMap2D('const_chroma.jpeg', reverse_x=True) # , xclip=(0,0.9))
rgb = cmap_2d(data_x, data_y)
cbar_rgb = cmap_2d.generate_cbar()
# plot the data
fig, plot_ax = plt.subplots(figsize=(8, 6))
plot_extent = (x.min(), x.max(), y.min(), y.max())
plot_ax.imshow(rgb, aspect='auto', extent=plot_extent, origin='lower')
plot_ax.set_xlabel('x')
plot_ax.set_ylabel('y')
plot_ax.set_title('data')
# create a 2D colorbar and make it fancy
plt.subplots_adjust(left=0.1, right=0.65)
bar_ax = fig.add_axes([0.68, 0.15, 0.15, 0.3])
cmap_extent = (data_x.min(), data_x.max(), data_y.min(), data_y.max())
bar_ax.imshow(cbar_rgb, extent=cmap_extent, aspect='auto', origin='lower',)
bar_ax.set_xlabel('amplitude')
bar_ax.set_ylabel('phase')
bar_ax.yaxis.tick_right()
bar_ax.yaxis.set_label_position('right')
for item in ([bar_ax.title, bar_ax.xaxis.label, bar_ax.yaxis.label] +
bar_ax.get_xticklabels() + bar_ax.get_yticklabels()):
item.set_fontsize(7)
plt.show()
I know this is an old post, but want to help out others that may arrive late. Below is a python function to implement complex_to_rgb from sage. Note: This implementation isn't optimal, but it is readable. See links: (examples)(source code)
Code:
import numpy as np
def complex_to_rgb(z_values):
width = z_values.shape[0]
height = z_values.shape[1]
rgb = np.zeros(shape=(width, height, 3))
for i in range(width):
row = z_values[i]
for j in range(height):
# define value, real(value), imag(value)
zz = row[j]
x = np.real(zz)
y = np.imag(zz)
# define magnitued and argument
magnitude = np.hypot(x, y)
arg = np.arctan2(y, x)
# define lighness
lightness = np.arctan(np.log(np.sqrt(magnitude) + 1)) * (4 / np.pi) - 1
if lightness < 0:
bot = 0
top = 1 + lightness
else:
bot = lightness
top = 1
# define hue
hue = 3 * arg / np.pi
if hue < 0:
hue += 6
# set ihue and use it to define rgb values based on cases
ihue = int(hue)
# case 1
if ihue == 0:
r = top
g = bot + hue * (top - bot)
b = bot
# case 2
elif ihue == 1:
r = bot + (2 - hue) * (top - bot)
g = top
b = bot
# case 3
elif ihue == 2:
r = bot
g = top
b = bot + (hue - 2) * (top - bot)
# case 4
elif ihue == 3:
r = bot
g = bot + (4 - hue) * (top - bot)
b = top
# case 5
elif ihue == 4:
r = bot + (hue - 4) * (top - bot)
g = bot
b = top
# case 6
else:
r = top
g = bot
b = bot + (6 - hue) * (top - bot)
# set rgb array values
rgb[i, j, 0] = r
rgb[i, j, 1] = g
rgb[i, j, 2] = b
return rgb