c++ - FreeImage+OpenCV - 16bit image get distorted - c++

I'm trying to load an image because I have to apply an algorithm on it.
If I load an 8 bit-per-channel image there are no problems, but if I load a 16bpc image then it get "ruined". Unfortunatly, since I don't have enough reputation I can't uplad images.
Those are the links to them:
Either the source and the 8bpc processing result
http://postimg.org/image/gc0zf2lp5/
..result if I process the same image saved as 16bpc
http://postimg.org/image/5nnwee7df/
And this is the code:
FreeImage_Initialise();
FREE_IMAGE_FORMAT formato = FreeImage_GetFileType(argv[1], 0);
FIBITMAP* imagetmp = FreeImage_Load(format, argv[1]);
FIBITMAP* image = FreeImage_Rotate(imagetmp, 180);
FreeImage_FlipHorizontal(image);
int depth = FreeImage_GetBPP(image);
printf("depth = %d\n", FreeImage_GetPitch(image));
cv::Mat img(FreeImage_GetHeight(image), FreeImage_GetWidth(image), CV_MAKETYPE(depth/3, 3), FreeImage_GetBits(image), FreeImage_GetPitch(image));
FreeImage_DeInitialise();
What could it be?

The value of depth is not what you expected. It refers to OpenCV depths defined as:
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
So, if you know that your FreeImage is of type FIT_RGB16, you should use as depth the value CV_16U. You should also convert from RGB to BGR, since OpenCV Mats are in BGR format.
Example here:
#include <FreeImage.h>
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
FreeImage_Initialise();
FREE_IMAGE_FORMAT format = FreeImage_GetFileType("path_to_image", 0);
FIBITMAP* imagetmp = FreeImage_Load(format, "path_to_image");
FIBITMAP* image = FreeImage_Rotate(imagetmp, 180);
FreeImage_FlipHorizontal(image);
int depth = FreeImage_GetBPP(image);
printf("depth = %d\n", FreeImage_GetPitch(image));
// FreeImage to Mat conversion
cv::Mat img(FreeImage_GetHeight(image), FreeImage_GetWidth(image), CV_MAKETYPE(CV_16U, 3), FreeImage_GetBits(image), FreeImage_GetPitch(image));
cvtColor(img, img, CV_BGR2RGB);
FreeImage_DeInitialise();
return 0;
}
Note that you may also avoid to create an additional FreeImage image just to flip it, and let OpenCV Mat to do that:
#include <FreeImage.h>
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
FreeImage_Initialise();
FREE_IMAGE_FORMAT format = FreeImage_GetFileType("path_to_image", 0);
FIBITMAP* image = FreeImage_Load(format, "path_to_image");
// FreeImage to Mat conversion
cv::Mat img(FreeImage_GetHeight(image), FreeImage_GetWidth(image), CV_MAKETYPE(CV_16U, 3), FreeImage_GetBits(image), FreeImage_GetPitch(image));
cvtColor(img, img, CV_BGR2RGB);
flip(img,img,0);
FreeImage_DeInitialise();
return 0;
}
You can't show this image directly with cv::imshow. You need to convert it to CV_8UC3 type to see it. You can do that for example calling convertScaleAbs(img, img); before imshow.
Or you can refer to this answer for a function to convert all types of FreeImage to OpenCV Mats.

Related

Convert from RGB to YUYV in OpenCV

Is there a way to convert from RGB to YUYV (YUY 4:2:2) format? I noted that OpenCV has reverse operation, but not RGB to YUYV for some reason. Maybe someone can point to code which does that (even outside of OpenCV library)?
UPDATE
I found libyuv library which may work for this purpose by doing BGR to ARGB conversion and then ARGB to YUY2 format (hopefully this is the same as YUYV 4:2:2). But it doesn't seem to work. Do you happen to know what yuyv buffer dimensions/type should look like? What its stride?
To clarify YUYV and YUY2 are the same formats if it helps.
UPDATE 2
Here is my code of using libyuv library:
Mat frame;
// Convert original image im from BGR to BGRA for further use in libyuv
cvtColor(im, frame, CVX_BGR2BGRA);
// Actually libyuv requires ARGB (i.e. reverse of BGRA), so I swap channels here
int from_to[] = { 0,3, 1,2, 2,1, 3,0 };
mixChannels(&frame, 1, &frame, 1, from_to, 4);
// This is the most confusing part. Not sure what argb_stride suppose to be - length of a row in bytes or size of single value in the array?
const uint8_t* argb_data = frame.data;
int argb_stride = 8;
// Also it is not clear what size of yuyv frame should be since we duplicate one Y
Mat yuyv(frame.rows, frame.cols, CVX_8UC2);
uint8_t* yuyv_data = yuyv.data;
int yuyv_stride = 16;
// Do actual conversion
libyuv::ARGBToYUY2(argb_data, argb_stride, yuyv_data, yuyv_stride,
frame.cols, frame.rows);
// Then I feed yuyv_data to video stream buffer and see green or purple image instead of video stream.
UPDATE 3
Mat frame;
cvtColor(im, frame, CVX_BGR2BGRA);
// ARGB
int from_to[] = { 0,3, 1,2, 2,1, 3,0 };
Mat rgba(frame.size(), frame.type());
mixChannels(&frame, 1, &rgba, 1, from_to, 4);
const uint8_t* argb_data = rgba.data;
int argb_stride = rgba.cols*4;
Mat yuyv(rgba.rows, rgba.cols, CVX_8UC2);
uint8_t* yuyv_data = yuyv.data;
int yuyv_stride = width * 2;
int res = libyuv::ARGBToYUY2(argb_data, argb_stride, yuyv_data, yuyv_stride, rgba.cols, rgba.rows);
It appears that although method is called ARGBToYUY2 it requires BGRA order of channels (not reverse).

