Transforming 3 channel image to greyscale 1 channel

Hi. I’m facing a problem transforming RGB image into greyscale image.

I want to make the channel of image from 3 to 1, and the image came out as 1 channel but green-ish color, not greyscale.

Lines of codes commented out in class MyNormalize is what I have tried.

Could anybody suggest a way to transform this RGB image into greyscale&1 channel image?

By the way, in test(test_loader) method, variable image includes 16 images, but does it process each of the 16 images?
I don’t quite get how this process is working :frowning:

I appreciate your cooperation. Thank you!

imgDataset = MyDataset(csv_file_path, ROOT_DIR, transform=transforms.Compose([
    transforms.Resize(50),
    transforms.ToTensor(),
    MyNormalize()
    ]))

class MyNormalize:
    def __call__(self, image):
        shape = image.shape

        '''
        trans1 = transforms.ToPILImage()
        trans2 = transforms.ToTensor()
        image = trans1(image)
        image = image.convert('L')
        image = trans2(image)
        '''
        
        #image = tv.transforms.functional.to_grayscale(trans1(image), num_output_channels=1)

        #image = 0.299 * image[:, :, 0] + 0.587 * image[:, :, 1] + 0.114 * image[:, :, 2]
        #image = (image - np.mean(image.numpy()))/np.std(image.numpy())*16+64
        
        return image

def test(test_loader):
    correct = 0
    test_loss = 0
    model.eval()
    with torch.no_grad():
        for (image, label) in test_loader:
            image, label = Variable(image.float()), Variable(label)
            print("image: ", type(image))
            #image:  <class 'torch.Tensor'>
            print("image: ", image.shape)
            #image:  torch.Size([16, 1, 50, 50])
            print("image[0]: ", type(image[0]))
            #image:  <class 'torch.Tensor'>
            print("image[0]: ", image[0].shape)
            #image:  torch.Size([1, 50, 50])
            print("image[0][0]: ", type(image[0][0]))
            #image:  <class 'torch.Tensor'>
            print("image[0][0]: ", image[0][0].shape)
            #image:  torch.Size([50, 50])

            output = model(image)
            test_loss += criterion(output, label).data
            pred = output.data.max(1, keepdim=True)[1]
            correct += pred.eq(label.data.view_as(pred)).long().cpu().sum()

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

How are you visualizing the image?
A single channel image shouldn’t have any color, so your current library used to plot the image might just create the green color.

Thank you for the reply.

I’m using pillow to visualize the image.
Do you mean my images as tensor are already in greyscale but pillow visualized image with green?

Trained model seems not to be classifying the images correctly, so I’m guessing if there is any relationship with color.

Could you post the code you are using to visualize the image?

A single channel image should not have any color, but the library might use some colormap to plot it.
The same applies e.g. for matplotlib and a float image:

import matplotlib.pyplot as plt
import numpy as np

img = np.random.randn(100, 100)
plt.imshow(img)  # Colored output

# plt.imshow(img, cmap='gray')  # Gray output
1 Like

Sorry for the late reply :frowning:

Below is my code.

I tried using .numpy() but imgDataset[0][0] was not the right shape for image data.

import matplotlib.pyplot as plt
from PIL import Image

print(imgDataset[0][0].shape)
#torch.Size([1, 50, 50])
print(imgDataset[0][0].size)
#<built-in method size of Tensor object at 0x1c2cc82360>
print(imgDataset[0][0].dtype)
#torch.float32

trans = transforms.ToPILImage()
plt.imshow(trans(imgDataset[0][0]))
plt.show()
1 Like

If you are using PIL, you can use

trans.convert('L')

which will convert the RGB image into greyscale (more here). You can now plot the image using

plt.imshow(trans)

If that gives non-greyscaled colors, you can force the colormap to be black and white using matplotlib’s binary colormap by writing:

plt.imshow(trans, cmap=plt.cm.binary)

Here, I’m assuming type(trans) is <class 'PIL.Image.Image'>.

Thank you very much!
Using binary colormap, the image came out in greyscale.

However, the problem is that the image does not seem to be processed as greyscale in train function.

How can I convert the image properly in greyscale when I load data from dataset??
Transformation code is shown in the first post.

Using the CIFAR10 dataset, for example, you can use the Grayscale transform.

import torchvision.transforms as transforms
import torchvision.datasets as datasets

trainset = datasets.CIFAR10(root='./data', train=True, download=True,
    transform=transforms.Grayscale(num_output_channels=1))

To confirm, you could call trainset[0][0].size, which gives back (32, 32) (with 1 channel). Also, you could again plot the data using:

plt.imshow(trainset[0][0], cmap=plt.cm.binary)

Thank you @mattdeitke

transforms.Grayscale ran without errors, but it gave [1, 50, 50] shape, and showed green image without cmap=plt.cm.binary option.

Could it be a problem with the original dataset?

class MyDataset(Dataset):

    def __init__(self, csv_file_path, root_dir, transform=None):
        self.image_dataframe = pd.read_csv(csv_file_path)
        self.root_dir = root_dir
        self.transform = transform

    def __len__(self):
        return len(self.image_dataframe)

    def __getitem__(self, idx):
        label = self.image_dataframe.iat[idx, LABEL_IDX]
        img_name = os.path.join(self.root_dir, 'data', 'load_data', self.image_dataframe.iat[idx, IMG_IDX]) 
        image = Image.open(img_name)
        if self.transform:
            image = self.transform(image)

        return image, label

imgDataset = MyDataset(csv_file_path, ROOT_DIR, transform=transforms.Compose([
    transforms.Grayscale(num_output_channels=1),
    transforms.Resize(50),
    transforms.ToTensor(),
    MyNormalize()
    ]))

print(imgDataset[0][0].size)
#<built-in method size of Tensor object at 0x1c2fce0708>
print(imgDataset[0][0].shape)
#torch.Size([1, 50, 50])

Thank you so very much for all your cooperation!!
Problem solved!!

Shape of the tensor which came out as [1, 50, 50] successfully became [50, 50] by first loading the image with OpenCV and transforming into PIL to modify.

As @mattdeitke and @ptrblck said, green-ish visualization of the image was a matter of option. Using plt.gray() solved the problem.


class MyDataset(Dataset):
    def __getitem__(self, idx):
        label = self.image_dataframe.iat[idx, LABEL_IDX]
        img_name = os.path.join(self.root_dir, 'data', 'load_data', self.image_dataframe.iat[idx, IMG_IDX]) 
        image = cv2.imread(img_name)
        if self.transform:
            im = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            im2 = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
            image = Image.fromarray(im2)
            image = self.transform(image)


trans = transforms.ToPILImage()
plt.imshow(trans(imgDataset[0][0]))
plt.gray()
plt.show()