Invalid number of channels in input image error using OpenCV

|▌冷眼眸甩不掉的悲伤 提交于 2021-02-17 06:58:04

问题


cv2.error: OpenCV(4.2.0) c:\projects\opencv
python\opencv\modules\imgproc\src\color.simd_helpers.hpp:92: error:
(-2:Unspecified error) in function '__cdecl cv::impl::`anonymous- 

namespace'::CvtHelper<struct cv::impl::`anonymous
namespace'::Set<3,4,-1>,struct cv::impl::A0xe227985e::Set<1,-1,-1>,struct cv::impl::A0xe227985e::Set<0,2,5>,2>::CvtHelper(const class cv::_InputArray &,const class cv::_OutputArray &,int) > Invalid number of channels in input image: > 'VScn::contains(scn)' > where > 'scn' is 1

img = cv2.cvtColor(images, cv2.COLOR_BGR2GRAY) 

this line gives me error

hello everyone i am new to use opencv, now i am doing project on image classification

my complete code as follows

from flask import Flask, request
from flask_restful import Api, Resource
import sys, os
from myconstants1 import path_logs, path_resources
from logConfig1 import setup_logger
import pathlib, pycountry, cv2, pickle, random, PIL, sys
from pathlib import Path 
import pathlib as pl
import numpy as np
from sklearn.model_selection import train_test_split
import pandas as pd
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam
from keras.utils.np_utils import to_categorical
from keras.layers import Flatten, Dropout
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.preprocessing.image import ImageDataGenerator
from PIL import Image, ImageOps
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

logger = setup_logger('/modelTrain', path_logs+'/modelTrain.log')

app = Flask(__name__)
api = Api(app)

path = sys.path
path = Path(__file__).parent

print("path2", path)

class HandleRequest5(Resource):
    y_validation = ""
    x_test = ""
    x_validation  = ""
    x_train = ""
         
    @classmethod
    def post(cls, json): 
               
        data = request.get_json()
        
        json = ({
            "Status": "failed",
            "message": "ALL fields are mandatory"
                })
                
        try:
            country_code = data["country_code"].upper()  
            batch_size = data["batch_size"]
            step_per_epoch_val = data["step_per_epoch_val"]
            epoch = data["epoch"]
        except KeyError:
            print(json)
            logger.debug(json)
            return(json)

        
        try:
            country = pycountry.countries.get(alpha_3 = data["country_code"].upper()).name.lower()
            print("country1", country)
            logger.debug(f'country1 : {country}')
            country = country.split()
            country =("_".join(country))
            print("country : ", country)
            logger.debug(f"country : {country}")
            alpha_2 = pycountry.countries.get(alpha_3 = data["country_code"].upper()).alpha_2
            print("alpha_2 : ", alpha_2)
            logger.debug(f"alpha_2 : {alpha_2}")
        except AttributeError:                
            jsonify1 = {
                        "status": "invalid",
                        "message" : "Invalid country_code"
                        }
            print("invalid country_code")
            
            logger.debug({
                        "status": "invalid",
                        "message" : "Invalid country_code"
                        })
            return jsonify1
    
    #   path = rf'{path}/{country}'  # folder with all class folders
        
        labelFile = rf'{path_resources}/{country}/labels.csv'         
        imageDimensions = (99, 200, 3)
        print("imageDimensions:", imageDimensions)
        testRatio = 0.2               # if 1000 images split will 200 for testing
        validationRatio = 0.2  
        print("line 91 is going to execute")
        cls.importImages( cls,testRatio , validationRatio , imageDimensions  ,country , labelFile)   
        
    def importImages(cls, testRatio, validationRatio, imageDimensions, country, labelFile):
        count = 0
        images = []
        classNo = []
        

        p = pl.Path(f'{path_resources}/{country}')                    
        mylist = [x for x in p.iterdir() if x.is_dir()]
        print("mylist1", mylist)
        print("total classs detected :", len(mylist))
        noofClasses = len(mylist)
        print("noofClasses:", noofClasses)
        print("importing classes...")
        
        for x in range(0, len(mylist)):

            myPicList = os.listdir(os.path.join(str(path_resources), str(country)+'//'+str(count))) 
            print("myPicList1:", myPicList)

            #for y in myPicList:
                #print(os.path.join(path, str(count), y))
                #curImg = cv2.imread((str(path_resources)+"/"+str(count)+"//"+y))
                
            for y in myPicList:
                print(os.path.join(path_resources, country, str(count)+y))

                curImg = cv2.imread(f"{path_resources}{country}/{str(count)}//{y}")                                                      
                images.append(curImg)
                classNo.append(count)
            print(count, end = " ")
            count+=1
        print(" ")
        images = np.array(images, dtype=np.uint8) 
        images = np.array(images)
        print("line 128")
        print(images.shape)
        #images = np.append(images,4)
        #images = images.append((Image.fromarray(images, dtype=np.float32).convert('RGB') / 255.))
