Creating large image from csv data

Question:

I’m trying to create a very large image from CSV file data. The image will be used as a material texture in rendering software, so there is some motivation to avoid splitting the image up.
The image can be monochromatic – just a series of filled circles on a background (doesn’t matter whether is black on white or vice versa).
The CSV data is 3 columns – an X and Y coordinate (in pixels), and an ‘R’ value which represents the radius of the circle (in pixels). There are something like 55,000 lines in the CSV file

I’m using OpenCV to create the file, but the image size (smallest of 10 files) is 2800px x 57000px.

The problem (unsurprisingly) is that it’s VERY slow to execute – just under 1 sec per line of the csv file – I expect this is due to the large image size. Tests with smaller CSV files, but the same image size had similar results.

So I have 2 questions:

  1. Generally speaking – what can I do to speed up execution? (Preferably keeping the spirit of what we have so far – e.g. not moving to other languages etc)
  2. I’d like to limit the file to just monochrome instead of RGB (assuming this helps). How do I do this? I couldn’t find any examples that seemed to fit my use case.

Here’s my code:

import cv2
import numpy as np
import datetime
import csv


# Reading an image in default mode
Img = np.zeros((57000, 2800, 3), np.uint8)
 
with open('C:TempOPENCV.csv', newline='') as csvfile:
    Data = csv.DictReader(csvfile)
# Window name in which image is displayed
window_name = 'Preview'
print("Start: " + str(datetime.datetime.now()))
count = 0
for row in Data: 
    if count != 0:

        # Center coordinates
        print(row)
        #print(row['X'],row['Y'])
        center_coordinates = (int(row['X']),int(row['Y']))
        
        # Radius of circle
        radius = int(row['R'])
        
        # Red color in BGR
        color = (255, 255, 255)
        
        # Line thickness of -1 px
        thickness = -1
        
        # Using cv2.circle() method
        # Draw a circle of red color of thickness -1 px
        image = cv2.circle(Img, center_coordinates, radius, color, thickness)

        cv2.imwrite("C:TempCVTest.png", image)
    count = count+1

print("End: " + str(datetime.datetime.now()))
# Displaying the image
cv2.namedWindow(window_name, cv2.WINDOW_AUTOSIZE)
cv2.imshow(window_name, image)
cv2.waitKey(0)
cv2.destroyAllWindows()

EDIT: Oversight on my part – moving cv2.imwrite out of the for loop made a massive difference. Instead of ~1 sec per line, it was ~2 seconds for the entire image!

EDIT: For future reference. here’s what I learned about making the image monochromatic. For OpenCV, change the Zeros array to np.zeros((58000,2800), np.uint8, then add the [cv2.IMWRITE_PNG_BILEVEL,1] parameter to the imwrite line. But a more filesize efficient way is to use pil. For this, I use OpenCV to create the image, then converted it to a numpy array, then converted that to a pil image and finally saved it as a 1 bit image.

#'image' is my OpenCV image
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
im_pil = Image.fromarray(img)
im_pil.save('C:TempCVTest2.png',bits=1,optimize=True)

In one example, this resulted in a 2687 kb file for OpenCV, and 1188 kb for pil. There may be an even more efficient way to create the image without OpenCV, but I haven’t tried yet. This works well enough for me.

Asked By: G.H.

||

Answers:

You can speed up execution by not saving the picture in every iteration, like you do so far.

Saving the picture takes time. If it’s a large image, it takes more time.

Just save it once, when you’re done.


Monochrome:

  • change the shape of the numpy array (2-dimensional, no third dimension)
  • draw circles having a scalar for color instead of a color tuple
Answered By: Christoph Rackwitz
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.