OpenCV Image substraction signed output

I wouls like to subtract two gray scale images (CV_8UC1) and get their signed difference as result(CV_16SC1) .
I have tried the code below but i get as difference a CV_8UC1 matrix insted of a signed CV_16SC1.
Could you please help properly defining the Mask matrix and data type parameter?
Thanks!
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include "Imagesubstraction.h"
using namespace cv;
using namespace std;
int main(void)
{
Mat M1, M2,Dif;
M1 = imread("../data/difference/a.bmp", CV_LOAD_IMAGE_GRAYSCALE);
M2 = imread("../data/difference/b.bmp", CV_LOAD_IMAGE_GRAYSCALE);
Mat Mask(1024, 1024, CV_8UC1, Scalar(1));
subtract(M1, M2, Dif,Mask,3);
imwrite("../data/difference/c.bmp", Dif);
return 0;
}
Of course you're going to get an unsigned matrix after saving it to BMP format. As stated in OpenCV documentation:
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.
But if you take a look at your Dif matrix before saving it, you will see it is a 16-bit signed matrix.
At least, the following code snippet works as expected:
cv::Mat m1(100, 100, CV_8U, cv::Scalar(50));
cv::Mat m2(100, 100, CV_8U, cv::Scalar(30));
cv::Mat dif;
cv::Mat mask(100, 100, CV_8U, cv::Scalar(255));
cv::subtract(m2, m1, dif, mask, CV_16S);
std::cout << dif << std::endl;

OpenCV convert CV_8U to CV_64F

I am trying to convert a grayscale image to the type CV64F. From the OpenCv documentation I have understood that a grayscale image is of the type CV_8U. I have also found that imshow plots different types differently, hence I need to divide by 255 before converting. But after converting the image I still get many saturated pixels.
I am using this image, saved as a jpg:
http://www.ele.uri.edu/~hansenj/projects/ele585/lab2/cameraman.gif
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <cstring>
#include <cmath>
int main()
{
Mat I, input_image;
string path = "C:/<your_path>/camera_man.jpg";
input_image = imread(path.c_str(), 0); // Read the file as grayscale
imshow("Original", input_image);
// Convert image to CV_64F
input_image *= (double) 1 / 255;
input_image.convertTo(I, CV_64F);
imshow("Converted", I);
}
When you do this:
input_image *= (double) 1 / 255; // (1)
input_image.convertTo(I, CV_64F); // (2)
You are dividing each value in a CV_8UC1 matrix by 255 in (1), so each pixel will be:
new_value = static_cast<uchar>(old_value / 255)
so that new_value can have only values 0 for 0 <= old_value < 255, and 1 for old_value = 255. Then the conversion is applied in (2) on truncated values.
So, you need either to first convert to CV_64FC1 and then divide:
input_image.convertTo(I, CV_64F);
I *= (double)1 / 255;
or apply scaling directly during the conversion:
input_image.convertTo(I, CV_64F, 1.0 / 255.0);

Copy Mat in opencv

