Convert jpg image in ppm P3 ASCII format using Opencv - c++

Using opencv imwrite function I managed to convert jpg image in ppm P6 format.
Mat image = imread(picPath);
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PXM_BINARY);
compression_params.push_back(1);
imwrite("bez.ppm", image, compression_params);
Problem is that I actually have to convert jpg image in ppm P3 ASCII format.
Does anyone know how to do it?
Thanks!
EDIT:
In the project I have the following piece of code where I check the maximum value of pixels:
int maxVal;
fscanf(in, "%d", &maxVal);
if (maxVal != 255)
{
printf("Input file error: Not a Netpbm color image with 256 levels\n");
exit(0);
}
When I set parameter 0 then I get: Not a Netpbm color image with 256 levels!
When I do the conversion from jpg to ppm p3 with irfanview program works.

The code involved is in the file modules/imgcodecs/src/grfmt_pxm.cpp in the OpenCV source tree.
It sets the internal flag isBinary like this according to the compression parameters:
for( size_t i = 0; i < params.size(); i += 2 )
if( params[i] == CV_IMWRITE_PXM_BINARY )
isBinary = params[i+1] != 0;
so, if you want ASCII (P3) you need to have
compression_params.push_back(0)
and have image type CV_8UC1, CV_8UC3 or CV_16UC1.

Related

OpenCV image conversion goes wrong

I have an algorithm that does some stuff. Among them, there is a conversion that works fine if I'm working on a CV_8UC3 image but goes wrong if the file type is C_16UC3.
This is some code:
//new image is created
Mat3w img(100,100,Vec3w(1000,0,0));
//Image Conversion - ERROR!
cv::Mat inputSource;
//saving the image here will work
img.convertTo(inputSource, CV_64FC3);
//saving the image here will not work -> black image
The problem is that the CV_16UC3 image's processing result is an image of the right dimensions but fully black.
The problem is in the conversion because saving the image right before will give a legit one while saving it right after will give an almost completely white one.
EDIT:
I made some changes: cut off some useless code and added the inputSource declaration.
Now, while I was trying stuff, I arrived at the conclusion that either I haven't understood the CV Types, or something strange is happening.
I always thought that the number in the type was indicating the number of bits per channel. So, in my head, CV_16UC3 is a 3 channel with 16bits per channel. That idea is strengthened by the fact that the image I save during as tests (before the img.convertTo) actually had matching bits per channel number. The strange thing, is that the saved inputSource (type CV_64FC3) is an 8bpc image.
What's am I missing?
You get confused with the way imwrite and imread work in OpenCV. From the OpenCV documentation
imwrite
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.
imread
The function imread loads an image from the specified file and returns it. Possible flags are:
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.
So for your case, CV_16U are saved without conversion, while CV_64F is converted and saved as CV_8U. If you want to store double data, you should use FileStorage.
You should also take care to use imread the image with the appropriate flag.
This example should clarify:
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
// Create a 16-bit 3 channel image
Mat3w img16UC3(100, 200, Vec3w(1000, 0, 0));
img16UC3(Rect(0, 0, 20, 50)) = Vec3w(0, 2000, 0);
// Convert to 64-bit (double) 3 channel image
Mat3d img64FC3;
img16UC3.convertTo(img64FC3, CV_64FC3);
// Save to disk
imwrite("16UC3.png", img16UC3); // No conversion
imwrite("64FC3.png", img64FC3); // Converted to CV_8UC3
FileStorage fout("64FC3.yml", FileStorage::WRITE);
fout << "img" << img64FC3; // No conversion
fout.release();
Mat img_maybe16UC3_a = imread("16UC3.png" /*, IMREAD_COLOR*/); // Will be CV_8UC3
Mat img_maybe16UC3_b = imread("16UC3.png", IMREAD_ANYDEPTH); // Will be CV_16UC1
Mat img_maybe16UC3_c = imread("16UC3.png", IMREAD_UNCHANGED); // Will be CV_16UC3
Mat img_maybe64FC3_a = imread("64FC3.png" /*, IMREAD_COLOR*/); // Will be CV_8UC3
Mat img_maybe64FC3_b = imread("64FC3.png", IMREAD_ANYDEPTH); // Will be CV_8UC1
Mat img_maybe64FC3_c = imread("64FC3.png", IMREAD_UNCHANGED); // Will be CV_8UC3
Mat img_mustbe64FC3;
FileStorage fin("64FC3.yml", FileStorage::READ);
fin["img"] >> img_mustbe64FC3; // Will be CV_64FC3
fin.release();
return 0;
}

