Convert Color image to grayscale image using Octave - image

I have a color image that I'm trying to convert to grayscale, but I get an error:
warning: the 'rgb2gray' function belongs to the image package from Octave Forge but has not yet been implemented
I'm using Octave 4.2.2 on Ubuntu 18.04 64-bit and can't upgrade this version to Octave 5.1 yet.
Is there a workaround?
My goal is:
To convert a color image into grayscale.
Then place the intensity/brightness of each grayscale pixel into a range between 0-1.
My code:
pkg load image
% read image from url (I took a random image on internet)..
[url_img, map] = imread('http://i.imgur.com/9PDZb7i.png');
figure, imshow(url_img), title('Image from url')
% resize it..
resized_img1 = imresize(url_img, 0.2); % resize by a factor here 0.2
resized_img2 = imresize(url_img, [600 500]); % resize to a specific dimensions
% there are many ways of interpolation to perform resizing
%resized_img3 = imresize(url_img, 0.2,'method','nearest'); % rsize by a specific interpolation method
figure, imshow(resized_img1), title('Resized image')
% change color did you mean from RGB to grayscale
gray_img = rgb2gray(resized_img1);
figure, imshow(gray_img), title ('Grayscale image')

Reinstall the image package. You somehow have a botched installation.
The function rgb2gray has always been part of the image package. It is one of the functions that has been there since the very start.
What has happened is that since version 4.4, Octave core also includes an implementation of rgb2gray. To support both old and new Octave versions, the image package checks if rgb2gray is available during installation. If so, it installs its own implementation. If not, it does nothing and defaults to the implementation in Octave core. If you have both image package and Octave 4.2 installed, and rgb2gray is not available, then you somehow messed up your installation of the image package.
Is your installation of the image package maybe done with a version of Octave different from the one you are running?
Also, consider using the octave packages that are provided by your system package manager which should not have this problem (apt install octave-image) after uninstalling the ones you installed manually.

According to Octave documentation on rgb2gray, the conversion is done as follows:
I = 0.298936*R + 0.587043*G + 0.114021*B
So converting a 3D RGB image matrix to a 2D gray-scale can be done by this code:
gray_img = (...
0.298936 * resized_img1(:,:,1) +...
0.587043 * resized_img1(:,:,2) +...
0.114021 * resized_img1(:,:,3));
When you call imread than the pixels are integers of type uint8, in this case you can round the result for better accuracy by adding 0.5:
gray_img = (...
0.298936 * resized_img1(:,:,1) +...
0.587043 * resized_img1(:,:,2) +...
0.114021 * resized_img1(:,:,3) + 0.5);
To get the pixels into a range between 0-1 use im2double

If RGB is an RGB image (a matrix of size [n,m,3]) then converting to a gray-scale image gray (an array of [n,m]) is accomplished by a weighted averaging of the 3 color channels.
Depending on your application, the best approach could be instead to take only the green channel (this is the most sensitive one, CCDs have twice as many green pixels than blue or red pixels):
gray = rgb(:,:,2);
A simple non-weighted average is often good enough:
gray = mean(rgb,3);
The Adobe D65 standard RGB uses weights of 0.2973769, 0.6273491 and 0.0752741 for red, green and blue (source). But I don’t know what weights are used by the MATLAB implementation of rgb2gray. Let’s assume it’s those weights. This code computes the weighted average:
[n,m] = size(rgb);
gray = reshape(rgb,[],3);
gray = gray * [0.30;0.63;0.07];
gray = reshape(gray,n,m);
In Octave you can write it as a one-liner:
gray = reshape(reshape(rgb,[],3) * [0.30;0.63;0.07], size(rgb)[1:2]);

If you want to write function create new function convgray.m then just paste
function[G] = convgray(F)
rgb=double(F);
[height,width,c] = size(rgb);
gray = reshape(rgb,[],3);
gray = gray * [0.30;0.63;0.07];
gray = reshape(gray,height,width);
gray=uint8(gray);
imshow(gray)
then in command window type
>> image = imread('yourimage.jpg');
>> convgray(image)
it will show your grayscale image with no error
here's my output

Related

Reshaping greyscale images for neural network training - how to do this correctly