I try to copy a image to other image using opencv, but I got a problem. Two image is not the same, like this:
This is the code I used:
#include <opencv2\opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <cmath>
#include <iostream>
#include <opencv2\opencv.hpp>
int main()
{
cv::Mat inImg = cv::imread("C:\\Users\\DUY\\Desktop\\basic_shapes.png");
//Data point copy
unsigned char * pData = inImg.data;
int width = inImg.rows;
int height = inImg.cols;
cv::Mat outImg(width, height, CV_8UC1);
//data copy using memcpy function
memcpy(outImg.data, pData, sizeof(unsigned char)*width*height);
//processing and copy check
cv::namedWindow("Test");
imshow("Test", inImg);
cv::namedWindow("Test2");
imshow("Test2", outImg);
cvWaitKey(0);
}
Simply use .clone() function of cv::Mat:
cv::Mat source = cv::imread("basic_shapes.png");
cv::Mat dst = source.clone();
This will do the trick.
You are making an image with one channel only (which means only shades of gray are possible) with CV_8UC1, you could use CV_8UC3 or CV_8UC4 but for simply copying stick with the clone function.
You actually don't want to copy the data, since you start with a RGB CV_8UC3 image, and you want to work on a grayscale CV_8UC1 image.
You should use cvtColor, that will convert your RGB data into grayscale.
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
int main()
{
Mat inImg = cv::imread("C:\\Users\\DUY\\Desktop\\basic_shapes.png"); // inImg is CV_8UC3
Mat outImg;
cvtColor(inImg, outImg, COLOR_RGB2GRAY); // Now outImg is CV_8UC1
//processing and copy check
imshow("Test", inImg);
imshow("Test2", outImg);
waitKey();
}
With a simple memcopy you're copying a sequence of uchar like this:
BGR BGR BGR BGR ...
into an image that expects them to be (G for gray):
G G G G ...
and that's is causing your outImg to be uncorrect.
Your code will be correct if you define outImage like:
cv::Mat outImg(width, height, CV_8UC3); // Instead of CV_8UC1
the best way is to use the opencv clone method:
cv::Mat outImg = inImg.clone();
Your original image is in color. cv::Mat outImg(width, height, CV_8UC1); says that your new image is of data type CV_8UC1 which is an 8-bit grayscale image. So you know that is not correct. Then you try to copy the amount of data from the original image to the new image that corresponds to total pixels * 8-bits which is at best 1/3 of the actual image (assuming the original image was 3 color, 8-bits per color, aka a 24-bit image) and perhaps even 1/4 (if it had an alpha channel, making it 4 channels of 8-bits or a 32-bit image).
TLDR: you're matrices aren't the same type, and you are making assumptions about the size of the data to be copied off of an incorrect, and incorrectly sized type.
Here is a simple code to copy image.
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <cmath>
int main()
{
cv::Mat inImg = cv::imread("1.jpg");
cv::Mat outImg = inImg.clone();
cv::namedWindow("Test");
imshow("Test", inImg);
cv::namedWindow("Test2");
imshow("Test2", outImg);
cvWaitKey(0);
}
Mat source = imread("1.png", 0);
Mat dest;
source.copyTo(dest);

Convert RGB to Black & White in OpenCV

I would like to know how to convert an RGB image into a black & white (binary) image.
After conversion, how can I save the modified image to disk?
AFAIK, you have to convert it to grayscale and then threshold it to binary.
1. Read the image as a grayscale image
If you're reading the RGB image from disk, then you can directly read it as a grayscale image, like this:
// C
IplImage* im_gray = cvLoadImage("image.jpg",CV_LOAD_IMAGE_GRAYSCALE);
// C++ (OpenCV 2.0)
Mat im_gray = imread("image.jpg",CV_LOAD_IMAGE_GRAYSCALE);
2. Convert an RGB image im_rgb into a grayscale image: Otherwise, you'll have to convert the previously obtained RGB image into a grayscale image
// C
IplImage *im_rgb = cvLoadImage("image.jpg");
IplImage *im_gray = cvCreateImage(cvGetSize(im_rgb),IPL_DEPTH_8U,1);
cvCvtColor(im_rgb,im_gray,CV_RGB2GRAY);
// C++
Mat im_rgb = imread("image.jpg");
Mat im_gray;
cvtColor(im_rgb,im_gray,CV_RGB2GRAY);
3. Convert to binary
You can use adaptive thresholding or fixed-level thresholding to convert your grayscale image to a binary image.
E.g. in C you can do the following (you can also do the same in C++ with Mat and the corresponding functions):
// C
IplImage* im_bw = cvCreateImage(cvGetSize(im_gray),IPL_DEPTH_8U,1);
cvThreshold(im_gray, im_bw, 128, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
// C++
Mat img_bw = im_gray > 128;
In the above example, 128 is the threshold.
4. Save to disk
// C
cvSaveImage("image_bw.jpg",img_bw);
// C++
imwrite("image_bw.jpg", img_bw);
This seemed to have worked for me!
Mat a_image = imread(argv[1]);
cvtColor(a_image, a_image, CV_BGR2GRAY);
GaussianBlur(a_image, a_image, Size(7,7), 1.5, 1.5);
threshold(a_image, a_image, 100, 255, CV_THRESH_BINARY);
I do something similar in one of my blog postings. A simple C++ example is shown.
The aim was to use the open source cvBlobsLib library for the detection
of spot samples printed to microarray slides, but the images have to be
converted from colour -> grayscale -> black + white as you mentioned, in order to achieve this.
A simple way of "binarize" an image is to compare to a threshold:
For example you can compare all elements in a matrix against a value with opencv in c++
cv::Mat img = cv::imread("image.jpg", CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat bw = img > 128;
In this way, all pixels in the matrix greater than 128 now are white, and these less than 128 or equals will be black
Optionally, and for me gave good results is to apply blur
cv::blur( bw, bw, cv::Size(3,3) );
Later you can save it as said before with:
cv::imwrite("image_bw.jpg", bw);
Simple binary threshold method is sufficient.
include
#include <string>
#include "opencv/highgui.h"
#include "opencv2/imgproc/imgproc.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("./img.jpg",0);//loading gray scale image
threshold(img, img, 128, 255, CV_THRESH_BINARY);//threshold binary, you can change threshold 128 to your convenient threshold
imwrite("./black-white.jpg",img);
return 0;
}
You can use GaussianBlur to get a smooth black and white image.