AttributeError: 'NoneType' object has no attribute 'copy'

Hi,

My repo for the project I am creating is: GitHub - IIPeteII/car-license-plate-recognition-clean.

I have a requirements.txt file along with the app.py file, the other .ipynb files are where I created the initial code for checks and then copied over to the .py file.

Here’s the gist of my issue

I am trying to create a computer vision model that takes an uploaded file from the user (a picture in .jpg format).

This variable: uploaded_file is used in the subsequent code:

st.subheader('License plate detection model')

#Extract plate function
def extract_plate(img): # the function detects and perfors blurring on the number plate.
	plate_img = img.copy()
	
	#Loads the data required for detecting the license plates from cascade classifier.
	plate_cascade = cv2.CascadeClassifier('indian_license_plate.xml')

	# detects numberplates and returns the coordinates and dimensions of detected license plate's contours.
	plate_rect = plate_cascade.detectMultiScale(plate_img, scaleFactor = 1.3, minNeighbors = 7)

	for (x,y,w,h) in plate_rect:
		#a,b = (int(0.02*img.shape[0]), int(0.025*img.shape[1])) #parameter tuning #Check this later!
		#plate = plate_img[y+a:y+h-a, x+b:x+w-b, :]
		plate = plate_img[y:y+h, x:x+w, :] #don't need the parameter tuning stuff
		# finally representing the detected contours by drawing rectangles around the edges.
		cv2.rectangle(plate_img, (x,y), (x+w, y+h), (51,51,255), 3)
        
	return plate_img, plate # returning the processed image.

#Apply extraction function
dk_test_img = cv2.imread(uploaded_file) #set to work with uploaded file currently
plate_img_out, plate_out = extract_plate(dk_test_img)

#Match contours