I have a general question about convolutional neural networks and image processing for training if your images are grey scale.
Take this image for example:
Its a grey scale image but when I do
image = cv2.imread("image.jpg")
print(image.shape)
I get
(1024, 1024, 3)
I know that opencv automatically creates 3 channels for jpg images. But when it comes to network training, it would be much more computationally efficient if I could use images in (1024, 1024, 1) - just like many of the MNIST tutorials demonstrate. However, if I reshape this:
image.reshape(1024, 1024 , 1)
And then try for example to show the image
plt.axis("off")
plt.imshow(reshaped_image)
plt.show()
I get
raise TypeError("Invalid dimensions for image data")
Does that mean that reshaping my images this way before network training is incorrect? I want to keep as much information in the image as possible but I don't want to have those extra channels if they aren't needed.
The reason that you're getting the error is that the output of your reshape does not have the same number of elements as the input. From the documentation for reshape:
No extra elements are included into the new matrix and no elements are excluded. Consequently, the product rows*cols*channels() must stay the same after the transformation.
Instead, use cvtColor to convert your 3-channel BGR image to a 1-channel grayscale image:
In Python:
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Or in C++:
cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
You could also avoid conversion altogether by reading the image using the IMREAD_GRAYSCALE flag:
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
or
image = cv2.imread(image_path, 0)
(Thanks to #Alexander Reynolds for the Python code.)
This worked for me.
for image_path in dir:
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
X.append(img)
X = np.array(X)
X = np.expand_dims(X, axis=3)
set axis = Int : based on your array, 1 means it will prepend a new dimension in front.

how can I get good binary image using Otsu method for this image?

here is my image
a.png
for binarization I try this code.
im=rgb2gray(I);
maxp=uint16(max(max(im)));
minp=uint16(min(min(im)));
bw=im2bw(im,(double(minp+maxp))/(1.42*255));
bw=~bw;
imm=bw;
but I need binarization by otsu.how can I get good binary output using otsu method?
plz help
thanks
MATLAB has its own implementation of Otsu thresholding called multithresh. In your case the code to obtain the segmented image should be something like this:
im=rgb2gray(I); % convert image to grayscale
thresh = multithresh(im); % find one threshold (using Otsu method)
segmented_im = imquantize(im, thresh); % segment image
imagesc(segmented_im); % show segmented image
I haven't tested it so I don't know how well it would perform on your image.
EDIT:
I tested it, and it doesn't work as expected. One of the problems is that Otsu's method works well when there is a clear bimodal distribution of the pixel intensities. This bimodality is lacking in your image. A call to imhist(im) after the grayscale conversion leads to this (comments added by me):
As you can see, the distribution is almost trimodal, and the threshold selected by multithresh is the first one, while you want the second one. The first workaround that comes to my mind (especially if all the images in your dataset are similar to the one you posted, i.e. have a similar intensity distribution) is to make multithresh output two thresholds, and then selecting the last (highest) one:
thresholds = multithresh(im, 2);
thresh = thresholds(end);
Then proceed with the segmentation of the image as stated above. This second method leads to this segmentation:
EDIT 2 (putting it all together):
Indeed the output segmented_im is not a binary image, but a label image. It's easy enough to convert it to a binary image. I will include directly all the code in this next snippet:
im=rgb2gray(I); % convert image to grayscale
thresholds = multithresh(im, 2); % find two thresholds using Otsu
thresh = thresholds(end); % select larger one
segmented_im = imquantize(im, thresh); % segment image
segmented_im(segmented_im == 1) = 0; % make background black (0)
segmented_im(segmented_im == 2) = 255; % make foreground white (255)
binary_im = im2bw(segmented_im); % make binary (logical) image
imshow(binary_im); % show binary image
binary_im il a logical matrix with false (0) for background, and true (1) for foreground. segmented_im is a double matrix with 0 for background and 255 for foreground. I hope this serves your purposes!

Resizing ROI in an image

I have a 565 * 584 image as shown
I want to reduce the radius of the circle by certain number of pixels without changing the size of the image. How can I do it? Please explain or give some ideas. Thank You.
I would use ImageMagick and an erosion like this:
convert http://i.stack.imgur.com/c8lfe.jpg -morphology erode octagon:8 out.png
If you know that the background of the image is a constant, as in your example, this is easy.
Resize the entire image by the ratio you wish to shrink by. Then create a new image at the size of the original and fill it with the background color, then paste the resized image into the center of it.
Here's how you'd do it in OpenCV Python. Going with Mark Setchell's approach, simply specify a round structuring element so that you can maintain or respect the round edges of the object. The closest thing that OpenCV has to offer is the elliptical mask.
As such:
import numpy as np # Import relevant packages - numpy and OpenCV
import cv2
# Read in image and threshold - convert to grayscale first
im = cv2.imread('c8lfe.jpg', 0) > 128
# Specify radius of ellipse
radius = 21
# Obtain structuring element, then erode image
se = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (radius, radius))
# Make sure you convert back to grayscale and multiply by 255
out = 255*(cv2.erode(im, se).astype('uint8'))
# Show the image, wait for user key, then close window and write image
cv2.imshow('Reduced shape', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('out.png', out)
We get:
Be advised that the small bump at the top right corner of your shape will mutate. As we are essentially shrinking the perimeter of the object, that bump will also shrink as well. If you wish to preserve the structure of the object while maintaining the image resolution, use Mark Ransom's approach or my slightly modified version of his approach. Both are shown below.
However, to be self-contained, we can certainly do what Mark Ransom has suggested. Resize the image, initialize a blank image that is size of the original image, and place it in the centre:
import numpy as np # Import relevant packages - OpenCV and Python
import cv2
im = cv2.imread('c8lfe.jpg', 0) # Read in the image - grayscale
scale_factor = 0.75 # Set scale factor - We are shrinking the image by 25%
# Get the desired size (row and columns) of the shrunken image
desired_size = np.floor(scale_factor*np.array(im.shape)).astype('int')
# Make sure desired size is ODD for easier placement
if desired_size[0] % 2 == 0:
desired_size[0] += 1
if desired_size[1] % 2 == 0:
desired_size[1] += 1
# Resize the image. Columns come first, followed by rows, which is why we
# reverse the desired_size array
rsz = cv2.resize(im, tuple(desired_size[::-1]))
# Determine half width of both dimensions of shrunken image
half_way = np.floor(desired_size/2.0).astype('int')
# Create output image that is the same size as the input and find its centre
out = np.zeros_like(im, dtype='uint8')
centre = np.floor(np.array(im.shape)/2.0).astype('int')
# Place shrunken image in the centre of the larger output image
out[centre[0]-half_way[0]:centre[0]+half_way[0]+1, centre[1]-half_way[1]:centre[1]+half_way[1]+1] = rsz
# Show the image, wait for user key, then close window and write image
cv2.imshow('Reduced shape', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('out.png', out)
We get:
Another suggestion
What I can also recommend you do is pad the array with zeroes, then reshrink the image back to its original size. You would essentially extend the borders of the original image so that the borders contain zeroes. In this case, we would do what Mark Ransom also suggested, but we are working within the inside, out.
Here's the way to pad a matrix with zeroes using OpenCV C++: Pad array with zeros- openCV . However, in Python, simply use numpy's pad function:
import numpy as np # Import relevant packages - numpy and OpenCV
import cv2
# Read in image and threshold - convert to grayscale first
im = cv2.imread('c8lfe.jpg', 0)
# Set how many pixels along the border you want to add on each side
pad_radius = 75
# Pad the image
out = np.lib.pad(im, ((pad_radius, pad_radius), (pad_radius, pad_radius)), 'constant', constant_values=((0,0),(0,0)))
# Shrink it back to what the original size was
out = cv2.resize(out, im.shape[::-1])
# Show the image, wait for user key, then close window and write image
cv2.imshow('Reduced shape', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('out.png', out)
We thus get:

Fusion of two images in matlab

Consider two images. The size of these two images could be anything. Bring size of those two images same. Develop an algorithm to mix these two images, such that alternate pixels are brought from two image courses. It is fusion of two images. For example, pixel 1 is image1’s, pixel 2 is from image 2, 3rd pixel from image 1 and so on like that……
I know you prefer to use Matlab, but until someone gives you a Matlab answer, you may like to play around with ImageMagick which can do this for you and is in most Linux distributions anyway and available for free for Windows and Mac OSX.
First, let's create 2 images of different sizes and colours:
convert -size 300x300 xc:blue image1.png
convert -size 200x400 xc:red image2.png
Basically, you can resize images as you read them in by specifying the image size in square brackets after the filename, so I am arbitrarily choosing to resize both images to 256x256 pixels. Then I use the extremely powerful fx operator, so detect if I am processing an odd or an even numbered pixel, and choose either from the first or the second image accordingly:
convert image1.png[256x256] image2.png[256x256] -fx "i%2?u:v" out.png
Here is a way to do it with MATLAB.
clear
clc
%// Initialize red and blueimages
RedImage = zeros(300,300,3,'uint8');
BlueImage = zeros(200,400,3,'uint8');
%// Color them
RedImage(:,:,1) = 255;
BlueImage(:,:,3) = 255;
figure('Color',[1 1 1]);
%// Show them
subplot(1,2,1)
imshow(RedImage)
subplot(1,2,2)
imshow(BlueImage)
It looks like this:
%// Resize them to same size
RedImage = imresize(RedImage,[256 256]);
BlueImage = imresize(BlueImage,[256 256]);
%// Initialize new image
NewImage = zeros(256,256,3,'uint8');
%// Assign alternate pixels to new images
NewImage(1:2:end,1:2:end,:) = RedImage(1:2:end,1:2:end,:);
NewImage(2:2:end,2:2:end,:) = BlueImage(2:2:end,2:2:end,:);
figure
imshow(NewImage)
Which outputs this:
It looks dark but resize the figure will show you that it works indeed!
Hope that helps! Have fun.

Python - Checking for color in the center of an image

Hi I was wondering how to check the average color of the center of an image and take the image from a webcam.
I looked into PIL and doesn't seem like it can access the webcam to grab a photo, or is it possible?
I found this piece of code
im = Image.open('image.gif')
rgb_im = im.convert('RGB')
r, g, b = rgb_im.getpixel((60, 80))
print r, g, b
That will check a single pixel. I'm new to python and am wondering how to do a loop to check a 20x20 pixel square and then average the RGB.
Also is there a simple way to determine a color?
(I am doing some if > and < statements with RGB right now)
Crop the center part of the image which you want
Select the crop part.
Find its color by using this below code
result = cropped_image.convert('P', palette=Image.ADAPTIVE, colors=5)
This output the 5 colors which are used. You can change colors=1 for one color.
You can now use Image.quantize method.
Similar answer : How do I convert any image to a 4-color paletted image using the Python Imaging Library?

Resources