OpenCV convert from B/W to RGB - c++

i have converted an image from RGB to B/W then i want to convert it back to RGB but i have a problem on that:
my code:
int width=zoomedImage->width;
int height=zoomedImage->height;
TempImage=cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1);
cvCvtColor(zoomedImage, TempImage,CV_RGB2GRAY);
cvThreshold( TempImage, TempImage,128,256,CV_THRESH_BINARY);
cvCvtColor( TempImage,zoomedImage,CV_GRAY2RGB);
this->pictureBox1->Image=(gcnew
System::Drawing::Bitmap(zoomedImage->width,zoomedImage->height,zoomedImage->widthStep,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,(System::IntPtr)zoomedImage->imageData));
here i'm displaying zoomedImage as a B/W image,in another action i want to display zoomedImage as an RGB image the major problem here is that i can't change the image that will be draw as another parts of my code is depending on this sequence, i wrote that in the other action:
cvCvtColor( TempImage,zoomedImage,CV_GRAY2RGB);
this->pictureBox1->Image=(gcnew
System::Drawing::Bitmap(zoomedImage->width,zoomedImage->height,zoomedImage->widthStep,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,(System::IntPtr)zoomedImage->imageData));
but zoomedImage still dispalyed as B/W, i heared that when a true color image is converted to gray it can't be returned again as a true color image, so what does CV_GRAY2RGB do?

When you convert an RGB image to a gray level image, color information is lost, and this information cannot be recovered fom the gray level image again.
When you try to convert B/W image to RGB you only make a 3 channel image, but all channels contain the same intensity data. Hence you get a gray level image with 3 channels. Nothing more.

i have solved my problem as following:
Convert Original image to B/W
int width=zoomedImage->width;
int height=zoomedImage->height;
ColorSaver=cvCreateImage(cvSize(width,height),zoomedImage->depth,zoomedImage->nChannels);
ColorSaver=cvCloneImage(zoomedImage);
TempImage=cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1);
cvCvtColor(zoomedImage, TempImage,CV_RGB2GRAY);
cvThreshold( TempImage, TempImage,128,256,CV_THRESH_BINARY);
cvCvtColor( TempImage,zoomedImage,CV_GRAY2RGB);
this->pictureBox1->Image=(gcnew
System::Drawing::Bitmap(zoomedImage->width,zoomedImage->height,zoomedImage->widthStep,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,(System::IntPtr)zoomedImage->imageData));
return back the original image to RGB:
zoomedImage=cvCloneImage(ColorSaver);
this->pictureBox1->Image=(gcnew
System::Drawing::Bitmap( zoomedImage->width, zoomedImage->height, zoomedImage->widthStep,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,(System::IntPtr) zoomedImage->imageData));

Related

how to read only one channel of image color from disk in opencv with C++

I need to read large numbers of images with high speed requirements, and just need to handle the Blue channel of a color image.
If I read image with cv::imread(imgName, CV_LOAD_IMAGE_COLOR); It will be very long time, so I want to read only one color image channel. How to do it ??? thanks very much !!
OpenCV doesn't provide any method to load only a specific channel. However, you have a few options.
Load as color image and extract the channel you need
cv::Mat3b img("path/to/image", cv::IMREAD_COLOR);
cv::Mat1b blue;
cv::extractChannel(img, blue, 0);
This is a little faster than using the split approach, but you still need to load the color image.
In a preprocessing stage, load all your images (you can use glob to retrieve all images into a folder), extract the blue channel and store it as grayscale. Then you can load the image as grayscale.
// Preprocessing
cv::String folder = "your_folder_with_images/*.jpg";
std::vector<cv::String> filenames;
cv::glob(folder, filenames);
for (size_t i = 0; i < std::filenames.size(); ++i)
{
cv::Mat3b img = cv::imread(filenames[i], cv::IMREAD_COLOR);
cv::Mat1b blue;
cv::extractChannel(img, blue, 0);
cv::imwrite("some/other/name", blue);
}
// Processing
cv::Mat1b blue = imread("path/to/image", cv::IMREAD_UNCHANGED);
You can improve speed by saving / loading the image in binary format:
// Preprocessing
...
matwrite("some/other/name", blue);
// Processing
cv::Mat1b blue = matread("path/to/image");
I think you can not do this, at least with OpenCV. If you check the documentation of cv::imread you will see that there is no option to read only one color channel:
IMREAD_UNCHANGED: If set, return the loaded image as is (with alpha channel, otherwise it gets cropped).
IMREAD_GRAYSCALE: If set, always convert image to the single channel grayscale image.
IMREAD_COLOR: If set, always convert image to the 3 channel BGR color image.
IMREAD_ANYDEPTH: If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
IMREAD_ANYCOLOR: If set, the image is read in any possible color format.
IMREAD_LOAD_GDAL: If set, use the gdal driver for loading the image.
IMREAD_REDUCED_GRAYSCALE_2: If set, always convert image to the single channel grayscale image and the image size reduced 1/2.
IMREAD_REDUCED_COLOR_2: If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2.
IMREAD_REDUCED_GRAYSCALE_4: If set, always convert image to the single channel grayscale image and the image size reduced 1/4.
IMREAD_REDUCED_COLOR_4: If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4.
IMREAD_REDUCED_GRAYSCALE_8: If set, always convert image to the single channel grayscale image and the image size reduced 1/8.
IMREAD_REDUCED_COLOR_8: If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8.
IMREAD_IGNORE_ORIENTATION: If set, do not rotate the image according to EXIF's orientation flag.
If you want, you can split the channels of a matrix after loading it usin Mat::split:
Mat src = imread("img.png",CV_LOAD_IMAGE_COLOR); //load image
Mat bgr[3]; //destination array
split(src,bgr);//split source
//Note: OpenCV uses BGR color order
imwrite("blue.png",bgr[0]); //blue channel
imwrite("green.png",bgr[1]); //green channel
imwrite("red.png",bgr[2]); //red channel

