Related
I am using open-source tesseract OCR to extract texts from the images. I need texts with proper space preserved and aligned for some information extraction tasks. I have used the tesseract .image_to_data() method. This method outputs the recognized texts along with its bounding box and confidence score.
After performing sorting I get all the texts on a string in the order they exist in the image but not formatted in the way that I want. One of my test images is
My code:
import cv2
import pytesseract
import argparse
import time
from pytesseract import Output
from utils.config import ENG_CONFIG
def check_confidence(df):
df = df[df['conf'] != -1]
return df
class OCR:
def __init__(self, conf, image):
self.conf = conf
self.image = image
def get_ocr_output(self):
result = pytesseract.image_to_data(self.image, config=self.conf, output_type=Output.DATAFRAME)
result = check_confidence(result)
lines, formatted_string = self.parse_texts(result)
return lines, formatted_string
def parse_texts(self, df):
img = self.image.copy()
parsed_info = []
start = time.time()
for row in df.itertuples(): # for index, row in df.iterrows()
loc = (row.left, row.top, row.width, row.height)
# draw box on the image
cv2.rectangle(img, (loc[0], loc[1]), (loc[0] + loc[2], loc[1] + loc[3]), (255, 255, 0), 2)
text = row.text
conf = row.conf
row_info = {'box': loc, 'text': text, 'conf': conf}
parsed_info.append(row_info)
end = time.time()
print("\n[INFO]: Total time to read the dataframe row by row is : {} seconds.".format(end - start))
print("\n[INFO]: Parsed Information before sorting boxes is: \n{}".format(parsed_info))
parsed_info = self.sort_box_vertically(parsed_info)
lines = self.get_lines_vertically(parsed_info)
lines = self.sort_box_horizontally(lines)
print("\n[INFO]: Parsed Information after sorting boxes horizontally-vertically is : \n{}".format(lines))
# formatted_strings = self.format_texts(lines)
formatted_strings = self.format_texts_by_reserving_spaces(lines)
print("\n[INFO] : Text for the image is : \n{}".format(formatted_strings))
cv2.imshow("text box", img)
cv2.waitKey(0)
return lines, formatted_strings
def sort_box_vertically(self, parsed_info):
"""
Method to sort the boxes vertically
:param parsed_info:
:return:
"""
for counter in range(len(parsed_info) - 1):
for num in range((len(parsed_info) - 1) - counter):
if self.get_box_center(parsed_info[num]['box'])[1] > self.get_box_center(parsed_info[num + 1]['box'])[
1]:
temp = parsed_info[num + 1]
parsed_info[num + 1] = parsed_info[num]
parsed_info[num] = temp
return parsed_info
def get_lines_vertically(self, parsed_info):
"""
Method to separate the lines vertically
:param parsed_info:
:return:
"""
lines = []
while len(parsed_info) != 0:
ref_box = parsed_info[0]['box']
line = [parsed_info[0]]
for i in range(1, len(parsed_info)):
if self.is_intersects(ref_box, parsed_info[i]['box']) or self.is_intersects(parsed_info[i]['box'],
ref_box):
line.append(parsed_info[i])
for i in range(len(line)):
parsed_info.remove(line[i])
lines.append(line)
return lines
def is_intersects(self, box1, box2, margin=0):
x_1, y_1, w_1, h_1 = box1
x_2, y_2, w_2, h_2 = box2
# check if box2 intersect with box1 by height
h1, k1 = self.get_box_center(box1)
h2, k2 = self.get_box_center(box2)
if int(k2) in range(int(y_1 + margin * h_1), int((y_1 + h_1 - h_1 * margin))) or int(k1) in range(
int(y_2 + margin * h_2), int((y_2 + h_2 - h_2 * margin))):
return True
return False
def get_box_center(self, box):
"""
Method to get the center of box (h,k)
:param box: box as tuple (x, y, w, h)
:return:
"""
x, y, w, h = box
x1 = x
y1 = y
x2 = x + w
y2 = y + h
# Find the center of the box
h, k = int((x1 + x2) / 2), int((y1 + y2) / 2)
return h, k
def format_texts(self, lines):
"""
Method to return the formatted texts from the image
:param lines:
:return:
"""
formatted_string = ""
for line in lines:
for word in line:
# print(word)
formatted_string += " " + word['text']
formatted_string += '\n'
return formatted_string
def format_texts_by_reserving_spaces(self, lines):
"""
An efficient Method to return the formatted texts from the image
:param lines:
:return:
"""
formatted_string = ""
for line in lines:
flag = 0
for word in line:
# print(word)
word_start = word['box'][0]
word_end = word['box'][0] + word['box'][2]
formatted_string += " " * (word_start - flag - 1) + word['text']
flag = word_end
formatted_string += '\n'
return formatted_string
def sort_box_horizontally(self, lines):
final_lines = []
for line in lines:
for counter in range(len(line) - 1):
for num in range((len(line) - 1) - counter):
# If x1 of first line is greater than second line
if self.get_box_center(line[num]['box'])[0] > self.get_box_center(line[num + 1]['box'])[0]:
temp = line[num + 1]
line[num + 1] = line[num]
line[num] = temp
final_lines.append(line)
return final_lines
def sort_box_horizontally_example(self, line_boxes):
"""
Method to sort the boxes horizontally
:param boxes: list of tuple of boxes eg:[((,),(,))]
:return:
"""
final_lines = []
for line in line_boxes:
for counter in range(len(line) - 1):
for num in range((len(line) - 1) - counter):
# If x1 of first line is greater than second line
if line[num][0][0] > line[num + 1][0][0]:
temp = line[num + 1]
line[num + 1] = line[num]
line[num] = temp
final_lines.append(line)
return final_lines
def sorting_example(self):
"""
Sorting array using bubble sort
:return:
"""
list_ = [3, 6, 8, 44, 77, 2, 44, 556, 66, 565, 34]
for i in range(len(list_) - 1):
for j in range((len(list_) - 1) - i):
if list_[j] > list_[j + 1]:
temp = list_[j]
list_[j] = list_[j + 1]
list_[j + 1] = temp
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--image',
default="./intelligent_information_extractor/images/img5.png",
help="Path to input image to be OCR'd")
args = parser.parse_args()
image = cv2.imread(args.image)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
ocr = OCR(ENG_CONFIG, image)
results = ocr.get_ocr_output()
The output it produces can be found here
While formatting texts which ocr outputs, I have converted a pixel to a single character. But it creates the text string where texts are separated by large whitespace.
So, How to decrease the whitespace to properly format the output texts by preserving all the spaces and alignment?
I've been trying to record multi-source video(color, depth, IR) from two kinect V2 sensors. I'm using libfreenect2, drivers that enables multiple kinect v2 sensors and its python wrapper:pylibfreenect2.
The example code is for single sensor. It supports multi-source video streaming for one kinect sensor using cv2.imshow. Since my purpose is to record, I simply changed cv2.imshow to cv2.videowriter to save the video. However, I found that the code becomes slower after using videowriter since the frame number is decreasing for a fixed period of time.
For example, for one minute running the code using a single kinect sensor:
single_stream 1875 frames=31.25Hz,
single_record 1036 frames=17.26Hz
Question 1:
We can see there is a significant drop on the sampling rate. Is there any way to avoid the drop?
Also, since I'm trying to use multiple kinect sensors, I simply instantiate everything twice for two different sensors. Compared to single sensor, dual sensors version also has a significant sampling rate drop:
dualstream 1500 frames=25Hz,
dualsave 660 frames=11Hz
Question 2:
How can I optimize the code using multi-thread instead of just instantiating everything twice?
Here is my code:
# coding: utf-8
# An example using startStreams
import numpy as np
import cv2
import sys
from pylibfreenect2 import Freenect2, SyncMultiFrameListener
from pylibfreenect2 import FrameType, Registration, Frame
import argparse
import datetime
import keyboard
parser = argparse.ArgumentParser()
parser.add_argument('--out_name', type=str, help="the output names", default=None)
parser.add_argument('--out_dir', type=str, help="the output directory", default='data/')
args = parser.parse_args()
try:
from pylibfreenect2 import OpenGLPacketPipeline
pipeline0 = OpenGLPacketPipeline()
pipeline1 = OpenGLPacketPipeline()
except:
try:
from pylibfreenect2 import OpenCLPacketPipeline
pipeline0 = OpenCLPacketPipeline()
pipeline1 = OpenCLPacketPipeline()
except:
from pylibfreenect2 import CpuPacketPipeline
pipeline0 = CpuPacketPipeline()
pipeline1 = CpuPacketPipeline()
print("Packet pipeline:", type(pipeline0).__name__)
enable_rgb = True
enable_depth = True
fn = Freenect2()
num_devices = fn.enumerateDevices()
if num_devices == 0:
print("No device connected!")
sys.exit(1)
serial0 = fn.getDeviceSerialNumber(0)
serial1 = fn.getDeviceSerialNumber(1)
device0 = fn.openDevice(serial0, pipeline=pipeline0)
device1 = fn.openDevice(serial1, pipeline=pipeline1)
types = 0
if enable_rgb:
types |= FrameType.Color
if enable_depth:
types |= (FrameType.Ir | FrameType.Depth)
listener0 = SyncMultiFrameListener(types)
listener1 = SyncMultiFrameListener(types)
# Register listeners
device0.setColorFrameListener(listener0)
device0.setIrAndDepthFrameListener(listener0)
device1.setColorFrameListener(listener1)
device1.setIrAndDepthFrameListener(listener1)
if enable_rgb and enable_depth:
device0.start()
device1.start()
else:
device0.startStreams(rgb=enable_rgb, depth=enable_depth)
device1.startStreams(rgb=enable_rgb, depth=enable_depth)
# NOTE: must be called after device.start()
if enable_depth:
registration0 = Registration(device0.getIrCameraParams(),
device0.getColorCameraParams())
registration1 = Registration(device1.getIrCameraParams(),
device1.getColorCameraParams())
undistorted0 = Frame(512, 424, 4)
registered0 = Frame(512, 424, 4)
undistorted1 = Frame(512, 424, 4)
registered1 = Frame(512, 424, 4)
time_list = []
#specify output folder
out_dir = args.out_dir
out_name = args.out_name
# height = 540
# width = 960
height = 1080//2
width = 1920//2
channel = 3
fps = 30
cnt = 0
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
# cv2.VideoWriter_fourcc(*'mp4v')
# cv2.VideoWriter_fourcc(*'MP42')
color_wrapper0 = cv2.VideoWriter(out_dir + out_name + '_color0.mp4', fourcc, float(fps), (width, height), True)
depth_wrapper0 = cv2.VideoWriter(out_dir + out_name + '_depth0.mp4', fourcc, float(fps), (512, 424), False)
ir_wrapper0 = cv2.VideoWriter(out_dir + out_name + '_ir0.mp4', fourcc, float(fps), (512, 424), False)
register_wrapper0 = cv2.VideoWriter(out_dir + out_name + '_register0.mp4', fourcc, float(fps), (512, 424), True)
color_wrapper1 = cv2.VideoWriter(out_dir + out_name + '_color1.mp4', fourcc, float(fps), (width, height), True)
depth_wrapper1 = cv2.VideoWriter(out_dir + out_name + '_depth1.mp4', fourcc, float(fps), (512, 424), False)
ir_wrapper1 = cv2.VideoWriter(out_dir + out_name + '_ir1.mp4', fourcc, float(fps), (512, 424), False)
register_wrapper1 = cv2.VideoWriter(out_dir + out_name + '_register1.mp4', fourcc, float(fps), (512, 424), True)
start_time = datetime.datetime.now()
while True:
frames0 = listener0.waitForNewFrame()
frames1 = listener1.waitForNewFrame()
if enable_rgb:
color0 = frames0["color"]
color1 = frames1["color"]
if enable_depth:
ir0 = frames0["ir"]
depth0 = frames0["depth"]
ir1 = frames1["ir"]
depth1 = frames1["depth"]
if enable_rgb and enable_depth:
registration0.apply(color0, depth0, undistorted0, registered0)
registration1.apply(color1, depth1, undistorted1, registered1)
elif enable_depth:
registration0.undistortDepth(depth0, undistorted0)
registration1.undistortDepth(depth1, undistorted1)
# if enable_depth:
# cv2.imshow("ir0", ir0.asarray() / 65535.)
# cv2.imshow("depth0", depth0.asarray() / 4500.)
# cv2.imshow("undistorted0", undistorted0.asarray(np.float32) / 4500.)
# cv2.imshow("ir1", ir1.asarray() / 65535.)
# cv2.imshow("depth1", depth1.asarray() / 4500.)
# cv2.imshow("undistorted1", undistorted1.asarray(np.float32) / 4500.)
# if enable_rgb:
# cv2.imshow("color0", cv2.resize(color0.asarray(),
# (int(1920 / 3), int(1080 / 3))))
# cv2.imshow("color1", cv2.resize(color1.asarray(),
# (int(1920 / 3), int(1080 / 3))))
# if enable_rgb and enable_depth:
# cv2.imshow("registered0", registered0.asarray(np.uint8))
# cv2.imshow("registered1", registered1.asarray(np.uint8))
color_wrapper0.write(cv2.resize(color0.asarray(),(width, height))[:,:,:-1])
color_wrapper1.write(cv2.resize(color0.asarray(),(width, height))[:,:,:-1])
color_wrapper0.write(color0.asarray()[:,:,:-1])
color_wrapper1.write(color1.asarray()[:,:,:-1])
depth_wrapper0.write((depth0.asarray() * (255.0/4500.0)).clip(0, 255).astype(np.uint8))
depth_wrapper1.write((depth1.asarray() * (255.0/4500.0)).clip(0, 255).astype(np.uint8))
ir_wrapper0.write((ir0.asarray() * (255.0/65535.0)).clip(0, 255).astype(np.uint8))
ir_wrapper1.write((ir1.asarray() * (255.0/65535.0)).clip(0, 255).astype(np.uint8))
register_wrapper0.write(registered0.asarray(np.uint8)[:,:,:-1])
register_wrapper1.write(registered1.asarray(np.uint8)[:,:,:-1])
time_list.append(datetime.datetime.now())
listener0.release(frames0)
listener1.release(frames1)
cnt += 1
# key = cv2.waitKey(delay=1)
# if key == ord('q'):
# break
if keyboard.is_pressed("q"): # if key 'q' is pressed
print('finishing the loop')
break # finishing the loop
key = cv2.waitKey(delay=1)
if key == ord('q'):
break
ir_wrapper0.release()
ir_wrapper1.release()
print("save ir video successfully")
depth_wrapper0.release()
depth_wrapper1.release()
print("save depth video successfully")
color_wrapper0.release()
color_wrapper1.release()
print("save color video successfully")
register_wrapper0.release()
register_wrapper1.release()
print("save registered video successfully")
np.save(out_dir + out_name + '_time.npy', time_list)
print("save time array successfully")
print("total frame is: ", cnt)
end_time = datetime.datetime.now()
print("total time is: ", end_time - start_time)
device0.stop()
device0.close()
device1.stop()
device1.close()
sys.exit(0)
The comment part is the streaming code using cv2.imshow. Below that is what I modified to save the video using cv2.videowriter.
I have a piece of code that initially decodes a .dat file into a .txt file using a binary chipher cycle style decoder. It results in an over 500 line text file of data points with lines 0-65 being titles and other display features and the last few lines, starting from 586, being wrongly decoded text that looks something like:
ßÅBÎheÀœaÜ;sî3TÐêM·Zì?pêI†Q’&×¥ü#ÇPËiPì¿j–hñHžíoî#ˆ[ÿ>BÿÃ#ÌhcP¿_ÔkõOˆEñlÀ‹J–>tò5Ægã_ð: yŽ6aÎ
“uôhaù*°Dý4}Ó´Qá4wÙ
žZôØ
‘~êlHí–’/mÑ=žt
k×£QÉoû·]Ý&õC´Jœ9mû»ZÃ+]þ6ƒ[ቶS;Uö¥Wã
Lè:ÂXÿ4sÈÄAïPó€Dó$EØÙ•dДeïkHâN xÐj#Ø"”eë1aõÅCÒ7ùC–ñiÐCÑP‹Æ
Ñ
]ô†}ÌdDñ
Ë,WÎÄdó^ã8žDäÓ)Çq9}ùÃfÄP÷ÇzîoiÒ ÁpìeSÖ€ÒMŒÀ“;Bö
I am using the code:
with open (file) as f:
xpoints, ypoints, gradient = np.loadtxt(itertools.islice(f,68, 584), delimiter=',', unpack=True)
in order to load only the lines that contain the data points I am after.
For some reason however, this causes the program to throw an error that it cant decode a byte as it maps to undefined. I have confirmed it is caused by the junk text at the bottom and seems to be thrown in the line shown above but I cannot figure out why this is the case as it shouldn't need to read those lines at all.
Full error:
File "C:\Users\brady\Desktop\Slider_All\Slide-Mobile.py", line 57, in
module
xpoints, ypoints, gradient = np.loadtxt(IT.islice(f,68,
500), delimiter=',', unpack=True) File
"C:\Users\brady\AppData\Local\Programs\Python\Python38-32\lib\site-packag
es\numpy\lib\npyio.py", line 1159, in loadtxt
for x in read_data(_loadtxt_chunksize): File "C:\Users\brady\AppData\Local\Programs\Python\Python38-32\lib\site-packag
es\numpy\lib\npyio.py", line 1075, in read_data
for i, line in enumerate(line_iter): File "C:\Users\brady\AppData\Local\Programs\Python\Python38-32\lib\encodings\c
p1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0] UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position
7758: cha racter maps to undefined
Does itertools.islice or numpy.loadtxt possibly attempt to read the whole document first before it takes the slice and runs into a problem or is this something else entirely that I'm missing. I will post my entire unedited code below for completions sake, thankyou for any and all help.
import matplotlib.animation as animation
from matplotlib.widgets import Slider, Button
import matplotlib as mpl
from matplotlib import pyplot as plt
import scipy.interpolate as inter
import numpy as np
import itertools as IT
from itertools import cycle
from scipy.interpolate import interp1d
import os
file = 'example.dat'
x1 = 250 #Lower bound Bigger bound leads to lots of lag
x2 = 300 #Upper bound Recommended to remain close to range of 50
#=========================================================================================================================
start = [] #Stores data before given points
end = [] #Stores data after given points
files = [] #Stores text file to be removed when done
#This function decodes and re-encodes the dat files
class Decoder:
def decode(fn_in, fn_out):
CIPHER = cycle([0b01011100, 0b00100111, 0b10111010, 0b01111011, 0b11110010, 0b00110010, 0b10100101])
with open(fn_in, 'rb') as fin, open(fn_out, 'wb') as fout:
fout.write(fin.read(14))
byte = fin.read(1)
while byte:
fout.write( ( int.from_bytes(byte, 'big') ^ next(CIPHER) ).to_bytes(1, 'big') )
byte = fin.read(1)
def to_txt(filename):
#global files
if filename [-3:] == "dat":
Decoder.decode( filename, filename[:-3] + "txt" )
filename = filename[:-3] + "txt"
else:
print("Extension not recognised for input filename \""+str(filename)+"\", skipping...")
return filename
def to_dat(filename):
files.append(filename)
if filename [-3:] == "txt":
Decoder.decode( filename, tempfile[:-3]+ "dat" )
#file.append(filename[:-3] + "dat")
else:
print("Extension not recognised for input filename \""+str(filename)+"\", skipping...")
if file[-3:] == "dat":
file = Decoder.to_txt(file) #Converts .dat to .txt
files.append(file)
#Gets all data points from file
with open (file) as f:
xpoints, ypoints, gradient = np.loadtxt(IT.islice(f,68, 584), delimiter=',', unpack=True)
#get a list of points to fit a spline to as well
xmin = min(xpoints)
xmax = max(xpoints)
#Calculates which lines of data are required to plot
X1 = int(516*((x1 - xmin)/(xmax-xmin))) + 68
X2 = int(516*((x2 - xmin)/(xmax-xmin))) + 68
#Gets specific lines and saves the rest to copy back later
with open (file) as f:
xp, ypoints, gradient = np.loadtxt(IT.islice(f,X1, X2), delimiter=',', unpack=True)
with open(file) as f:
for line in IT.islice(f,0,X1):
start.append(line)
with open (file) as f:
for line in IT.islice(f,X2,584):
end.append(line)
#Sets amount of data points to plot, must be multiple of point range
#The lower the number the more accurate the plot but the slower it will run
N = len(xp)
if N < 200:
j = 1
elif N < 400:
j = 1
else: j = 1
x = xp[::j]
yvals = ypoints[::j]
N = len(x)
xnew = xp
#spline fit
spline = inter.InterpolatedUnivariateSpline (x, yvals)
#set up a plot
fig,axes = plt.subplots(1,1,figsize=(12.0,4.0),sharex=True)
fig,axes.set_position([0.05,0.08,0.93,0.80])
ax1 = axes
pind = None #active point
epsilon = 5 #max pixel distance
#Updates plot when point is dragged
def update(val):
global yvals
global spline
# update curve
for i in np.arange(N):
yvals[i] = sliders[i].val
l.set_ydata(yvals)
spline = inter.InterpolatedUnivariateSpline (x, yvals)
m.set_ydata(spline(X))
# redraw canvas while idle
fig.canvas.draw_idle()
#Resets plot back to original save from when opened
def reset(event):
global yvals
global spline
#reset the values
yvals = ypoints
for i in np.arange(N):
sliders[i].reset()
spline = inter.InterpolatedUnivariateSpline (x, yvals)
l.set_ydata(yvals)
m.set_ydata(spline(X))
# redraw canvas while idle
fig.canvas.draw_idle()
#Overwirtes current save with new plot
def save(event):
f = interp1d(x, yvals, kind='cubic')
ynew = f(xnew)
ax1.plot(xnew,ynew)
newfile = np.vstack((xnew,ynew, gradient)).T
with open(file, 'w') as f:
for item in start:
f.write("%s" % item)
np.savetxt(f, newfile, delimiter = ',')
for item in end:
f.write("%s" % item)
#f.write('""')
Decoder.to_dat(file) #Converts .txt to .dat
#Event handler for mouse click
def button_press_callback(event):
'whenever a mouse button is pressed'
global pind
if event.inaxes is None:
return
if event.button != 1:
return
#print(pind)
pind = get_ind_under_point(event)
#Event handler for mouse release
def button_release_callback(event):
'whenever a mouse button is released'
global pind
if event.button != 1:
return
pind = None
#Gets clicked point number
def get_ind_under_point(event):
'get the index of the vertex under point if within epsilon tolerance'
# display coords
#print('display x is: {0}; display y is: {1}'.format(event.x,event.y))
t = ax1.transData.inverted()
tinv = ax1.transData
xy = t.transform([event.x,event.y])
#print('data x is: {0}; data y is: {1}'.format(xy[0],xy[1]))
xr = np.reshape(x,(np.shape(x)[0],1))
yr = np.reshape(yvals,(np.shape(yvals)[0],1))
xy_vals = np.append(xr,yr,1)
xyt = tinv.transform(xy_vals)
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.hypot(xt - event.x, yt - event.y)
indseq, = np.nonzero(d == d.min())
ind = indseq[0]
#print(d[ind])
if d[ind] >= epsilon:
ind = None
#print(ind)
return ind
#Event handler for mosue movement
def motion_notify_callback(event):
'on mouse movement'
global yvals
if pind is None:
return
if event.inaxes is None:
return
if event.button != 1:
return
#update yvals
#print('motion x: {0}; y: {1}'.format(event.xdata,event.ydata))
yvals[pind] = event.ydata
# update curve via sliders and draw
sliders[pind].set_val(yvals[pind])
fig.canvas.draw_idle()
X = xp
ax1.plot (X, ypoints, 'k--', label='original')
l, = ax1.plot (x,yvals,color='k',linestyle='none',marker='o',markersize=8)
m, = ax1.plot (X, spline(X), 'r-', label='spline')
if max(ypoints) > 0:
yheight = 0.01*max(ypoints)
ylower =0
else:
yheight = -0.1*max(ypoints)
ylower = yheight
ax1.set_yscale('linear')
ax1.set_xlim(x1, x2)
ax1.set_ylim(min(ypoints)-ylower,max(ypoints)+yheight)
ax1.grid(True)
ax1.yaxis.grid(True,which='minor',linestyle='--')
sliders = []
for i in np.arange(N):
axamp = plt.axes([0.84, -1, 0.12, 0.01])
# Slider
s = Slider(axamp, 'p{0}'.format(i), -100, 10, valinit=yvals[i])
sliders.append(s)
for i in np.arange(N):
#samp.on_changed(update_slider)
sliders[i].on_changed(update)
axres = plt.axes([0.84, 0.90, 0.15, 0.08])
bres = Button(axres, 'Reset')
bres.on_clicked(reset)
axsave = plt.axes([0.68, 0.90, 0.15, 0.08])
bsave = Button(axsave, 'Save')
bsave.on_clicked(save)
fig.canvas.mpl_connect('button_press_event', button_press_callback)
fig.canvas.mpl_connect('button_release_event', button_release_callback)
fig.canvas.mpl_connect('motion_notify_event', motion_notify_callback)
plt.show()
for filename in files:
os.remove(filename)
EDIT: I know believe the error is almost definitely tied to the itertools.islice command as I have found a similar issue here: Python 3 itertools.islice continue despite UnicodeDecodeError.
Currently researching alternate way to potentially open the file as changing decode style for .dat is not possible at this stage
I have solved the issue using the solution posted here: https://stackoverflow.com/a/31113251/10475989
My final code is:
types_of_encoding = ["utf8", "cp1252"]
for encoding_type in types_of_encoding:
with open (file, 'r', encoding = encoding_type, errors='ignore') as f:
xpoints, ypoints, gradient = np.loadtxt(IT.islice(f,65, 582), delimiter=',', unpack=True)
This question already has answers here:
How to clear memory completely of all matplotlib plots
(5 answers)
Closed 3 years ago.
I will first describe the script and later will publish the code and a way to reproduce the problem, in my machine might not be the case for every machine.
So I have a script that build objects, clouds, with specific properties and export the data to files that is the input to a software called SHDOM, this tool analyze radiation properties and can be further read in the following link.
The script is its a bit long, sorry in advance:
import numpy as np
from itertools import product
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from mpl_toolkits.mplot3d import Axes3D
import gc
def writeDomainFile(type, file_name, temp_vec,
lwc, r_effective, dx, dy,
height_vec):
if type == 2:
assert list(lwc.shape) == list(r_effective.shape), "when the file type is 2 the shapes of lwc and r_effective shold be the same"
x_lwc, y_lwc, z_lwc = lwc.shape
x_r_effetive, y_r_effetive, z_r_effetive = r_effective.shape
fp = open(file_name, 'w')
# write the file format
fp.write("{:}\n".format(type))
# write the number of x, y, z points
fp.write("{:} {:} {:}\n".format(x_lwc, y_lwc, z_lwc))
# write the spacing resolution in x and y
fp.write("{:} {:}\n".format(dx, dy))
# write the height vector
fp.write(" ".join(map(str,height_vec)) + "\n")
# write the temprature vector
fp.write(" ".join(map(str,temp_vec)) + "\n")
indices = product(range(x_lwc), range(y_lwc), range(z_lwc))
print_indices = product(range(1, x_lwc + 1), range(1, y_lwc + 1), range(1, z_lwc + 1))
if type == 1: # only lwc
for triplet, print_triplets in zip(indices, print_indices):
# t = tuple(1 + list(triplet))
if not lwc[triplet]:
fp.write("{:} {:} {:} {:}\n".format(*print_triplets, 0))
else:
fp.write("{:} {:} {:} {:6.4f}\n".format(*print_triplets, lwc[triplet]))
elif type == 2:
for triplet, print_triplets in zip(indices, print_indices):
# t = tuple(map(lambda x: x + 1, list(triplet)))
if not lwc[triplet]:
fp.write("{:} {:} {:} {:} {:}\n".format(*print_triplets, 0, 0))
else:
fp.write("{:} {:} {:} {:6.4f} {:4.2f}\n".format(*print_triplets, lwc[triplet], r_effective[triplet]))
fp.close()
return
def lapseRateTemp(base_temp, position):
temp = np.zeros(position.shape)
temp[0] = base_temp
temp[1:] = base_temp - 6.5 * position[1:]
return np.round(temp, 4)
def createCubicCloudDomain(cloud_tick, x_size, y_size,
dx, dy, dz, r_effective_option, lwc_option,
cloud_x_size, cloud_y_size):
# const
temp0 = 300 # kelvin
N_x = x_size / dx # number of x points
N_y = y_size / dy # number of y points
cld_base = 1.0 # the cloud always start from 1[km] above ground
x_center = N_x / 2
y_center = N_y / 2
prog_x = (cloud_x_size / 2) / dx
prog_y = (cloud_y_size / 2) / dy
cloud_x_west = int(x_center - prog_x)
cloud_x_east = int(x_center + prog_x)
cloud_y_south = int(y_center - prog_y)
cloud_y_north = int(y_center + prog_y)
cloud_x_vec = np.arange(cloud_x_west, cloud_x_east, 1)
cloud_y_vec = np.arange(cloud_y_south, cloud_y_north, 1)
for tick in cloud_tick: # cloud_tick might be a vector
cld_top = cld_base + tick
N_z = tick / dz # number of z points
cloud_z_vec = np.round(np.arange(cld_base, cld_top, dz), 4)
# temprature
temp_base = temp0 - 9.8 * cld_base
temp_vec = lapseRateTemp(temp_base, cloud_z_vec - cld_base,)
temp_cloud = np.tile(temp_vec,(int(temp_vec.shape[0]), int(temp_vec.shape[0]), 1))
temp_domain = np.zeros((int(N_x), int(N_y), int(N_z)))
temp_domain[cloud_x_west:cloud_x_east, cloud_y_south: cloud_y_north, :] = temp_cloud
plotTemperatureGradient(temp_domain, 'test.png')
# del temp_cloud
# del temp_domain
# gc.collect()
for r in r_effective_option:
for l in lwc_option:
r_effective_cloud = np.full((cloud_x_vec.shape[0],
cloud_y_vec.shape[0],
cloud_z_vec.shape[0]),
r)
lwc_cloud = np.full((cloud_x_vec.shape[0],
cloud_y_vec.shape[0],
cloud_z_vec.shape[0]),
l)
r_effective_domain = np.zeros((int(N_x), int(N_y), int(N_z)))
lwc_domain = np.zeros((int(N_x), int(N_y), int(N_z)))
# the positions to enter the cloud data
lwc_domain[cloud_x_west:cloud_x_east, cloud_y_south: cloud_y_north, :] = lwc_cloud
r_effective_domain[cloud_x_west:cloud_x_east, cloud_y_south: cloud_y_north, :] = r_effective_cloud
writeDomainFile(2, "test.txt", temp_vec, lwc_domain, r_effective_domain, dx, dy, cloud_z_vec)
plotGeneratedCloud(lwc_domain, r_effective_domain)
return
def plotTemperatureGradient(temp_mat, file_name):
xs, ys, zs = temp_mat.shape
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y, Z = np.mgrid[:xs, :ys, :zs]
faltten_data = temp_mat.ravel().astype(np.float16)
color_map = np.zeros((faltten_data.shape[0], 4))
# map scalars to colors
minima = np.min(faltten_data[np.nonzero(faltten_data)])
maxima = np.max(faltten_data[np.nonzero(faltten_data)])
norm = matplotlib.colors.Normalize(vmin=minima, vmax=maxima, clip=True)
mapper = cm.ScalarMappable(norm=norm, cmap='jet')
rgba = mapper.to_rgba(faltten_data)
color_map[:,0:3] = rgba[:, 0:3]
color_map[:,3] = np.where(faltten_data > 0, 0.07, 0)
p = ax.scatter(X, Y, Z, c=color_map.astype(np.float16))
ax.set_xlabel('X position [Arb.]')
ax.set_ylabel('Y position [Arb.]')
ax.set_zlabel('Z position [Arb.]')
fig.colorbar(p)
# plt.title(title)
plt.savefig(file_name)
return
def plotGeneratedCloud(lwc, r_effective):
light_blue = np.array([0, 1, 0.7])
matrixPlotter(lwc, 'lwc.png',
'Liquid water content of the cloud',
light_blue)
# gc.collect()
light_pink = np.array([0.9, 0.6, 0.9])
matrixPlotter(r_effective, 'r_effective.png',
'Effective radius of the cloud',
light_pink)
return
def matrixPlotter(mat, file_name, title, c=np.array([0.2, 0.6, 0])):
xs, ys, zs = mat.shape
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y, Z = np.mgrid[:xs, :ys, :zs]
faltten_data = mat.ravel().astype(np.float16)
color_map = np.zeros((faltten_data.shape[0], 4))
color_map[:,0:3] = c
color_map[:,3] = np.where(faltten_data > 0, 0.07, 0)
ax.scatter(X, Y, Z, c=color_map.astype(np.float16))
ax.set_xlabel('X position [Arb.]')
ax.set_ylabel('Y position [Arb.]')
ax.set_zlabel('Z position [Arb.]')
plt.title(title)
plt.savefig(file_name)
return
# def main():
# return
if __name__ == '__main__':
# main()
createCubicCloudDomain([0.5], 2.02, 2.02, 0.01, 0.01, 0.01, [0.5], [1], 0.5, 0.5)
print("Done")
So the problem occur when:
I try to export all 3 plots, calling the following blocks:
plotGeneratedCloud(lwc_domain, r_effective_domain) or
temp_cloud = np.tile(temp_vec,(int(temp_vec.shape[0]), int(temp_vec.shape[0]), 1))
temp_domain = np.zeros((int(N_x), int(N_y), int(N_z)))
temp_domain[cloud_x_west:cloud_x_east, cloud_y_south: cloud_y_north, :] = temp_cloud
plotTemperatureGradient(temp_domain, 'test.png')
When I try to create more then 1 object (cloud), so calling the function with:
createCubicCloudDomain([0.5, 0.6], 2.02, 2.02, 0.01, 0.01, 0.01, [0.5, 0.4], [1, 0.3], 0.5, 0.5)
increasing the domain size like:
createCubicCloudDomain([0.5], 4.02, 4.02, 0.01, 0.01, 0.01, [0.5], [1], 0.5, 0.5)
The type of error I'm getting is the following:
File
"C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\matplotlib\backends\backend_agg.py",
line 396, in draw
self.figure.draw(self.renderer) File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\matplotlib\artist.py",
line 38, in draw_wrapper
return draw(artist, renderer, *args, **kwargs) File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\matplotlib\figure.py",
line 1735, in draw
mimage._draw_list_compositing_images( File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\matplotlib\image.py",
line 137, in _draw_list_compositing_images
a.draw(renderer) File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\matplotlib\artist.py",
line 38, in draw_wrapper
return draw(artist, renderer, *args, **kwargs) File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py",
line 291, in draw
sorted(self.collections, File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py",
line 292, in
key=lambda col: col.do_3d_projection(renderer), File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpl_toolkits\mplot3d\art3d.py",
line 538, in do_3d_projection
vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M) File
"C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpl_toolkits\mplot3d\proj3d.py",
line 214, in proj_transform_clip
vec = _vec_pad_ones(xs, ys, zs) File "C:\Users\davidsr\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpl_toolkits\mplot3d\proj3d.py",
line 189, in _vec_pad_ones
return np.array([xs, ys, zs, np.ones_like(xs)]) MemoryError: Unable to allocate array with shape (4, 2040200) and data type float64
I understand the problem is memory issues, so I thought that deleting the arrays once finish with them might solve the problem, so i used del function or even using the garbage collector solution used in the following question: How can I explicitly free memory in Python?
Would appreciate some help
The figure object persists in memory if you don't .close() it first, and it includes all the data it's plotting.
Try putting a plt.close(fig) before the return statement in matrixPlotter. Then your gc.collect() statements should be able to catch it if it's not collected by matplotlib (and I don't think it is).
This is simple script on Ascii art generator from image , I get this error :
I run it in cmd line , and I am using windows 7 operating system
Traceback (most recent call last):
File "C:\Python33\mbwiga.py", line 251, in <module>
converter.convertImage(sys.argv[-1])
File "C:\Python33\mbwiga.py", line 228, in convertImage
self.getBlobs()
File "C:\Python33\mbwiga.py", line 190, in getBlobs
width, height = self.cat.get_width(), self.cat.get_height()
AttributeError: 'NoneType' object has no attribute 'get_width'
what am I messing here..?? can some one help..?
Here is full source code some one asked :
import sys
import pygame
NAME = sys.argv[0]
VERSION = "0.1.0" # The current version number.
HELP = """ {0} : An ASCII art generator. Version {1}
Usage:
{0} [-b BLOB_SIZE] [-p FONT_WIDTH:HEIGHT] [-c] image_filename
Commands:
-b | --blob Change the blob size used for grouping pixels. This is the width of the blob; the height is calculated by multiplying the blob size by the aspect ratio.
-p | --pixel-aspect Change the font character aspect ratio. By default this is 11:5, which seems to look nice. Change it based on the size of your font. Argument is specified in the format "WIDTH:HEIGHT". The colon is important.
-c | --colour Use colour codes in the output. {0} uses VT100 codes by default, limiting it to 8 colours, but this might be changed later.
-h | --help Shows this help.""""
.format(NAME, VERSION)
NO_IMAGE = \
""" Usage: %s [-b BLOB_SIZE] [-p FONT_WIDTH:HEIGHT] image_filename """ % (NAME)
import math
CAN_HAS_PYGAME = False
try:
import pygame
except ImportError:
sys.stderr.write("Can't use Pygame's image handling! Unable to proceed, sorry D:\n")
exit(-1)
VT100_COLOURS = {"000": "[0;30;40m",
"001": "[0;30;41m",
"010": "[0;30;42m",
"011": "[0;30;43m",
"100": "[0;30;44m",
"101": "[0;30;45m",
"110": "[0;30;46m",
"111": "[0;30;47m",
"blank": "[0m"}
VT100_COLOURS_I = {"000": "[0;40;30m",
"001": "[0;40;31m",
"010": "[0;40;32m",
"011": "[0;40;33m",
"100": "[0;40;34m",
"101": "[0;40;35m",
"110": "[0;40;36m",
"111": "[0;40;37m",
"blank": "[0m"}
# Convenient debug function.
DO_DEBUG = True
def debug(*args):
if not DO_DEBUG: return # Abort early, (but not often).
strrep = ""
for ii in args:
strrep += str(ii)
sys.stderr.write(strrep + "\n") # Write it to stderr. Niiicce.
# System init.
def init():
""" Start the necessary subsystems. """
pygame.init() # This is the only one at the moment...
# Get a section of the surface.
def getSubsurface(surf, x, y, w, h):
try:
return surf.subsurface(pygame.Rect(x, y, w, h))
except ValueError as er:
return getSubsurface(surf, x, y, w - 2, h - 2)
# The main class.
class AAGen:
""" A class to turn pictures into ASCII "art". """
def __init__(self):
""" Set things up for a default conversion. """
# Various blob settings.
self.aspectRatio = 11.0 / 5.0 # The default on my terminal.
self.blobW = 12 # The width. Also, the baseline for aspect ratio.
self.blobH = self.aspectRatio * self.blobW # The height.
self.blobList = []
self.cat = None # The currently open file.
self.chars = """##%H(ks+i,. """ # The characters to use.
self.colour = False # Do we use colour?
def processArgs(self):
""" Process the command line arguments, and remove any pertinent ones. """
cc = 0
for ii in sys.argv[1:]:
cc += 1
if ii == "-b" or ii == "--blob":
self.setBlob(int(sys.argv[cc + 1]))
elif ii == "-p" or ii == "--pixel-aspect":
jj = sys.argv[cc + 1]
self.setAspect(float(jj.split(":")[1]) / float(jj.split(":")[0]))
elif ii == "-c" or ii == "--colour":
self.colour = True
elif ii == "-h" or ii == "--help":
print(HELP)
exit(0)
if len(sys.argv) == 1:
print(NO_IMAGE)
exit(0)
def setBlob(self, blobW):
""" Set the blob size. """
self.blobW = blobW
self.blobH = int(math.ceil(self.aspectRatio * self.blobW))
def setAspect(self, aspect):
""" Set the aspect ratio. Also adjust the blob height. """
self.aspectRatio = aspect
self.blobH = int(math.ceil(self.blobW * self.aspectRatio))
def loadImg(self, fname):
""" Loads an image into the store. """
try:
tmpSurf = pygame.image.load(fname)
except:
print("Either this is an unsupported format, or we had problems loading the file.")
return None
self.cat = tmpSurf.convert(32)
if self.cat == None:
sys.stderr.write("Problem loading the image %s. Can't convert it!\n"
% fname)
return None
def makeBlob(self, section):
""" Blob a section into a single ASCII character."""
pxArr = pygame.surfarray.pixels3d(section)
colour = [0, 0, 0]
size = 0 # The number of pixels.
# Get the density/colours.
for i in pxArr:
for j in i:
size += 1
# Add to the colour.
colour[0] += j[0]
colour[1] += j[1]
colour[2] += j[2]
# Get just the greyscale.
grey = apply(lambda x, y, z: (x + y + z) / 3 / size,
colour)
if self.colour:
# Get the 3 bit colour.
threshold = 128
nearest = ""
nearest += "1" if (colour[0] / size > threshold) else "0"
nearest += "1" if (colour[1] / size > threshold) else "0"
nearest += "1" if (colour[2] / size > threshold) else "0"
return VT100_COLOURS[nearest], grey
return grey
# We just use a nasty mean function to find the average value.
# total = 0
# for pix in pxArr.flat:
# total += pix # flat is the array as a single-dimension one.
# return total / pxArr.size # This is a bad way to do it, it loses huge amounts of precision with large blob size. However, with ASCII art...
def getBlobs(self):
""" Get a list of blob locations. """
self.blobList = [] # Null it out.
width, height = self.cat.get_width(), self.cat.get_height()
# If the image is the wrong size for blobs, add extra space.
if height % self.blobH != 0 or width % self.blobW != 0:
oldimg = self.cat
newW = width - (width % self.blobW) + self.blobW
newH = height - (height % self.blobH) + self.blobH
self.cat = pygame.Surface((newW, newH))
self.cat.fill((255, 255, 255))
self.cat.blit(oldimg, pygame.Rect(0, 0, newW, newH))
# Loop over subsections.
for row in range(0, height, int(self.blobH)):
rowItem = []
for column in range(0, width, self.blobW):
# Construct a Rect to use.
src = pygame.Rect(column, row, self.blobW, self.blobH)
# Now, append the reference.
rowItem.append(self.cat.subsurface(src))
self.blobList.append(rowItem)
return self.blobList
def getCharacter(self, value, colour = False):
""" Get the correct character for a pixel value. """
col = value[0] if colour else ""
value = value[1] if colour else value
if not 0 <= value <= 256:
sys.stderr.write("Incorrect pixel data provided! (given %d)\n"
% value)
return "E"
char = self.chars[int(math.ceil(value / len(self.chars))) % len(self.chars)]
return char + col
def convertImage(self, fname):
""" Convert an image, and print it. """
self.loadImg(fname)
self.getBlobs()
pval = "" # The output value.
# Loop and add characters.
for ii in converter.blobList:
for jj in ii:
ch = self.makeBlob(jj)
pval += self.getCharacter(ch, self.colour) # Get the character.
# Reset the colour at the end of the line.
if self.colour: pval += VT100_COLOURS["blank"]
pval += "\n" # Split it up by line.
pval = pval[:-1] # Cut out the final newline.
print(pval) # Print it.
# Main program execution.
if __name__ == "__main__":
init()
converter = AAGen()
converter.processArgs()
converter.convertImage(sys.argv[-1])
sys.exit(1)
The problem is hidden somewhere in the loadImg. The error says that self.cat is None. The self.cat could get the None when initialised at the line 97, or it was assigned the result of tmpSurf.convert(32) and the result of that call is None. In the first case, you should observe the message Either this is an unsupported format..., in the later case you should see the message Problem loading the image... as you are testing self.cat against None:
def loadImg(self, fname):
""" Loads an image into the store. """
try:
tmpSurf = pygame.image.load(fname)
except:
print("Either this is an unsupported format, or we had problems loading the file.")
return None
self.cat = tmpSurf.convert(32)
if self.cat == None:
sys.stderr.write("Problem loading the image %s. Can't convert it!\n"
% fname)
return None
By the way, return None is exactly the same as return without argument. Also, the last return None can be completely removed because any function implicitly returns None when the end of the body is reached.
For testing to None, the is operator is recommended -- i.e. if self.cat is None:.
Update based on the comment from May 31.
If you want to make a step further, you should really learn Python a bit. Have a look at the end of the original script (indentation fixed):
# Main program execution.
if __name__ == "__main__":
init() # pygame is initialized here
converter = AAGen() # you need the converter object
converter.processArgs() # the command-line arguments are
# converted to the object attributes
converter.convertImage(sys.argv[-1]) # here the conversion happens
sys.exit(1) # this is unneccessary for the conversion
If the original script is saved in the mbwiga.py, then you can or call it as a script, or you can use it as a module. In the later case, the body below the if __name__ == "__main__": is not executed, and you have to do it in the caller script on your own. Say you have test.py that tries to do that. Say it is located at the same directory. It must import the mbwiga. Then the mbwiga. becomes the prefix of the functionality from inside the module. Your code may look like this:
import mbwiga
mbwiga.init() # pygame is initialized here
converter = mbwiga.AAGen() # you need the converter object
# Now the converter is your own object name. It does not take the mbwiga. prefix.
# The original converter.processArgs() took the argumens from the command-line
# when mbwiga.py was called as a script. If you want to use some of the arguments
# you can set the converter object's attributes the way that is shown
# in the .processArgs() method definition. Or you can call it the same way to
# extract the information from the command line passed when you called the test.py
#
converter.processArgs()
# Now the conversion
converter.convertImage('myImageFilename.xxx') # here the conversion happens