Loading 12-Bit Raw GreyScale Image using Opencv

I'm working with opencv 2.4.9. I would like to load a 12-Bit grey scale raw(.raw) image stored as Y16(16-Bit) format.This format contains only a single, 16 bit Y plane for monochrome images. Each pixel is represented by a 16 bit, little endian format.
I used the following code for loading the image.
Mat Img_Source16Bit_Gray(m_ImgWidth,m_ImgHeight,CV_16UC1);
Mat Img_Destination8Bit_Gray;
FILE * f;
f=fopen(FileName_S.c_str(),"rb");
if ( !f )
{
MessageBox(L"File Not Found");
return;
}
uchar* Pixels_Char_16Bit;
Pixels_Char_16Bit = new uchar[m_ImgWidth * m_ImgHeight *2];
fread(Pixels_Char_16Bit,m_ImgWidth * m_ImgHeight*2,1,f);
fclose(f);
Img_Source16Bit_Gray.data= Pixels_Char_16Bit;
Img_Source16Bit_Gray.convertTo(Img_Destination8Bit_Gray,CV_8UC1,1);
imshow("Img_Source16Bit_Gray",Img_Source16Bit_Gray);
imshow("Img_Destination8Bit_Gray",Img_Destination8Bit_Gray);
Actual image is shown in the right side & the output left hand side I'm getting is not correct & the Result8 bit image is full of white pixels. Can anyone please provide me the steps to load a 16 Bit grey scale image?
Thank you for helping me to find the answer! Here is my updated code.
Mat Img_Source16Bit_Gray(m_ImgHeight,m_ImgWidth,CV_16UC1);
Mat Img_Destination8Bit_Gray(m_ImgHeight,m_ImgWidth,CV_8UC1);
FILE * f;
f=fopen(FileName_S.c_str(),"rb");
if ( !f )
{
MessageBox(L"File Not Found");
return;
}
char16_t* pY16Pixels;//w-2592 h- 1944
pY16Pixels = new char16_t[m_ImgWidth * m_ImgHeight];
fread(pY16Pixels,m_ImgWidth*m_ImgHeight*2,1,f);
Img_Source16Bit_Gray.data= reinterpret_cast<uchar*>(pY16Pixels);
double minVal, maxVal;
minMaxLoc(Img_Source16Bit_Gray, &minVal, &maxVal); //find minimum and maximum intensities
Img_Source16Bit_Gray.convertTo(Img_Destination8Bit_Gray, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
namedWindow("Img_Source16Bit_Gray",WINDOW_NORMAL);
namedWindow("Img_Destination8Bit_Gray",WINDOW_NORMAL);
imshow("Img_Source16Bit_Gray",Img_Source16Bit_Gray);
imshow("Img_Destination8Bit_Gray",Img_Destination8Bit_Gray);

OpenCV: Error copying one image to another

I am trying to copy one image to another pixel by pixel (I know there are sophisticated methods available. I am trying to solve another problem and answer to this will be useful).
This is my code:
int main()
{
Mat Img;
Img = imread("../../../stereo_images/left01.jpg");
Mat copyImg = Mat::zeros(Img.size(), CV_8U);
for(int i=0; i<Img.rows; i++){
for(int j=0; j<Img.cols; j++){
copyImg.at<uchar>(j,i) = Img.at<uchar>(j,i);
}}
namedWindow("Image", CV_WINDOW_AUTOSIZE );
imshow("Image", Img);
namedWindow("copyImage", CV_WINDOW_AUTOSIZE );
imshow("copyImage", copyImg);
waitKey(0);
return 0;
}
When I run this code in visual studio I get the following error
OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 < (unsigned)si
ze.p[0] && (unsigned)(i1*DataType<_Tp>::channels) < (unsigned)(size.p[1]*channel
s()) && ((((sizeof(size_t)<<28)|0x8442211) >> ((DataType<_Tp>::depth) & ((1 << 3
) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file c:\opencv\opencv-2.4.9\ope
ncv\build\include\opencv2\core\mat.hpp, line 537
I know for fact that Img's type is CV_8U. Why does this happen ?
Thanks!
// will read in a rgb image , no matter what the content is
Img = imread("../../../stereo_images/left01.jpg");
to make it read grayscale images use:
Img = imread("../../../stereo_images/left01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
then, you don't need to copy per pixel (and you should even avoid that), just use:
Mat im2 = Img.clone();
if you do per-pixel loops, watch out to get the indices right. it's row-col world here, not x,y, so it should be:
copyImg.at<uchar>(i,j) = Img.at<uchar>(i,j);
in your case
I know for fact that Img's type is CV_8U.
But CV_8U is just the image depth (8-bit U-nsigned). The type also specifies the number of channels, which is usually three. One for blue, one for green and one for red in this order as default for OpenCV. The type would be CV_8UC3 (C-hannels = 3). imread will convert even a black and white image to a 3-channel image by default. imread(filename, CV_LOAD_IMAGE_GRAYSCALE) will load a 1-channel image (CV_8UC1). But if you're not sure the easiest solution is
Mat copyImg = Mat::zeros(Img.size(), Img.type());
To access the array elements you have to know the size of it. Using .at<uchar>() on a 3-channel image will only access the first channel because you have 3*8 bit per pixel. So on a 3-channel image you have to use
copyImg.at<Vec3b>(i,j) = Img.at<Vec3b>(i,j);
where Vec3b is a cv::Vec<uchar, 3>. You should also note that the first argument of at<>(,) is the index along dim 0 which are the rows and second argument cols. Or in other words in classic 2d-xy-chart order you access a pixel with .at<>(y,x) == .at<>(Point(x,y)).
your problem is with this line :
copyImg.at<uchar>(j,i) = Img.at<uchar>(j,i);
It should be :
copyImg.at<uchar>(i,j) = Img.at<uchar>(i,j);
Note that if you want to copy image you can simply do this :
Mat copyImg = Img.clone();

DevIL library: save gray scale image in three matrices instead one

I need to make a program that convert a RGB image to a GRAYSCALE image and save it in PGM format. I use DevIL library, but when I save the image, I obtain always a 3D image (3 matrix), in grayscale but, if I load it in MATLAB, I have 3 matrices instead of just one. How can I obtain just one matrix in my output file using DevIL?
int main()
{
ilInit();
ilEnable(IL_ORIGIN_SET);
ilOriginFunc(IL_ORIGIN_UPPER_LEFT);
ilEnable(IL_FILE_OVERWRITE);
ILuint ImageName; // The image name to return.
ilGenImages(1, &ImageName);
ilBindImage(ImageName);
if(!ilLoadImage("/home/andrea/Scrivania/tests/siftDemoV4/et000.jpg"))
{ printf("err");
exit;
}
else
printf("caricata\n");
ILuint width,height;
width = ilGetInteger(IL_IMAGE_WIDTH);
height = ilGetInteger(IL_IMAGE_HEIGHT);
double v[3]={0.2989360212937755001405548682669177651405334472656250000,0.5870430744511212495240215503145009279251098632812500000,0.1140209042551033058465748126764083281159400939941406250};
printf("%.55f %.55f %.55f",v[0],v[1],v[2]);
ILubyte *imgValue=ilGetData();
int i=0;
ILubyte imgNuova[width*height];
while( i < width*height)
{
imgNuova[i]=(char)round( ( (double)imgValue[3*i]*v[0])+ ( (double)imgValue[3*i+1]*v[1])+((double)imgValue[3*i+2]*v[2]));
i++;
}
ILuint ImageName2;
ilGenImages(2, &ImageName2);
ilBindImage(ImageName2);
ilTexImage(width, height, 1, 1, IL_LUMINANCE,
IL_UNSIGNED_BYTE, imgNuova);
iluFlipImage();
ilSave(IL_PNM,"/home/andrea/Scrivania/tests/siftDemoV4/et000new.pgm");
return 0;
}
Unfortunately, due to a bug in the PNM export, DevIL can and will only write PPM (Portable Pixmaps, 3 channel RGB) files regardless of the file extension. The only solution to this is to use a different file format, that supports single channel grayscale images, like PNG.
Matlab should be able to use that just as well. If you absolutely need or want files in the PGM format, you will have to use a converter like png2pnm.

How should I name my RGB channels, using cv::Mat_

I want to access my matrix elements in the following manner:
frame[i][j].Red
, that is, the (i,j)-th pixe's red channel.
I have tried:
typedef struct{unsigned char Blue,Green,Red;}Pixel;
typedef cv::Mat_<Pixel> Image;
However when trying to imread(), imwrite() or whatever with the thus defined type, g++ greets me with:
OpenCV Error: Assertion failed (func != 0) in convertTo, file /home/users/mvitkov/projects/opencv-legacy/OpenCV-2.3.1/modules/core/src/convert.cpp, line 937
terminate called after throwing an instance of 'cv::Exception'
what(): /home/users/mvitkov/projects/opencv-legacy/OpenCV-2.3.1/modules/core/src/convert.cpp:937: error: (-215) func != 0 in function convertTo
Update: So no answer to my probably badly asked question. Too bad. The essence of the question is how to address the individual channels with sensible names (red, green, bkue), and not the c-era array indexing notation [2]. Duh!
Here's how you access each channel:
blue = frame.at<cv::Vec3b>(i,j)[0];
green = frame.at<cv::Vec3b>(i,j)[1];
red = frame.at<cv::Vec3b>(i,j)[2];
The above code assumes that you have a 3-channel image where each value is an 8-bit unsigned char (CV_8UC3). This type is used in many common image formats. However, if you have a different type of 3-channel image, here's what you do:
If the image type is 3-channel float (CV_32FC3), then replace cv::Vec3b with cv::Vec3f
If the image type is 3-channel double (CV_64FC3), then replace cv::Vec3b with cv::Vec3d
If the image type is 3-channel int (CV_32SC3), then replace cv::Vec3b with cv::Vec3i
If the image type is 3-channel short int (CV_16SC3) or 16-bit uchar (CV_16UC3), then replace cv::Vec3b with cv::Vec3s
Not sure what image format you're using? Try calling getImgType(frame) (see the code below).
string getImgType(cv::Mat frame)
{
int imgTypeInt = frame.type();
int numImgTypes = 28; // 7 base types, with 4 channel options each (C1, ..., C4)
int enum_ints[] = {CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4, CV_8SC1, CV_8SC2, CV_8SC3, CV_8SC4, CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4, CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4, CV_32SC1, CV_32SC2, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4, CV_64FC1, CV_64FC2, CV_64FC3, CV_64FC4};
string enum_strings[] = {"CV_8U", "CV_8UC1", "CV_8UC2", "CV_8UC3", "CV_8UC4", "CV_8SC1", "CV_8SC2", "CV_8SC3", "CV_8SC4", "CV_16UC1", "CV_16UC2", "CV_16UC3", "CV_16UC4", "CV_16SC1", "CV_16SC2", "CV_16SC3", "CV_16SC4", "CV_32SC1", "CV_32SC2", "CV_32SC3", "CV_32SC4", "CV_32FC1", "CV_32FC2", "CV_32FC3", "CV_32FC4", "CV_64FC1", "CV_64FC2", "CV_64FC3", "CV_64FC4"};
for(int i=0; i<numImgTypes; i++)
{
if(imgTypeInt == enum_ints[i]) return enum_strings[i];
}
return "unknown image type";
}
If the image type is 3-channel short int (CV_16SC3) or 16-bit uchar (CV_16UC3), then replace cv::Vec3b with cv::Vec3s
this is simply not correct. I adressed a 3 channel 16 bit unsigned image once with < Vec3s>, and got a negative value G:29096 B:-21671 R:23413 returned.
If CV_16UC3, adress Mat with < Vec3w>. Using this I got G:29096 B:43865 R:23413
the "s" in CV_16SC3 stands for signed and not short.