#        image = Image.fromarray(images)
        #images = images.convert("RGB")
        classNo = np.array(classNo)        
        
        cls.splitData(cls,images, classNo, testRatio, validationRatio , imageDimensions, labelFile, noofClasses)
        return images, classNo, noofClasses
        
        # split data #
    def splitData(cls, images, classNo, testRatio, validationRatio , imageDimensions, labelFile, noofClasses):

        x_train, x_test, y_train, y_test = train_test_split(images, classNo, test_size = testRatio)
        x_train, x_validation, y_train, y_validation = train_test_split(x_train, y_train , test_size = validationRatio)
        
        # to check if no of images matches to number of labels for each data set
        print("data shapes...")
        print("train : ", end = "");print(x_train.shape, y_train.shape)
        print("validation :", end = ""); print(x_validation.shape, y_validation.shape) 
        print("test :", end = ""); print(x_test.shape, y_test.shape)        
        
        assert (x_train.shape[0] == y_train.shape[0]),  "the no of images is not equal to the no of labels in training set"
        assert (x_validation.shape[0] == y_validation.shape[0]), "the no of images is not equal to the no of labels in validation set"
        assert (x_test.shape[0] == y_test.shape[0]), "the no of images is not equal to the no of labels in test set"
        #print(x_train.shape)
        assert (x_train.shape[1:]  == (imageDimensions)),  "the dimension of training images are wrong"
        assert (x_validation.shape[1:] == (imageDimensions)), "the dimension of validation images are wrong"
        assert (x_test.shape[1:] == (imageDimensions)), "the dimension of test images are wrong"        
        
        data = pd.read_csv(labelFile) 
        
        cls.grayscale(cls, images, x_train, x_validation, x_test, y_train, y_validation, y_test )
        
        return images, x_train, x_validation, x_test, y_train, y_validation, y_test
        # preprocessing the image #

    def grayscale(cls,images, x_train, x_validation, x_test, y_train, y_validation, y_test):
        #images = ImageOps.grayscale(images)
        images = cv2.cvtColor(images, cv2.COLOR_BGR2GRAY)
        cls.equalize(images)
        return images
        
    def equalize(images):
        img = cv2.equalizeHist(images)       
        cls.preprocessing(img, grayscale, equalize)
        return img
        
    def preprocessing(cls, img, grayscale, equalize, x_train, x_validation, x_test, y_train, y_test, y_validation):
        img = grayscale(img)  #convert to grayscale
        img = equalize(img)   #standardize the lightining of an image
        img = img/255         # to normaize value between 0 and 1 instead of 0 to 255
        return img , x_train, x_validation, x_test, y_train, y_test, y_validation

        x_train = np.array(list(map(preprocessing, x_train)))  # to iterate and preprocess all images
        x_validation = np.array(list(map(preprocessing, x_validation)))
        x_test = np.array(list(map(preprocessing, x_test)))

        #cv2.imshow("grayscale images", x_train[random.randint(0, len(x_train)-1)]) #to check if training is done properly
               
        # add a depth of 1 #

        x_train = x_train.reshape(x_train.shape[0], x_train.shape[1], x_train.shape[2], 1)
        x_validation = x_validation.reshape(x_validation .shape[0], x_validation .shape[1], x_validation .shape[2], 1)
        x_test = x_test.reshape(x_test.shape[0], x_test.shape[1], x_test.shape[2], 1)


    def dataAugmentation(cls, x_train, y_train, noofClasses):
    # augmentation of images to make it more generic #

        dataGen = ImageDataGenerator(width_shift_range = 0.1, 
                             height_shift_range  = 0.1,
                             zoom_range = 0.2,
                             shear_range = 0.1,
                             rotation_range = 10)

        dataGen.fit(x_train)
        batches = dataGen.flow(x_train, y_train, batch_size = 20)
        x_batch, y_batch = next(batches)


# to show augmentated image sample 
#fig, axs = plt.subplots(24, 2, figsize = (20, 5))
#fig.tight_layout()
#print(axs)
#print("axs0:",axs[0])
#print("axs1:",axs[1])
#for i in range(10):
    #axs[i].imshow(x_batch[i].reshape(imageDimensions[0], imageDimensions[1]))
    #axs[0][1].imshow(x_batch[i].reshape(imageDimensions[0], imageDimensions[1]))
    #axs[i].axis("off")
    #axs[0][1].axis('off')
#plt.show()

        y_train = to_categorical(y_train, noofClasses) 
        y_validation = to_categorical(y_validation, noofClasses) 
        y_test = to_categorical(y_test, noofClasses) 
        cls.splitData(y_validations)        
        cls.myModel(noofClasses)