def find_contours(dimensions, img) :

    # Find all contours in the image
    cntrs, _ = cv2.findContours(img.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # Retrieve potential dimensions
    lower_width = dimensions[0]
    upper_width = dimensions[1]
    lower_height = dimensions[2]
    upper_height = dimensions[3]
    
    # Check largest 5 or  15 contours for license plate or character respectively
    cntrs = sorted(cntrs, key=cv2.contourArea, reverse=True)[:15]
    
    ii = cv2.imread('contour.jpg')
    
    x_cntr_list = []
    target_contours = []
    img_res = []
    for cntr in cntrs :
        #detects contour in binary image and returns the coordinates of rectangle enclosing it
        intX, intY, intWidth, intHeight = cv2.boundingRect(cntr)
        
        #checking the dimensions of the contour to filter out the characters by contour's size
        if intWidth > lower_width and intWidth < upper_width and intHeight > lower_height and intHeight < upper_height :
            x_cntr_list.append(intX) #stores the x coordinate of the character's contour, to used later for indexing the contours

            char_copy = np.zeros((44,24))
            #extracting each character using the enclosing rectangle's coordinates.
            char = img[intY:intY+intHeight, intX:intX+intWidth]
            char = cv2.resize(char, (20, 40))
            
            cv2.rectangle(ii, (intX,intY), (intWidth+intX, intY+intHeight), (50,21,200), 2)
            plt.imshow(ii, cmap='gray')

#             Make result formatted for classification: invert colors
            char = cv2.subtract(255, char)

            # Resize the image to 24x44 with black border
            char_copy[2:42, 2:22] = char
            char_copy[0:2, :] = 0
            char_copy[:, 0:2] = 0
            char_copy[42:44, :] = 0
            char_copy[:, 22:24] = 0

            img_res.append(char_copy) #List that stores the character's binary image (unsorted)
            
    #Return characters on ascending order with respect to the x-coordinate (most-left character first)
            
    plt.show()
    #arbitrary function that stores sorted list of character indeces
    indices = sorted(range(len(x_cntr_list)), key=lambda k: x_cntr_list[k])
    img_res_copy = []
    for idx in indices:
        img_res_copy.append(img_res[idx])# stores character images according to their index
    img_res = np.array(img_res_copy)

    return img_res

#Find characters function

def segment_characters(image) :

    # Preprocess cropped license plate image
    img_lp = cv2.resize(image, (333, 75))
    img_gray_lp = cv2.cvtColor(img_lp, cv2.COLOR_BGR2GRAY)
    _, img_binary_lp = cv2.threshold(img_gray_lp, 200, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    img_binary_lp = cv2.erode(img_binary_lp, (3,3))
    img_binary_lp = cv2.dilate(img_binary_lp, (3,3))

    LP_WIDTH = img_binary_lp.shape[0]
    LP_HEIGHT = img_binary_lp.shape[1]

    # Make borders white
    img_binary_lp[0:3,:] = 255
    img_binary_lp[:,0:3] = 255
    img_binary_lp[72:75,:] = 255
    img_binary_lp[:,330:333] = 255

    # Estimations of character contours sizes of cropped license plates
    dimensions = [LP_WIDTH/6,
                       LP_WIDTH/2,
                       LP_HEIGHT/10,
                       2*LP_HEIGHT/3]
    plt.imshow(img_binary_lp, cmap='gray')
    plt.show()
    cv2.imwrite('contour.jpg',img_binary_lp)

    # Get contours within cropped license plate
    char_list = find_contours(dimensions, img_binary_lp)

    return char_list

char = segment_characters(plate_out)

However my logs are giving me the following issues

[ WARN:21@4861.850] global /io/opencv/modules/imgcodecs/src/loadsave.cpp (239) findDecoder imread_(''): can't open/read file: check file path/integrity
2022-11-26 19:33:26.382 Uncaught app exception
Traceback (most recent call last):
  File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 564, in _run_script
    exec(code, module.__dict__)
  File "/app/car-license-plate-recognition-clean/app.py", line 111, in <module>
    plate_img_out, plate_out = extract_plate(dk_test_img)
  File "/app/car-license-plate-recognition-clean/app.py", line 92, in extract_plate
    plate_img = img.copy()
AttributeError: 'NoneType' object has no attribute 'copy'

I also get the error:

[ WARN:29@8560.858] global /io/opencv/modules/imgcodecs/src/loadsave.cpp (239) findDecoder imread_(''): can't open/read file: check file path/integrity
2022-11-26 20:35:05.391 Uncaught app exception
Traceback (most recent call last):
  File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 564, in _run_script
    exec(code, module.__dict__)
  File "/app/car-license-plate-recognition-clean/app.py", line 112, in <module>
    plate_img_out, plate_out = extract_plate(dk_test_img)
  File "/app/car-license-plate-recognition-clean/app.py", line 93, in extract_plate
    plate_img = img.copy()
AttributeError: 'NoneType' object has no attribute 'copy'
2022-11-26 20:35:23.977 Uncaught app exception
Traceback (most recent call last):
  File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 564, in _run_script
    exec(code, module.__dict__)
  File "/app/car-license-plate-recognition-clean/app.py", line 111, in <module>
    dk_test_img = cv2.imread(uploaded_file) #set to work with uploaded file currently
TypeError: Can't convert object to 'str' for 'filename'

This is my first time deploying and I suspect that it has to do with the extract function where plate_img = img.copy(). Part of me also suspect that the .xml file in the CascadeClassifier is a culprit.

and idea what I should do or if I’m doing major mistakes?

Hey @IIPeteII,

Is your app hitting this same error when you run it locally?

The first error message is saying 'NoneType' object has no attribute 'copy', which would indicate that img in the line plate_img = img.copy() is not correctly defined here – since you’re passing dk_test_img to extract_plate(), it seems like the error is probably coming from this line:

dk_test_img = cv2.imread(uploaded_file) #set to work with uploaded file currently

The last error message confirms that idea – TypeError: Can't convert object to 'str' for 'filename'
It sounds like cv2.imread() is expecting uploaded_file to be a string (the name of a file), but it’s actually an object.

It doesn’t look like you included the section of code where you define uploaded_file – I’d recommend checking that section out.

Hey @Caroline !

No, I have made a successfully running .ipynb file and want to convert it into an app.py file now.

And okay, maybe it’s the uploaded_file as you say, the code for that is:

#upload a picture
uploaded_file = st.file_uploader("Upload a file", type="jpg")
if uploaded_file is not None:
    # To read file as bytes:
    st.image(uploaded_file, caption='Your uploaded picture')
    #bytes_data = uploaded_file.getvalue()
    #st.write(bytes_data)

    # To convert to a string based IO:
    #stringio = StringIO(uploaded_file.getvalue().decode("utf-8"))
    #st.write(stringio)

    # To read file as string:
    #string_data = stringio.read()
    #st.write(string_data)

    # Can be used wherever a "file-like" object is accepted:
    #dataframe = pd.read_csv(uploaded_file)
    #st.write(dataframe)

Would you know what I should write for it to get an object rather than a string?

Thank you so much for responding! :smiley:

For situations where you need a file path, rather than an in-memory file object, you can use NamedTemporyFiles, like in this example File uploading and reading using st.file_uploader - #2 by blackary

1 Like

Hi @blackary

Thank you so much - I will check that out! I guess in your example it was an audio file, but it should work fine with pictures as well right?

Yup!

Hey @blackary

I tried doing your solution - however I am uncertain of what functions to use for loading the image, it seems that there is a discrepancy between the code for .mp3 and .jpg.

Thus, I modified it because all I need right now is uploading the file and then write the functions + model later etc:

uploaded_file = st.file_uploader("Upload your picture (only .jpg)", type=["jpg"])

if uploaded_file is not None:
    with NamedTemporaryFile(suffix="jpg") as temp:
        temp.write(uploaded_file.getvalue())
        temp.seek(0)
        st.write(temp)#, caption = 'Your uploaded image') I tried doing st.image, but that returned many errors

What I get with this is the following output:

I am not getting any errors in the logs with this way but I have two questions/wonderings:

  1. Is this the correct code for a .jpg file? I guess there is a way to show the image that the user of the app has uploaded? I suspect I am doing something wrong and there definitely is a way.

  2. In the example you showed, the developer had already made a model and wrote that into their upload function - do you suggest I do the same? In my mind the app would work in the flow of: upload picture → functions preprocess the picture → model is trained and applied to picture → output (classification) is returned.

Thank you so much for your contribution - I am trying my best at getting this to work :smiley:

1 Like

Hi @IIPeteII :wave:

I think we may slightly be overcomplicating things :sweat_smile: Let me know if I’m mistaken: what you’re looking for is to:

  1. Upload a .jpg using st.file_uploader
  2. Display the uploaded image with st.image
  3. Process the uploaded image with OpenCV for ML etc

Here’s the code to do just that:

import cv2
import numpy as np
import streamlit as st

uploaded_file = st.file_uploader("Upload an image (JPG)", type="jpg")
if uploaded_file is not None:
    # Display image
    st.write("Original Image")
    st.image(uploaded_file, caption="Uploaded Image")

    # Convert image to cv2 format
    file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
    opencv_image = cv2.imdecode(file_bytes, 1)

    # opencv_image is now an array that can be processed with OpenCV
    st.write("`type(opencv_image)`", type(opencv_image))
    st.write("`opencv_image.shape`", opencv_image.shape)

    # Do more magic with ML
    # Apply extraction function etc ...

    # Then display processed image
    st.write("Processed Image")
    st.image(opencv_image, caption="OpenCV Image", channels="BGR")

image-upload-opencv

Hope this answers your original question.

You could also replace the snippet from my example:

# Convert image to cv2 format
file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
opencv_image = cv2.imdecode(file_bytes, 1)

with

# Convert image to cv2 format
bytes_data = uploaded_file.getvalue()
opencv_image = cv2.imdecode(np.frombuffer(bytes_data, np.uint8), cv2.IMREAD_COLOR)

There are multiple ways to achieve the same objective. Next time you’re stuck, I would encourage you to first read through the NumPy and OpenCV docs, and also to perhaps Google or stackoverflow for converting between various formats for various libraries. You will likely learn more in the process :smile:

Happy Streamlit-ing! :balloon:
Snehan

PS: perhaps it makes sense to improve our st.image docs with more use-case examples like we do in the st.camera_input docs.

Hey @snehankekre

Thank you so much for the valuable feedback! I am still so new to everything that I am learning new things by the hour - much appreciated :smiley:

I applied your suggestion and even the latest suggestion with using bytes_data and it seems to work for the image.

Now that the image uploading is in place, then I went on to the next part of the workflow which is to extract the number plate from the picture of a car:

#create extraction function

def extract_plate(img): # the function detects and perfors blurring on the number plate.
	plate_img = img.copy()
	
	#Loads the data required for detecting the license plates from cascade classifier.
	plate_cascade = cv2.CascadeClassifier('indian_license_plate.xml')

	# detects numberplates and returns the coordinates and dimensions of detected license plate's contours.
	plate_rect = plate_cascade.detectMultiScale(plate_img, scaleFactor = 1.3, minNeighbors = 7)

	for (x,y,w,h) in plate_rect:
		plate = plate_img[y:y+h, x:x+w, :]
		# finally representing the detected contours by drawing rectangles around the edges.
		cv2.rectangle(plate_img, (x,y), (x+w, y+h), (51,51,255), 3)
        
	return plate_img, plate # returning the processed image.

After the function is defined, I get no error… but as soon as I apply it using:

dk_test_img = cv2.imread(uploaded_file) #read file
plate_img_out, plate_out = extract_plate(dk_test_img) #apply

I get the following feedback from logs:

[ WARN:4@954.605] global /io/opencv/modules/imgcodecs/src/loadsave.cpp (239) findDecoder imread_(''): can't open/read file: check file path/integrity

2022-12-02 19:07:27.408 Uncaught app exception

Traceback (most recent call last):

  File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 564, in _run_script

    exec(code, module.__dict__)

  File "/app/car-license-plate-recognition-clean/app.py", line 94, in <module>

    plate_img_out, plate_out = extract_plate(dk_test_img) #apply

  File "/app/car-license-plate-recognition-clean/app.py", line 76, in extract_plate

    plate_img = img.copy()

AttributeError: 'NoneType' object has no attribute 'copy'

[ WARN:5@959.119] global /io/opencv/modules/imgcodecs/src/loadsave.cpp (239) findDecoder imread_(''): can't open/read file: check file path/integrity

2022-12-02 19:07:31.922 Uncaught app exception

Traceback (most recent call last):

  File "/home/appuser/venv/lib/python3.10/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 564, in _run_script

    exec(code, module.__dict__)

  File "/app/car-license-plate-recognition-clean/app.py", line 94, in <module>

    plate_img_out, plate_out = extract_plate(dk_test_img) #apply

  File "/app/car-license-plate-recognition-clean/app.py", line 76, in extract_plate

    plate_img = img.copy()

AttributeError: 'NoneType' object has no attribute 'copy'

I am quite unsure on what this could be, but from google, stack overflow and opencv docs it seems like it’s because the uploaded file is not recognized - even changing the variable from uploaded_file to opencv_image returns (NameError: name ‘opencv_image’ is not defined).

unsure what this could be to be honest :frowning:

Do you have an idea?

Hey @snehankekre

Did you see my message above? I have not been able to find a solution… :confused:

@IIPeteII It appears that cv2.imread must be returning None, perhaps because it’s expecting a file path and you’re passing something that’s not a valid path.

Could you expand the code you’ve shared to show exactly how you’re getting uploaded_file?

Apologies for the delay - I don’t quite understand why you’re still doing:

dk_test_img = cv2.imread(uploaded_file) #read file

In my example above, I shared a workaround so you don’t have to use cv2.imread:

# Convert image to cv2 format
bytes_data = uploaded_file.getvalue()
opencv_image = cv2.imdecode(np.frombuffer(bytes_data, np.uint8), cv2.IMREAD_COLOR)

You can adapt it to your code:

bytes_data = uploaded_file.getvalue()
dk_test_img = cv2.imdecode(np.frombuffer(bytes_data, np.uint8), cv2.IMREAD_COLOR) #read file
plate_img_out, plate_out = extract_plate(dk_test_img) #apply

The objective is to convert the uploaded image to an OpenCV compatible format (maybe an array). How you do that (cv2.imread or cv2.imdecode(…)) shouldn’t factor in. If one of them (cv2.imread) is throwing an error, use the other?

Thanks @snehankekre , this was the solution - I realized that I should use the cv2.imdecode in both uploading the picture, and when applying the extraction function:

The code is as follows for anyone interested:

#upload a picture
uploaded_file = st.file_uploader(“Upload your picture (only .jpg)”, type=[“jpg”])
if uploaded_file is not None:
# Display image
st.write(“Original Image”)
st.image(uploaded_file, caption=“Uploaded Image”)

# Convert image to cv2 format
bytes_data = uploaded_file.getvalue()
opencv_image = cv2.imdecode(np.frombuffer(bytes_data, np.uint8), cv2.IMREAD_COLOR)

# opencv_image is now an array that can be processed with OpenCV
st.write("`type(opencv_image)`", type(opencv_image))
st.write("`opencv_image.shape`", opencv_image.shape)

#------------ License plate detection model
st.subheader(‘License plate detection model’)

#create extraction function

def extract_plate(img): # the function detects and perfors blurring on the number plate.
plate_img = img.copy()

#Loads the data required for detecting the license plates from cascade classifier.
plate_cascade = cv2.CascadeClassifier('indian_license_plate.xml')

# detects numberplates and returns the coordinates and dimensions of detected license plate's contours.
plate_rect = plate_cascade.detectMultiScale(plate_img, scaleFactor = 1.3, minNeighbors = 7)

for (x,y,w,h) in plate_rect:
	plate = plate_img[y:y+h, x:x+w, :]
	# finally representing the detected contours by drawing rectangles around the edges.
	cv2.rectangle(plate_img, (x,y), (x+w, y+h), (51,51,255), 3)
    
return plate_img, plate # returning the processed image.

#Apply extraction function

#dk_test_img = cv2.imread(opencv_image) #read file
test_bytes_data = uploaded_file.getvalue()
dk_test_img = cv2.imdecode(np.frombuffer(test_bytes_data, np.uint8), cv2.IMREAD_COLOR) #read file
plate_img_out, plate_out = extract_plate(dk_test_img) #apply

1 Like