how to dump the object detection labels into the pickle? - object

I have the labels of the object detection like the followings.
item{
id: 1
name: 'red_light'
}
item{
id: 2
name: 'blue_light'
}
item{
id: 3
name: 'blue_left'
}
item{
id: 4
name:'red_left'
}
{
id: 5
name: 'yellow_light'
}
Using the labels in the ssd moblienet v2 coco, I am going to detect the traffic signals. How can I create the pickle for the labels? When I load the pickle data, it should be like
{1:'red_light', 2:'blue_light', 3:'blue_left', 4:'red_left', 5:'yellow_light'}
I will appreciate it your advice.

It is not wise to stick to the original labels format and try to convert it. Instead just input the desired output like the following.
import argparse
import pickle
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-l", "--labels", required=True,
help="path to output labels file")
args = vars(ap.parse_args())
LABEL_ENCODINGS = {"red_light": 1,
"blue_light": 2,
"blue_left": 3,
"red_left": 4,
"yellow_light": 5,
}
# reverse the label encoding dictionary
labels = {v: k for (k, v) in LABEL_ENCODINGS.items()}
# save the class labels to disk
f = open(args["labels"], "wb")
f.write(pickle.dumps(labels))
f.close()

Related

ListError when mapping Pydantic class to csv

I am trying to use pydantic classes to represent records of a CSV. Some fields in this CSV represent things like numbers, dates, encoded lists that are better handled as such. So I assign the appropriate type to the coresponding pydantic field and rely on pydantic to cast the string to the type. Unfortunately this fails for lists.
from typing import List
import csv
from pydantic import BaseModel
class Foo(BaseModel):
a: int
b: List[str]
c: str
# Write class to CSV
x = Foo(a=1, b=["hello", "world"], c="foo")
with open("/tmp/test.csv", "w") as f:
writer = csv.DictWriter(f, fieldnames=x.dict().keys())
writer.writeheader()
writer.writerow(x.dict())
# Try to load the class back from CSV
with open("/tmp/test.csv") as f:
reader = csv.DictReader(f)
y = Foo(**next(reader))
I expect that y would be instance with the same values as x, but instead it crashes with ListError. This code does succeed in outputting /tmp/test.csv, and its contents are:
a,b,c
1,"['hello', 'world']",foo
How can I solve this problem?
So, here is how I would do what you want to do:
from typing import List
from pydantic import BaseModel, Field, validator
import json
class Foo(BaseModel):
bar: int = None
baz: List[pydantic.StrictStr] = Field(default_factory=list)
#validator('baz', pre=True)
def _maybe_json(cls, v):
if isinstance(v, str):
try:
return json.loads(v)
except json.JSONDecodeError as e:
raise ValueError("not valid JSON") from e
return v
def to_csv_row(self):
row = self.dict()
row["baz"] = json.dumps(row["baz"])
return row
Note how StrictStr handles this:
In [4]: Foo(baz='["a"]')
Out[4]: Foo(bar=None, baz=['a'])
In [5]: Foo(baz='[1]')
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Input In [22], in <cell line: 1>()
----> 1 Foo(baz='[1]')
File ~/miniconda3/envs/maze-etl/lib/python3.9/site-packages/pydantic/main.py:331, in pydantic.main.BaseModel.__init__()
ValidationError: 1 validation error for Foo
baz -> 0
str type expected (type=type_error.str)
But if you don't want that just use List[str]
And just use it like:
In [10]: foo = Foo(bar=1, baz=['a','b','c'])
In [11]: foo
Out[11]: Foo(bar=1, baz=['a', 'b', 'c'])
In [12]: foo.to_csv_row()
Out[12]: {'bar': 1, 'baz': '["a", "b", "c"]'}
The solution I found was to create a validator that checks the value being passed, and if it's a string, tries to eval it to a Python list.
class Foo(BaseModel):
a: int
b: List[str]
c: str
#validator("b", pre=True)
def eval_list(cls, val):
if isinstance(val, List):
return val
else:
return ast.literal_eval(val)
This can of course potentially allow people to inject Python code via the CSV, and it is possible to construct lists which cannot be reconstructed from their string representation. In my case, the CSVs are all created by me and the lists are simple lists of string, so this limitation is not a problem.

Unpacking a tuple within a list using spaCy's char_span

I have a dict that looks like this:
TRAIN_DATA = {'here is some text': [('1', '4', 'entity_label')], 'here is more text': [('2', '7', 'entity_label_2')], 'and even more text': [('1', '4', 'entity_label')]}
I'm trying to convert this to the format required for spaCy's NER model, using the following:
import pandas as pd
import spacy
from spacy.tokens import DocBin
nlp = spacy.blank("en") # load a new spacy model
db = DocBin() # create a DocBin object
for text, annot in TRAIN_DATA: # data in previous format
doc = nlp.make_doc(text) # create doc object from text
ents = []
for start, end, label in annot: # add character indexes
span = doc.char_span(start, end, label=label, alignment_mode="contract")
if span is None:
print("Skipping entity")
else:
ents.append(span)
doc.ents = ents # label the text with the ents
db.add(doc)
db.to_disk("train.spacy") # save the docbin object
It yields ValueError: not enough values to unpack (expected 3, got 2)
When I try something slightly different:
nlp = spacy.blank("en") # load a new spacy model
db = DocBin() # create a DocBin object
for body, [(entities)] in TRAIN_DATA.items():
doc = nlp(body)
ents = []
for start, end, label in entities:
span = doc.char_span(int(start), int(end), label=label, alignment_mode='contract')
ents.append(span)
doc.ents = ents
db.add(doc)
db.to_disk("train.spacy")
It yields the same error. When I remove the tuple and list notation (i.e. for body, entities... vs for body, [(entities)]) I get expected 2, got 3 instead of expected 3 got 2...
I've tried troubleshooting by unpacking the tuple manually (i.e. for i in entities.split(", ") print (i), and that seems to find all the values in the tuple, so I'm not sure what I'm doing wrong.