# convolution neural network #

    def myModel(cls, noofClasses, country):
        no_of_filters = 60 
        size_of_filter = (5,5)  #this is kernal that move around the image to get the features 
    
        size_of_filter2 = (3,3) 
        size_of_pool = (2,2)
        no_of_nodes = 200
    
        model = Sequential()
        model.add(Conv2D(no_of_filters, size_of_filter, input_shape = (imageDimensions[0], imageDimensions[1], 1), activation = "relu"))
        model.add(Conv2D(no_of_filters, size_of_filter, activation = "relu"))
        model.add(MaxPooling2D(pool_size = size_of_pool))
    
        model.add(Conv2D(no_of_filters//2, size_of_filter2, activation = "relu"))
        model.add(Conv2D(no_of_filters//2, size_of_filter2, activation = "relu"))
        model.add(Dropout(0.5))
    
        model.add(Flatten())
        model.add(Dense(no_of_nodes, activation = "relu"))
        model.add(Dropout(0.5))
    
    
  #  model.add(Flatten())
        model.add(Dense(noofClasses, activation = "softmax"))
    
    # compile model #
        model.compile(Adam(lr = 0.001), loss = "categorical_crossentropy",  metrics = ["accuracy"])
        return model    
    
    # train #
        model = myModel()

        print(model.summary())
        history = model.fit_generator  (dataGen.flow(x_train, y_train, batch_size = batch_size_val), steps_per_epoch = steps_per_epoch_val, epochs = epoch_val, validation_data = (x_train, y_train))  
        
# plot #

        plt.figure(1)
        plt.plot(history.history["loss"])
        plt.plot(history.history["val_loss"])
        plt.legend(["training", "validation"])

        plt.title("loss")
        plt.xlabel("epoch")
        plt.figure(2)
        plt.plot(history.history["accuracy"])
        plt.plot(history.history["val_accuracy"])

        plt.legend(["training", "accuracy"])
        plt.title("accuracy")
        plt.xlabel("epoch")
        #plt.show()


        score = model.evaluate(x_test, y_test, verbose = 0)
        print("test score: ", score[0])
        print("test accuracy: ", score[1])

    ###############################################################
    #store the model as pickle object #
    #save_path = rf'{path}/{country}'
        pickle_out = open(rf"{path_resources}/{country}.p", "wb")
    #model = model.save(rf'{country}_{epoch_val}.h5')

        pickle.dump(model, pickle_out)
        pickle_out.close()
        print(rf"{country}_model saved...")
        cv2.waitKey(0)        

        
api.add_resource(HandleRequest5, '/modelTrain')
if __name__ == ' __main__ ':
    app.run(debug = False)

回答1:


As suggested in the comments, you have two ways to do this. You can either iterate through each image and run the cv2.cvtColor method or you can use the formula to convert from RGB to grayscale directly. OpenCV uses the SMPTE Rec. 601 conversion formula, which is:

Y = 0.299*R + 0.587*G + 0.114*B

Let's cover both methods.

Method #1

Create a new 3D array that is 336 x 99 x 200 then iterate through each image in your 4D array, convert then set it to the corresponding location in the output.

images_gray = np.zeros(images.shape[:-1], dtype=images.dtype)
for i, img in enumerate(images):
    images_gray[i] = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

Method #2 - Use the conversion formula directly

I would argue that this method is the most efficient, mainly because what I will suggest that you do is compute this vectorized:

coeffs = np.array([0.114, 0.587, 0.229])
images_gray = (images.astype(np.float) * coeffs).sum(axis=-1)
images_gray = images_gray.astype(images.dtype)

Two things to note: The first is that the weights for each of the RGB values are reversed due to OpenCV reading images in BGR format. The second is that I have temporarily cast the images to floating-point precision so that you maintain the most accuracy possible due to the floating-point coefficients. We then convert the resulting output back to the same precision as your input images. Finally, what the above code will do is for every pixel for every image, we will multiply each colour pixel by the weights seen in the conversion formula, then sum over the values. The above code will do it in a vectorized way with no loops.

Minor note regarding image classification

I noticed in the comments thread above that you are using this to perform image classification. If you plan on using a deep learning framework, you usually need to maintain a singleton dimension to reflect the channel dimension so that you can properly do broadcasting in the forward pass of the network. In other words, you must have a 336 x 99 x 200 x 1 array. For method #1, simply declare your output array to have the four dimensions, but in the loop you will need to add a singleton dimension to the end of your array by using np.newaxis.

images_gray = np.zeros(images.shape, dtype=images.dtype)
for i, img in enumerate(images):
    images_gray[i] = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)[..., np.newaxis]

For method #2, you can add keepdims=True in the sum call:

coeffs = np.array([0.114, 0.587, 0.229])
images_gray = (images.astype(np.float) * coeffs).sum(axis=-1, keepdims=True)
images_gray = images_gray.astype(images.dtype)


来源:https://stackoverflow.com/questions/65388627/invalid-number-of-channels-in-input-image-error-using-opencv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!