Load, show and convert multiple images - c++

I'm starting to write my first program in C++ with OpenCV and I would like to represent a set of images (stored in my project and libelled "brain_mri_001.jpg -> brain_mri_015.jpg) as vectors of length LxL where L is the number of pixels in the x(y) direction.
Here is my code :
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
using namespace std;
int main()
{
//load images
for(int i=1; i<=25; i++)
{
char filename[50];
sprintf( filename, "brain_mri_%d.jpg", i );
IplImage *img=cvLoadImage( filename, CV_LOAD_IMAGE_GRAYSCALE);
if (!img)
{
printf("Error: Image not found.\n");
return 2; //error : not found image
}
cvNamedWindow("Projet Image", CV_WINDOW_AUTOSIZE);// create a window
IplImage *img2=cvCloneImage(img); //clone img
cvShowImage("Projet Image", img2); // display the image in a window
cvWaitKey(0); //attendre touche
cvDestroyWindow("Projet Image"); // destroy the window
cvReleaseImage(&img); // memory
return 0; //finish with success
//convert IplImage -> Matrix
int height = img->height;
int width = img->width;
CvMat *mat = cvCreateMat(height,width,CV_32FC3);
//convert Matrix -> Vector
//CvMat row_header, *row;
//row = cvReshape(mat, &row_header, 0, 1);
CvMat vector_header;
cvReshape(img, &vector_header, 0, 1);
//check the height and width of vector_header
if(vector_header.height != 1)
{
fprintf(stderr, "vector_header's height is %d\n", vector_header.height);
}
if(vector_header.width != width*height)
{
fprintf(stderr, "vector_header's width is %d\n", vector_header.width);
}
}
}
I should have made a mistake but I don't know where :(
I would be grateful if anyone can answer me !
P.S. Excuse my bad English...

I think what you need to do is a) cvReleaseImage your images when you're done with them, and 2) your reshape code should be something like this
CvMat vector_header;
cvReshape(img2, &vector_header, 0, 1); /* same # of channels, 1 row */
Note that cvReshape doesn't copy the data, so vector_header doesn't need to be allocated with cvCreateMat. I'm not sure how to test this, except maybe to try loading in a very small image and plotting its values to stdout. As a sanity check, you could check the height and width of vector_header, something like
if(vector_header.height != 1)
{
fprintf(stderr, "vector_header's height is %d\n", vector_header.height);
}
if(vector_header.width != width*height)
{
fprintf(stderr, "vector_header's width is %d\n", vector_header.width);
}
I might have that backwards in terms of height and width. I'm also not 100% sure how the reshape will go, i.e. if your image is [1 2; 3 4], will the resulting vector be [1 2 3 4] or [1 3 2 4]? If you're doing something like PCA (e.g., the eigenfaces algorithm), then it might not matter as long as it's consistent.

Related

Initializing Mats

I have the following simple program
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{
if ( argc != 2 )
{
printf("usage: DisplayImage.out <Image_Path>\n");
return -1;
}
Mat1b image;
image = imread( argv[1], IMREAD_GRAYSCALE );
if ( !image.data )
{
printf("No image data \n");
return -1;
}
int rows= image.rows;
int cols= image.cols;
Mat1b taped_image(rows + 32, cols + 32);
Mat1b pyr_image = taped_image(Rect{16, 16, cols, rows});//<----HERE!!
// int t=image.type();
// Mat taped_image(rows + 32, cols + 32,t);
// Mat pyr_image = taped_image(Rect{16, 16, cols, rows});
cv::copyMakeBorder(image, taped_image, 16, 16, 16, 16, BORDER_REFLECT_101 + BORDER_ISOLATED);
// namedWindow("Display Image", WINDOW_AUTOSIZE );
// imshow("Display Image", image);
imshow("Tapped image",taped_image);
imshow("Display Image", pyr_image);
waitKey(0);
return 0;
}
When I run it, I got the taped image in a window "Tapped image" (as expected) and a black and white noise image in "Display Image. (When I used Mat instead of Mat1b the image was all black)
However I have the exact same thing in a much complex program and in that program apparently pyr_image got the original image.(as if it were a pointer)
Also in the documentation Mat definition 16 it says
Array that (as a whole or partly) is assigned to the constructed
matrix. No data is copied by these constructors. Instead, the header
pointing to m data or its sub-array is constructed and associated with
it. The reference counter, if any, is incremented. So, when you modify
the matrix formed using such a constructor, you also modify the
corresponding elements of m . If you want to have an independent copy
of the sub-array, use Mat::clone() .
So it seems that taped_image and pyr_image are associated. My questions are
Why it appears as noise to me?
Are pyr_image and image associated? (therefore having the same data)?