Applying adaptive threshold to a grayscale image

I have a png image which is in grayscale 'test.png'. I need apply adaptive threshold to this image. I am using OpenCV.
image = cv2.imread('test_big.png')
im = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
I am not able to apply adaptive threshold since the image is not in grayscale.
So I tried to read the image as grayscale:
image = cv2.imread('test_big.png',1)
Now I can apply adaptive threshold but the output will be a blue and red image instead of black and white. Can anyone help?
The fault lies in the second code snippet:
image = cv2.imread('test_big.png',1)
Although you have said that test_big.png is a grayscale image, you have declared it as a color image (RGB) with three channels.
Hence you have to change the code to
image = cv2.imread('test_big.png', 0)
0 -> grayscale image
1 -> color image
You can also try:
cv2.imread('test_big.png', cv2.IMREAD_GRAYSCALE)
The bottom line is: although the image being read is a grayscale image, the system will not recognize it until it is explicitly specified. In your case, your image was a grayscale image, but since you declared it as a color image it considered the image to have three channels (RGB) and hence the subsequent adaptive threshold function did not execute.

imwrite in opencv gives a black/white image

I wrote a code for watershed segmentation in C API. Now I am converting all those into C++. so, cvsaveimage becomes imwrite. But when I use imwrite ,all i get is a black image.
this is the code:-
Mat img8bit;
Mat img0;
img0 = imread("source.png", 1);
Mat wshed(img0.size(), CV_32S);
wshed.setTo(cv::Scalar::all(0));
////after performing watershed segmentation and
// displaying the watershed image from wshed//
wshed.convertTo(img8bit, CV_32FC3, 255.0);
imwrite("Watershed.png", img8bit);
The original image that I want to save is in wshed. I saw suggestions from the net that we need to convert it to 16 bit or higher so that the imwrite saves it right. Like you see,I tried that. But the wshed image is being displayed correctly when using imshow.The img0 is grey image/black and white while the wshed image is coloured. any help on this?
Edit- I changed the 4th line to
Mat wshed(img0.size(), CV_32FC3);
When calling Mat::convertTo() with a scalar (255 in your case), the values of every matrix item will be multiplied by this scalar value. This will cause all most every result pixel values exceed 255 (i.e. white pixels) except those of 0s where they remain 0 (i.e. black pixels). This is why you will get the black-white pixel in the end.
To make it work, simply change it to:
wshed.convertTo(img8bit, CV_32FC3);
You said:
The original image that I want to save is in wshed. I saw suggestions
from the net that we need to convert it to 16 bit or higher so that
the imwrite saves it right.
If saving the image does not work you should keep in mind that the image data has to be either 8-Bits or 16-Bit unsigned when using the imwrite Function, not 16-Bits or higher.
This is stated in the documentation:
The function imwrite saves the image to the specified file. The image
format is chosen based on the filename extension (see imread() for the
list of extensions). Only 8-bit (or 16-bit unsigned (CV_16U) in case
of PNG, JPEG 2000, and TIFF) single-channel or 3-channel (with ‘BGR’
channel order) images can be saved using this function. If the format,
depth or channel order is different, use Mat::convertTo() , and
cvtColor() to convert it before saving. Or, use the universal
FileStorage I/O functions to save the image to XML or YAML format.

Is there a way to have both grayscale and rgb pixels on the same image opencv C++?

I need to be able to work with images where some regions are grayscale while others are kept on the RGB format. I don't want to convert an image into a grayscale since it will lose the channels and will become simply one channeled, is there a way to keep the RGB channels of some pixels on the picture and turn the others into a grayscale?
NO.
I see two solutions to this:
Have both a gray (Mat1b) and a rgb (Mat3b) image, and work on the image you need.
Have a single rgb (Mat3b) image, and set r,g,b channels to the same gray value where you need. In this way you can mimic to have a mixed gray/rgb image.

In open cv, how can i convert gray scale image back in to RGB image(color)

In open cv to remove background, using current frame and former frame, i applied absdiff function and created a difference image in gray scale. However, i would like to covert the gray scale image back in to RGB with actual color of the image, but i have no idea how to operate this back in.
I'm using C++.
Could any one knowledgeable of open cv help me?
You cannot covert the gray scale image back into RGB with actual color of the image again as coverting RGB to gray scale is a data-losing process.
Instead, as #MatsPetersson suggested, you can take the use of the grayscale image to create a mask, e.g. by further applying a thresholding process. Then you can easily get the ROI color image by:
cv::Mat dst;
src.copyTo(dst, mask);