Converting TFRECORD file to text data

I have converted a .txt file to tfrecords with some changes to it. But now I want to convert or read same file so I could understand my data which is now changed. I am doing this for my knowledge graph project.
import numpy as np
import os
import tensorflow as tf
import tqdm
import pdb
import glob
import time
import sys
import re
import argparse
import fastBPE
import platform
use_py3 = platform.python_version()[0] == '3'
parser = argparse.ArgumentParser(description='TensorFlow code for creating TFRecords data')
parser.add_argument('--text_file', type=str, required=True,
help='location of text file to convert to TFRecords')
parser.add_argument('--control_code', type=str, required=True,
help='control code to use for this file. must be in the vocabulary, else it will error out.')
parser.add_argument('--sequence_len', type=int, required=True,
help='sequence length of model being fine-tuned (256 or 512)')
args = parser.parse_args()
path_to_train_file = fname = args.text_file
domain = [args.control_code]
train_text = open(path_to_train_file, 'rb').read().decode(encoding='utf-8')
bpe = fastBPE.fastBPE('../codes', '../vocab')
tokenized_train_text = bpe.apply([train_text.encode('ascii', errors='ignore') if not use_py3 else train_text])[0] # will NOT work for non-English texts
# if you want to run non-english text, please tokenize separately using ./fast applybpe and then run this script on the .bpe file with utf8 encoding
tokenized_train_text = re.findall(r'\S+|\n', tokenized_train_text)
tokenized_train_text = list(filter(lambda x: x != u'##', tokenized_train_text))
# load the vocabulary from file
vocab = open('../vocab').read().decode(encoding='utf-8').split('\n') if not use_py3 else open('../vocab', encoding='utf-8').read().split('\n')
vocab = list(map(lambda x: x.split(' ')[0], vocab)) + ['<unk>'] + ['\n']
print ('{} unique words'.format(len(vocab)))
if args.control_code not in vocab:
print('Provided control code is not in the vocabulary')
print('Please provide a different one; refer to the vocab file for allowable tokens')
sys.exit(1)
# Creating a mapping from unique characters to indices
word2idx = {u:i for i, u in enumerate(vocab)}
idx2word = np.array(vocab)
seq_length = args.sequence_len-1
def numericalize(x):
count = 0
for i in x:
if i not in word2idx:
print(i)
count += 1
return count>1, [word2idx.get(i, word2idx['<unk>']) for i in x]
tfrecords_fname = fname.lower()+'.tfrecords'
total = 0
skipped = 0
with tf.io.TFRecordWriter(tfrecords_fname) as writer:
for i in tqdm.tqdm(range(0, len(tokenized_train_text), seq_length)):
flag_input, inputs = numericalize(domain+tokenized_train_text[i:i+seq_length])
flag_output, outputs = numericalize(tokenized_train_text[i:i+seq_length+1])
total += 1
if flag_input or flag_output:
skipped += 1
continue
if len(inputs)!=seq_length+1 or len(outputs)!=seq_length+1:
break
example_proto = tf.train.Example(features=tf.train.Features(feature={'input': tf.train.Feature(int64_list=tf.train.Int64List(value=inputs)),
'output': tf.train.Feature(int64_list=tf.train.Int64List(value=outputs))}))
writer.write(example_proto.SerializeToString())
print('Done')
print('Skipped', skipped, 'of', total)
This is my code I want every changes in it except that to convert in tfrecords.
Read the TFRecord with a TFRecordDataset.
Then iterate through the TFRecordDataset and for each element, write to a new text file or print out the results.
https://www.tensorflow.org/api_docs/python/tf/data/TFRecordDataset

How to operate on a string while keep track of each char's score?

Problem
I have a string and scores for each char in the string
text = 'text'
scores = [0.99, 0.98, 0.97, 0.96]
I would like to do many string operations to text, like re, +, slice or split, after do these operations, I want to keep correspond scores. For example, if I do a slice operation I want get sliced_text and sliced_scores as result:
sliced_text = text[0:2]
sliced_scores = scores[0:2]
Python3 has UserString class, for some simple operations like slice or add, it's very useful.
from collections import UserString
from typing import List
class ScoreString(UserString):
def __init__(self, text: str, scores: List[float]):
super().__init__(text)
assert len(text) == len(scores)
self.scores = scores
def __getitem__(self, index) -> 'ScoreString':
return self.__class__(self.data[index], self.scores[index])
def __add__(self, other) -> 'ScoreString':
return self.__class__(self.data + other.data, self.scores + other.scores)
score_str = ScoreString('Test123', [1, 2, 3, 4, 5, 6, 7])
a = score_str[0:2] + score_str[4:]
print(a.data) # Te123
print(a.scores) # [1, 2, 5, 6, 7]
But split or re not work.
print(score_str.split('12')) # return string list ['Test', '3'], lose scores
import re
re.sub('123', '', score_str) # TypeError: expected string or bytes-like object
Any suggestion?
Actually what you are trying to do in split function
score_str.split('12')
--> Here score_str is a class object which is inheriting UserString, so when you apply split function it will call the function of base class and you will get a normal output as str.split().
import re
re.sub('123', '', score_str)
--> Here when you are trying to implement regular expression, Then sub functions have arguments sub(pattern, repl, string, count=0, flags=0), but here in third argument you are passing score_str, which is an object of ScoreString. That is why you are getting error.
If you will re-write as below,
re.sub('123', '', score_str.data)
It should work..
I hope this will help.

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.

Resources