OpenCV 3 C++ Mat fetching with pointer goes random

I'm quite new to OpenCV and I'm now using version 3.4.1 with C++ implementation. I'm still exploring, so this question is not specific to a project, but is more of a "try to understand how it works". Please consider, with the same idea in mind, that I know that I'm somehow "reinventing the will" with this code, but I wrote this example to understand "HOW IT WORKS".
The idea is:
Read an RGB image
Make it binary
Find Connected areas
Colour each area differently
As an example I'm using a 5x5 pixel RGB image saved as BMP. The image is a white box with black pixels all around it's contour.
Up to the point where I get the ConnectedComponents matrix, named Mat::Labels, it all goes fine. If I print the Matrix I see exactly what I expect:
11111
10001
10001
10001
11111
Remember that I've inverted the threshold so it is correct to get 1 on the edges...
I then create a Mat with same size of Mat::Labels but 3 channels to colour it with RGB. This is named Mat::ColoredLabels.
Next step is to instanciate a pointer that runs through the Mat::Labels and for each position in the Mat::Labels where the value is 1 fill the corresponding Mat:.ColoredLabels position with a color.
HERE THINGS GOT VERY WRONG ! The pointer does not fetch the Mat::Labels row byt row as I would expect but follows some other order.
Questions:
Am I doing something wrong or it is "obvious" that the pointer fetching follows some "umpredictable" order ?
How could I set values of a Matrix (Mat::ColoredLabels) based on the values of another matrix (Mat::Labels) ?
.
#include "opencv2\highgui.hpp"
#include "opencv2\opencv.hpp"
#include <stdio.h>
using namespace cv;
int main(int argc, char *argv[]) {
char* FilePath = "";
Mat Img;
Mat ImgGray;
Mat ImgBinary;
Mat Labels;
uchar *P;
uchar *CP;
// Image acquisition
if (argc < 2) {
printf("Missing argument");
return -1;
}
FilePath = argv[1];
Img = imread(FilePath, CV_LOAD_IMAGE_COLOR);
if (Img.empty()) {
printf("Invalid image");
return -1;
}
// Convert to Gray...I know I could convert it right away while loading....
cvtColor(Img, ImgGray, CV_RGB2GRAY);
// Threshold (inverted) to obtain black background and white blobs-> it works
threshold(ImgGray, ImgBinary, 170, 255, CV_THRESH_BINARY_INV);
// Find Connected Components and put the 1/0 result in Mat::Labels
int BlobsNum = connectedComponents(ImgBinary, Labels, 8, CV_16U);
// Just to see what comes out with a 5x5 image. I get:
// 11111
// 10001
// 10001
// 10001
// 11111
std::cout << Labels << "\n";
// Prepare to fetch the Mat(s) with pointer to be fast
int nRows = Labels.rows;
int nCols = Labels.cols * Labels.channels();
if (Labels.isContinuous()) {
nCols *= nRows;
nRows = 1;
}
// Prepare a Mat as big as LAbels but with 3 channels to color different blobs
Mat ColoredLabels(Img.rows, Img.cols, CV_8UC3, cv::Scalar(127, 127, 127));
int ColoredLabelsNumChannels = ColoredLabels.channels();
// Fetch Mat::Labels and Mat::ColoredLabes with the same for cycle...
for (int i = 0; i < nRows; i++) {
// !!! HERE SOMETHING GOES WRONG !!!!
P = Labels.ptr<uchar>(i);
CP = ColoredLabels.ptr<uchar>(i);
for (int j = 0; j < nCols; j++) {
// The coloring operation does not work
if (P[j] > 0) {
CP[j*ColoredLabelsNumChannels] = 0;
CP[j*ColoredLabelsNumChannels + 1] = 0;
CP[j*ColoredLabelsNumChannels + 2] = 255;
}
}
}
std::cout << "\n" << ColoredLabels << "\n";
namedWindow("ColoredLabels", CV_WINDOW_NORMAL);
imshow("ColoredLabels", ColoredLabels);
waitKey(0);
printf("Execution completed succesfully");
return 0;
}
You used connectedComponents function with CV_16U parameter. This means that the single element of the image will consist of 16 bits (hence '16') and you have to interpret them as unsigned integer (hence 'U'). And since ptr returns a pointer, you have to dereference it to get the value.
Therefore you should access label image elements in the following way:
unsigned short val = *Labels.ptr<unsigned short>(i) // or uint16_t
unsigned short val = Labels.at<unsigned short>.at(y, x);
Regarding your second question, it is as simple as that, but of course you have to understand which type casts result in loss of precisions or overflows and which ones not.
mat0.at<int>(y, x) = mat1.at<int>(y, x); // both matrices have CV_32S types
mat2.at<int>(y, x) = mat3.at<char>(y,x); // CV_32S and CV_8S
// Implicit cast occurs. Possible information loss: assigning 32-bit integer values to 8-bit ints
// mat4.at<unsigned char>(y, x) = mat5.at<unsigned int>(y, x); // CV_8U and CV_32U

