How to change code to read and create multiple xml files? - python

I have this code that I am using to change the images I have saved in a folder called 'images' from .png to .xml with the additional information about them. When I run this code I only get the .xml file for image 000001 which I understand because I am having the code select that specific image. I am unsure how though to select multiple images in my file at a single time. I have images named from 000000 to 000355. Any advice would be great! really do not want to manually run the code 355 times!
import os
import cv2
from lxml import etree
import xml.etree.cElementTree as ET
def write_xml(folder, img, objects, tl, br, savedir):
if not os.path.isdir(savedir):
os.mkdir(savedir)
image = cv2.imread(img.path)
height, width, depth = image.shape
annotation = ET.Element('annotation')
ET.SubElement(annotation, 'folder').text = folder
ET.SubElement(annotation, 'filename').text = img.name
ET.SubElement(annotation, 'segmented').text = '0'
size = ET.SubElement(annotation, 'size')
ET.SubElement(size, 'width').text = str(width)
ET.SubElement(size, 'height').text = str(height)
ET.SubElement(size, 'depth').text = str(depth)
for obj, topl, botr in zip(objects, tl, br):
ob = ET.SubElement(annotation, 'object')
ET.SubElement(ob, 'name').text = obj
ET.SubElement(ob, 'pose').text = 'Unspecified'
ET.SubElement(ob, 'truncated').text = '0'
ET.SubElement(ob, 'difficult').text = '0'
bbox = ET.SubElement(ob, 'bndbox')
ET.SubElement(bbox, 'xmin').text = str(topl[0])
ET.SubElement(bbox, 'ymin').text = str(topl[1])
ET.SubElement(bbox, 'xmax').text = str(botr[0])
ET.SubElement(bbox, 'ymax').text = str(botr[1])
xml_str = ET.tostring(annotation)
root = etree.fromstring(xml_str)
xml_str = etree.tostring(root, pretty_print=True)
save_path = os.path.join(savedir, img.name.replace('png', 'xml'))
with open(save_path, 'wb') as temp_xml:
temp_xml.write(xml_str)
if __name__ == '__main__':
"""
for testing
"""
folder = 'images'
img = [im for im in os.scandir('images') if '000001' in im.name][0]
objects = ['auv']
tl = [(10, 10)]
br = [(100, 100)]
savedir = 'annotations'
write_xml(folder, img, objects, tl, br, savedir)