Output image from OpenImageIO ImageBuf to OpenCV IplImage gets corrupted

I'm writing a converter from OpenImageIO ImageBufs to OpenCV IplImages. Running the following code causes the output images to get corrupted (pictures below). I am working on a pull request for OpenImageIO, but if someone from the OpenCV community has some insights on the OpenCV side, that would be super helpful. OpenImageIO Pull Request
Code
IplImage *
ImageBufAlgo::to_IplImage (const ImageBuf &src)
{
#ifdef USE_OPENCV
const ImageSpec &spec = src.spec();
const ImageBuf *tmp = &src;
ImageBuf localcopy;
int channels = std::min(spec.nchannels, 4);
// If the image has 4+ channels, then reduce to 4 channels, and use BGRA
// order (OpenCV ordering).
if (channels >=3) {
// OpenCV images support up to 4 channels (BGRA)
int channelorder[channels];
// Set the channel order
for (int i = 0; i < channels; ++i) {
channelorder[i] = i;
}
channelorder[0] = 2; // B
channelorder[1] = 1; // G
channelorder[2] = 0; // R
if (!ImageBufAlgo::channels(localcopy, src, channels, &channelorder[0])) {
DASSERT (0 && "Could not swap channels.");
return NULL;
}
tmp = &localcopy;
}
// Create an IplImage to write to.
CvSize size = cvSize(spec.width, spec.height);
IplImage *dst;
// Get the pixel data from the ImageBuf and send it to the IplImage.
switch (spec.format.basetype) {
case TypeDesc::UINT8: {
dst = cvCreateImageHeader(size, IPL_DEPTH_8U, channels);
if (!dst) {
DASSERT (0 && "Could not create dst IplImage.");
return NULL;
}
if (tmp->storage() != ImageBuf::IMAGECACHE) {
// Type is right and the IB is not backed by an ImageCache, so
// directly read from the local data of the IB.
cvSetData(dst, (void *)tmp->localpixels(), dst->widthStep);
} else {
// Either we are backed by an ImageCache, or we somehow still
// need a data format conversion, so make a local copy with
// get_pixels.
std::vector<unsigned char> data;
data.resize (spec.width * spec.height * spec.depth * channels);
tmp->get_pixels (ROI::All(), TypeDesc::INT8, &data[0]);
cvSetData(dst, &data[0], dst->widthStep);
}
break;
default:
DASSERT (0 && "unknown TypeDesc");
return NULL;
}
return dst;
#else
return NULL;
#endif
}
Test code
#include <ostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core_c.h>
#include <opencv2/highgui/highgui_c.h>
#include <OpenImageIO/imagebuf.h>
#include <OpenImageIO/imagebufalgo.h>
namespace oiio = OpenImageIO;
int main(int argc, char const *argv[]) {
oiio::ImageBuf src("/home/scott/Projects/oiio-test/images/input.jpg");
IplImage *dst = oiio::ImageBufAlgo::to_IplImage(src);
cvSaveImage("/home/scott/Projects/oiio-test/images/output.jpg", dst);
return 0;
}
Input images
Image 1
Image 2
Output images
Image 1
Image 2
My current theories
There's a difference in how the data is laid out. As far as I understand, OpenCV expects that a 3 channel image is laid out BGRBGRBGR... I think that by default, OpenImageIO's layout is RGBRGBRGB..., but that does get swapped. I'm not adding any padding, so there's padding already in the data, or I am missing something.
I am passing the wrong step into the cvSetData (step calculation: row * type size * channels). I tried manually setting some values but I either get worse results, or OpenCV raises an exception and segfaults.
Is there something else that I could be doing wrong?

Why Png Compression doesn't change destination size (C++)? OpenCV VS2010

I have tested this code with various values from compression_params.push_back(1); to compression_params.push_back(9); but the PNG image always has same size. 1950x1080 (contains screenshot of Google map - not the satellite photo) has 2,36 MB (2 477 230 bytes. Is this normal is takes so much. I thought png images are small size if they do not contain photos.
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(1);
try {
imwrite("RGB_1.png", source, compression_params);
}
catch (runtime_error& ex) {
fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
return 1;
}
Why is it? Also I cannot find out how to create the PNG object in memory (to keep the encode data in buffer). This means, I would like to save more images into one file (e.g database) so I need to convert into buffer and then save add buffer to file. Is it possible to do it usin OpenCV? Your tips welcome.
I think PNG should support some feature where the algorithm auto-selects background color, so if you see some cv::Scallar(200,200,200) takes too many place on the image, the algorithm could set it as background color and it is removed from the image so the image should take small place. So when it takes same size as regular PNG or even more, that doesn't give any sense.
i am not an expert on this subject but tried to test some compression_params maybe you will find the answer by testing the code below.
probably adding the following lines will do the trick.
compression_params.push_back(IMWRITE_PNG_STRATEGY);
compression_params.push_back(IMWRITE_PNG_STRATEGY_DEFAULT);
or you can try other alternatives accordind to documentation
also i opened an issue
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
void createAlphaMat(Mat &mat)
{
CV_Assert(mat.channels() == 4);
for (int i = 0; i < mat.rows; ++i) {
for (int j = 0; j < mat.cols; ++j) {
Vec4b& bgra = mat.at<Vec4b>(i, j);
bgra[0] = UCHAR_MAX; // Blue
bgra[1] = saturate_cast<uchar>((float(mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX); // Green
bgra[2] = saturate_cast<uchar>((float(mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX); // Red
bgra[3] = saturate_cast<uchar>(0.5 * (bgra[1] + bgra[2])); // Alpha
}
}
}
int main( int argc, char** argv )
{
// Create mat with alpha channel
Mat mat(480, 640, CV_8UC4);
createAlphaMat(mat);
vector<int> compression_params;
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
compression_params.push_back(0);
compression_params.push_back(IMWRITE_PNG_STRATEGY);
compression_params.push_back(IMWRITE_PNG_STRATEGY_DEFAULT);
for (int i = 0; i < 10; i++)
{
compression_params[1] = i;
imwrite(format("alpha%d.png",i), mat, compression_params);
}
return 0;
}

opencv - rgb value keeps changing

I'm having some problem with the rgb or in opencv bgr
What I'm trying to do is find the overall value of the bgr of a certain pictures
but every time I run the program without changing anything with exact same pictures the values of the bgr keeps changing..
This is how i coded to find the values of bgr
#include <opencv\cv.h>
#include <opencv\highgui.h>
using namespace std;
char path[255];
int main( int argc, char** argv )
{
IplImage *red[50];
IplImage *green[50];
IplImage *blue[50];
for(int i = 1; i <= 50; i++)
{
IplImage *img;
sprintf(path, "C:\\picture (%01).bmp", i);
img = cvLoadImage(path);
red[i] = cvCreateImage(cvGetSize(img), 8, 1);
green[i] = cvCreateImage(cvGetSize(img), 8, 1);
blue[i] = cvCreateImage(cvGetSize(img), 8, 1);
cvSplit(img, blue[i], green[i], red[i], NULL);
cvReleaseImage(&img);
int total = (int)(blue[i]) + (int)(green[i]) + (int)(red[i]);
cout << total << endl;
cvWaitKey(1);
}
cvWaitKey(0);
return 0;
}
I am not sure how did you achieve type casting from IplImage to int
int total = (int)(blue[i]) + (int)(green[i]) + (int)(red[i]);
but you certainly need to use pixel by pixel summation for each channel (not image by image) to find the overall values.
Try using two loops instead of one, one for loading the images and the other for performing your operation, and let me know if it works..