The basic idea is to make loop going through each of your image files, and do what you did for a single image before for each:
for img in os.scandir('images'):
objects = ['auv']
tl = [(10, 10)]
br = [(100, 100)]
savedir = 'annotations'
write_xml(folder, img, objects, tl, br, savedir)
(You might need to change the expression for your list of images, as it might now include things you don't want to process.)

Related

OpenCV, Python, cv2 error: (-5:Bad argument) Empty training data was given.You'll need more than one sample to learn a model. in function 'train'

This is my code. I have a learning problem. Gives the error
error: (-5:Bad argument) Empty training data was given. You'll need more than one sample to learn a model. in function 'train'.
I can't solve this problem. I can't find an explanation on how to fix it? Where can I read about the solution to this problem? My pictures have size 200x200, format .pgm.
import os
import cv2
import numpy as np
def read_images(path, image_size):
names = []
training_images, training_labels = [], []
label = 0
for dirname, subdirnames, filenames in os.walk(path):
for subdirname in subdirnames:
names.append(subdirname)
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
img = cv2.imread(os.path.join(subject_path, filename),
cv2.IMREAD_GRAYSCALE)
if img is None:
# The file cannot be loaded as an image.
# Skip it.
continue
img = cv2.resize(img, image_size)
training_images.append(img)
training_labels.append(label)
label += 1
training_images = np.asarray(training_images, np.uint8)
training_labels = np.asarray(training_labels, np.int32)
return names, training_images, training_labels
path_to_training_images = '/home/ace/OpenCV/cascades/A_M'# not properly. This is = '/home/ace/OpenCV/cascades/'
training_image_size = (200, 200)
names, training_images, training_labels = read_images(path_to_training_images, training_image_size)
model = cv2.face.EigenFaceRecognizer_create()
model.train(training_images, training_labels)
face_cascade = cv2.CascadeClassifier('/haarcascade_frontalface_default.xml')
camera = cv2.VideoCapture(2)
while (cv2.waitKey(1) == -1):
success, frame = camera.read()
if success:
faces = face_cascade.detectMultiScale(frame, 1.3, 5)
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
roi_gray = gray[x:x+w, y:y+h]
if roi_gray.size == 0:
# The ROI is empty. Maybe the face is at the image edge.
# Skip it.
continue
roi_gray = cv2.resize(roi_gray, training_image_size)
label, confidence = model.predict(roi_gray)
text = '%s, confidence=%.2f' % (names[label], confidence)
cv2.putText(frame, text, (x, y - 20),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
cv2.imshow('Face Recognition', frame)
Although it seems a little bit odd that you use recursive walk-function and then seperately list the subdirectories, your read_images function works for me if I create a folder structure like this:
images/
- sub1/
- img1.png
- img2.png
- sub2/
- img3.png
- img4.png
The files are found and the return values are filled. Probably your folder structure differs from what your code expects.
You can try to debug your setup with some further print-statements like this:
def read_images(path, image_size):
names = []
training_images, training_labels = [], []
label = 0
for dirname, subdirnames, filenames in os.walk(path):
print(f"dirname={dirname}")
for subdirname in subdirnames:
print(f"- subdirname={subdirname}")
names.append(subdirname)
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
print(f" - filename={filename}")
img = cv2.imread(os.path.join(subject_path, filename),
cv2.IMREAD_GRAYSCALE)
if img is None:
# The file cannot be loaded as an image.
# Skip it.
continue
img = cv2.resize(img, image_size)
training_images.append(img)
training_labels.append(label)
label += 1
training_images = np.asarray(training_images, np.uint8)
training_labels = np.asarray(training_labels, np.int32)
return names, training_images, training_labels
Please check the output of those prints and compare it with your folder structure. If this doesn't help, please share the debug output and your folder structure with us. Please don't share it as comment but by updating your question!

convert Image to PDF in Python

I want to convert image to pdf in python.
this is my code:
import docx
from docx.shared import Inches, Mm
import os
from PIL import Image
from PIL import Image, ImageDraw, ImageFont
from docx2pdf import convert
from wand.image import Image as Im
image_dir = os.listdir(os.getcwd()+'\\Images')
print(len(image_dir))
doc = docx.Document()
section = doc.sections[0]
section.page_height = Mm(1000)
section.page_width = Mm(580)
section.left_margin = Mm(25.4)
section.right_margin = Mm(25.4)
section.top_margin = Mm(25.4)
section.bottom_margin = Mm(25.4)
section.header_distance = Mm(12.7)
section.footer_distance = Mm(12.7)
p = doc.add_paragraph()
x = 0
for i in range(0, len(image_dir)):
size = (130, 160)
temp_img = Image.open(os.getcwd()+'\\Images\\'+image_dir[i])
temp_img = temp_img.resize(size)
# temp_img.thumbnail(size, Image.ANTIALIAS)
# temp_img.show()
background = Image.new('RGBA', (500, 220), (255, 255, 255, 0))
for k in range(0, 3):
background.paste(temp_img, (0,0))
background.paste(temp_img, (150,0))
background.paste(temp_img, (300,0))
font = ImageFont.truetype(r'arial.ttf', 25)
d1 = ImageDraw.Draw(background)
d1.text((5, 160), image_dir[i][:-4], fill =(0, 0, 0), font = font)
background.save("temp.png")
with Im(filename ="temp.png") as img:
# generating sharp image using sharpen() function.
img.sharpen(radius = 16, sigma = 8)
img.save(filename ="temp1.png")
r = p.add_run()
r.add_picture("temp1.png")
doc.save('demo1.docx')
convert("demo1.docx")
This code run well. But IMG quality of pdf is poor and process is very slowly.
I want to improve convert speed.
Somebody help me. Thank you.
I think PIL is enough for you to combine an image with text into a PDF.
for example, you can save the combined images with text like this
import os
from PIL import Image, ImageDraw, ImageFont
image_dir = os.listdir(os.getcwd()+'\\Images')
for i in range(0, len(image_dir)):
size = (130, 160)
temp_img = Image.open(os.getcwd()+'\\Images\\'+image_dir[i])
temp_img = temp_img.resize(size)
background = Image.new('RGB', (500, 220), (255, 255, 255))
for k in range(0, 3):
background.paste(temp_img, (0,0))
background.paste(temp_img, (150,0))
background.paste(temp_img, (300,0))
font = ImageFont.truetype('arial.ttf', 25)
d1 = ImageDraw.Draw(background)
d1.text((5, 160), image_dir[i][:-4], fill =(0, 0, 0), font = font)
background.save(f"{image_dir[i][:-4]}.pdf")
This is my try. Just give the directory path and its done; you end up with a subdirectory containing all the PDF files
from pathlib import *
from PIL import Image
# path input #
path = input("Enter The path of you Images directory: \n")
path = Path(f"{path}")
####################################################
# making a subdirectory to contain the PDF version #
(path/"PDF's").mkdir(exist_ok=True)
# iterating over every file in the given directory #
# we use try function to ignore non image files #
for pp in path.glob("*"):
try:
with Image.open(pp) as image:
im = image.convert("RGB")
pth = Path(f"{path}\PDF's\{pp.stem}.pdf")
im.save(pth, save_all=True)
except:
pass
print("Conversion Completed ...")
# if you want to merge PDF's uncomment the next segment
#import PyPDF2
# merger = PyPDF2.PdfFileMerger()
# path = Path(f"{path}\PDF's")
# # (path/"MergedPDF's").touch()
# if (path/"MergedPDF's.pdf").exists():
# (path/"MergedPDF's.pdf").unlink()
# for pdf in path.glob("*.pdf"):
# merger.append(PyPDF2.PdfReader(pdf, "rb"))
# output = open(path/"MergedPDF's.pdf", "wb")
# merger.write(output)
# merger.close()
Pillow solution is pretty good in my opinion. But if u need more control over your pdf I suggest u to use PyMuPDF. Best library for pdf manipulation, hands down.
# copy image files to PDF pages
# each page will have image dimensions
import fitz
doc = fitz.open() # new PDF
imglist = [ ... image file names ...] # e.g. a directory listing
for img in imglist:
imgdoc=fitz.open(img) # open image as a document
pdfbytes=imgdoc.convert_to_pdf() # make a 1-page PDF of it
imgpdf=fitz.open("pdf", pdfbytes)
doc.insert_pdf(imgpdf) # insert the image PDF
doc.save("allmyimages.pdf")
Is pretty handy if u want to add metadata:
import fitz
doc = fitz.open()
metadata = {'producer': 'YourName', 'format': 'PDF 1.4', 'encryption': None, 'author': 'YourName',
'modDate': 'none', 'keywords': 'none', 'title': 'YourPdf', 'creationDate': 'none',
'creator': 'none', 'subject': 'none'} # your metadata needs to be a dictionary
doc.set_metadata(metadata)
table of contents:
import fitz
doc = fitz.open()
# the toc is a basically a list of lists. Each list has 3 elements:
# - first: the layer of the toc link (basically the main category (1), subcategory (2) etc..)
# - second: title of the layer
# - third: page where the title is linked
table_of_content = [[1, 'The PyMuPDF Documentation', 1], [2, 'Introduction', 1], [3, 'Note on the Name fitz', 1], [3, 'License', 1]]
doc.set_toc(table_of_content)
etc... I think that giving a look at the documentation is pretty useful

Reading and detecting images, and save in folder

Firstly, I am simply reading images from the folder that contains different image formats. Secondly, the YOLO model detects the class, draws a rectangle and fills it with color only detected part, and saves it into another folder with the same name.
Second Case, If the model didn't detect anything in an image then it will save the same image with the same name but in a different folder.
My codebase is stuck on the first image and never moves on to the second image. I have no idea what is the problem happening.
Code
import torch
import cv2
from matplotlib import pyplot as plt
from utils.plots import Annotator, colors, save_one_box
import os
import glob
# Load Ours Custom Model
model = torch.hub.load('.', 'custom', path='/media/bmvc/CM_1/yolov5/runs/train/exp4/weights/last.pt', source='local')
# Files extension
img_Extension = ['jpg', 'jpeg', 'png']
# Load all testing images
my_path = "/home/bmvc/Documents/hide_info_test_dataset/testing_images/"
# Save images into array
files = []
[files.extend(glob.glob(my_path + '*.' + e)) for e in img_Extension]
# Iteration on all images
images = [cv2.imread(file) for file in files]
total_images = 1
# Taking only image name to save with save name
image_file_name = ''
for file in files:
for im in images:
detections = model(im[..., ::-1])
results = detections.pandas().xyxy[0].to_dict(orient="records")
if len(results) == 0:
cv2.imwrite(os.path.join("/home/bmvc/Documents/hide_info_test_dataset/detected/", file), im)
else:
for result in results:
print(result['class'])
con = result['confidence']
cs = result['class']
x1 = int(result['xmin'])
y1 = int(result['ymin'])
x2 = int(result['xmax'])
y2 = int(result['ymax'])
imagee = cv2.rectangle(im, (x1, y1), (x2, y2), (0, 255, 0), -1)
cv2.imwrite(os.path.join("/home/bmvc/Documents/hide_info_test_dataset/detected/", file), im)
total_images += 1
I have put a lot of loops that are completely useless for example reading different extension files, reading only images. I have improved the overall implementation and used only one loop to fix the above problem.
import torch
import cv2
from PIL import Image
from utils.plots import Annotator, colors, save_one_box
import os
import glob
import numpy as np
# Load Ours Custom Model
model = torch.hub.load('.', 'custom', path='/media/bmvc/CM_1/yolov5/runs/train/exp4/weights/last.pt', source='local')
# Files extension
img_Extension = ['jpg', 'jpeg', 'png']
# Load all testing images
my_path = "/home/bmvc/Documents/hide_info_test_dataset/testing_images/"
# Save images into array
files = []
[files.extend(glob.glob(my_path + '*.' + e)) for e in img_Extension]
# Iteration on all images
images = [cv2.imread(file) for file in files]
total_images = 1
# Taking only image name to save with save name
image_file_name = ''
for img in glob.glob(my_path + '*.*'):
img_bgr_rgb = cv2.imread(img)
file_Name = os.path.basename(img)
detections = model(img_bgr_rgb[:, :, ::-1])
results = detections.pandas().xyxy[0].to_dict(orient="records")
if len(results) == 0:
cv2.imwrite(os.path.join("/home/bmvc/Documents/hide_info_test_dataset/detected/", file_Name), img_bgr_rgb)
else:
for result in results:
print(result['class'])
con = result['confidence']
cs = result['class']
x1 = int(result['xmin'])
y1 = int(result['ymin'])
x2 = int(result['xmax'])
y2 = int(result['ymax'])
imagee = cv2.rectangle(img_bgr_rgb, (x1, y1), (x2, y2), (255, 87, 51), -1)
cv2.imwrite(os.path.join("/home/bmvc/Documents/hide_info_test_dataset/detected/", file_Name), img_bgr_rgb)

Python dict to csv

I have written a script to find image size and aspect ratio of all images in a directory along with their corresponding filepaths, I want to print dict values to csv file with following headers width,height,aspect-ratio and filepath
import os
import json
from PIL import Image
folder_images = "/home/user/Desktop/images"
size_images = dict()
def yocd(a,b):
if(b==0):
return a
else:
return yocd(b,a%b)
for dirpath, _, filenames in os.walk(folder_images):
for path_image in filenames:
if path_image.endswith(".png") or path_image.endswith('.jpg') or path_image.endswith('.JPG') or path_image.endswith('.jpeg'):
image = os.path.abspath(os.path.join(dirpath, path_image))
""" ImageFile.LOAD_TRUNCATED_IMAGES = True """
try:
with Image.open(image) as img:
img.LOAD_TRUNCATED_IMAGES = True
img.verify()
print('Valid image')
except Exception:
print('Invalid image')
img = False
if img is not False:
width, heigth = img.size
divisor = yocd(width, heigth)
w = str(int(width / divisor))
h = str(int(heigth / divisor))
aspectratio = w+':'+h
size_images[image] = {'width': width, 'heigth': heigth,'aspect-ratio':aspectratio,'filepath': image}
for k, v in size_images.items():
print(k, '-->', v)
with open('/home/user/Documents/imagesize.txt', 'w') as file:
file.write(json.dumps(size_images))```
You can add a (properly constructed) dict directly to a pandas.DataFrame. Then, DataFrames have a .to_csv() function.
Here are the docs:
Pandas: Create a DataFrame
Pandas: Write to CSV
Without dependencies (but you may have to tweak the formatting)
csv_sep = ';' # choose here wich field separatar you want
with open('your_csv', 'w') as f:
# header
f.write("width"+csv_sep"+height"+csv_sep"+aspect-ratio"+csv_sep+"filepath\n")
# data
for img in size_images:
fields = [img['width'], img['height'], img['aspect-ratio'], img['filepath']]
f.write(csv_sep.join(fields)+'\n')

OpenCV Panorama: TypeError: cannot unpack non-iterable NoneType object

I'm trying to make a panorama from a image set. I'm using Spyder, OpenCV 3.4 and Python 3.7. Here's the code:
The main:
from stitches import Stitcher
#from PIL import Image
import os
import glob
import numpy as np
import imutils
import cv2
cap = cv2.VideoCapture('C:/Users/VID_20181208_111037881.mp4')
img_dir = "C:/Users/user/images"
data_path = os.path.join(img_dir, '*g')
files = glob.glob(data_path)
args = []
for f1 in files:
img = cv2.imread(f1)
args.append(img)
def retImg(img):
return img
for i in args:
j = i+1
frame = retImg(i)
frame2 = retImg(j)
#imageA = cv2.imread(frame)
#imageB = cv2.imread(frame2)
imageA = imutils.resize(frame, width=400)
imageB = imutils.resize(frame2, width=400)
# stitch the images together to create a panorama
stitcher = Stitcher()
(result, vis) = stitcher.stitch([imageA, imageB], showMatches=False)
frame = result
# show the images
cv2.imshow("Keypoint Matches", vis)
cv2.imshow("Result", result)
cv2.waitKey(0)
The stitch inside stitches.py:
def stitch(self, images, ratio=0.75, reprojThresh=4.0, showMatches=False):
# unpack the images, then detect keypoints and extract
# local invariant descriptors from them
(imageB, imageA) = images
(kpsA, featuresA) = self.detectAndDescribe(imageA)
(kpsB, featuresB) = self.detectAndDescribe(imageB)
# match features between the two images
M = self.matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh)
# if the match is None, then there aren't enough matched
# keypoints to create a panorama
if M is None:
return None
# together
(matches, H, status) = M
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
# check to see if the keypoint matches should be visualized
if showMatches:
vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches, status)
# return a tuple of the stitched image and the
# visualization
return (result, vis)
# return the stitched image
return result
When I compile it, the following error is shown:
File "C:/Users/user/panorama.py", line 44, in <module>
(result, vis) = stitcher.stitch([imageA, imageB], showMatches=False)
TypeError: cannot unpack non-iterable NoneType object
And I cannot understand why. I'm new to Python, so maybe this mistake is really simple, but I don't know what to do here. Thanks by advance!

Categories