I am attempting to load a WebP image file into an array, but the WebPGetInfo function does not seem to be working as advertised. The width and height variables are not being updated to the values of the width and height of the input image.
Provided is the code I have for saving out an array as a WebP image, which does work, and the code for loading a WebP image into an array, which does not work. Included are comments to show prior unsuccessful attempts, as well as next steps.
If someone could let me know either what the error is or if there is a better function to use that would be greatly appreciated.
#include "bitmap.hh"
#include <webp/decode.h>
#include <webp/encode.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <random>
#include "webp/mux_types.h"
using namespace std;
bitmap::bitmap(uint32_t w, uint32_t h, uint32_t color)
: w(w), h(h), rgb(new uint32_t[w * h]) {
for (uint32_t i = 0; i < w * h; i++) rgb[i] = color;
}
bitmap::~bitmap() { delete[] rgb; }
void bitmap::clear(uint32_t color) {
for (uint32_t i = 0; i < w * h; i++) rgb[i] = color;
}
void bitmap::save(const char filename[]) {
uint8_t* out;
size_t s = WebPEncodeRGBA((uint8_t*)rgb, w, h, 4 * w, 100, &out);
ofstream f(filename, ios::binary);
f.write((char*)out, s);
WebPFree(out);
}
void bitmap::load(const char filename[]) {
// int s = WebPGetInfo(rgb, w * h * 4, w, h, out); potentially?
cout << '\n';
cout << "load function" << '\n';
// https://www.tutorialspoint.com/how-can-i-get-a-file-s-size-in-cplusplus
ifstream in_file(filename, ios::binary);
in_file.seekg(0, ios::end);
int file_size = in_file.tellg();
cout << "Size of the file is"
<< " " << file_size << " "
<< "bytes" << '\n';
uint8_t* out = new uint8_t[file_size];
in_file.read((char*)out, file_size);
ofstream out_file("out_test.webp", ios::binary);
out_file.write((char*)out, file_size);
//
// skip the header
// https://developers.google.com/speed/webp/docs/riff_container
// header is presumed to be 12 bytes
uint8_t skip = 12;
// https://developers.google.com/speed/webp/docs/api
// attempts to get the width and height of the input image, but the cout
// shows that WebPGetInfo does not work as expected
// attempted using WebPDecodeRGBA but no luck there either
int width = 0;
int height = 0;
int s = WebPGetInfo(&out[skip], file_size, &width, &height);
// use WebPDecodeRGBA maybe?
cout << file_size << ' ' << width << ' ' << height << ' ' << s << '\n';
// prints out the elements in the out array, but most entries are blank or
// garbage, with no elements being from "RIFF" item in header as expected
cout << "printing array" << '\n';
for (int i = 0; i < 20; i++) {
cout << "i = " << i << ' ' << out[i] << '\n';
}
// The width and height had not been updating properly. However, the following steps are
// what the code should do if the functions above worked as expected:
/*
w = width;
h = height;
uint8_t pixels_split = WebPGetInfo(&out[skip], file_size, &width, &height);
uint32_t pixels_merge[file_size];
for (int i = 0; i < file_size; i+=4) {
uint8_t r = pixels_split[i];
uint8_t g = pixels_split[i+1];
uint8_t b = pixels_split[i+2];
uint8_t T = pixels_split[i+3];
pixels_merge[i] = r *(256*256*256) + g *(256*256) + b *256 + T3;
}
rgb = pixels_merge;
(do something to alter the image)
bitmap::save("test_out.webp");
*/
}
Related
i have found this Loading a tga/bmp file in C++/OpenGL, it works flawless and would like to use it in my project. however my project requires a RGB struct to modify the pixels buffer.
glDrawPixels(win_height, win_width, GL_RGB, GL_FLOAT, myBuffer);
it needs some changes to make it work in my project.
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
float b, g, r, a;
};
} *PPixelInfo;
class BMP
{
private:
std::uint32_t width, height;
std::uint16_t BitsPerPixel;
std::vector<std::uint8_t> Pixels;
public:
BMP(const char* FilePath);
std::vector<std::uint8_t> GetPixels() const {return this->Pixels;}
std::uint32_t GetWidth() const {return this->width;}
std::uint32_t GetHeight() const {return this->height;}
bool HasAlphaChannel() {return BitsPerPixel == 32;}
};
BMP::BMP(const char* FilePath)
{
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!hFile.is_open()) throw std::invalid_argument("Error: File Not Found.");
hFile.seekg(0, std::ios::end);
std::size_t Length = hFile.tellg();
hFile.seekg(0, std::ios::beg);
std::vector<std::uint8_t> FileInfo(Length);
hFile.read(reinterpret_cast<char*>(FileInfo.data()), 54);
if(FileInfo[0] != 'B' && FileInfo[1] != 'M')
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. Bitmap Required.");
}
if (FileInfo[28] != 24 && FileInfo[28] != 32)
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. 24 or 32 bit Image Required.");
}
BitsPerPixel = FileInfo[28];
width = FileInfo[18] + (FileInfo[19] << 8);
height = FileInfo[22] + (FileInfo[23] << 8);
std::uint32_t PixelsOffset = FileInfo[10] + (FileInfo[11] << 8);
std::uint32_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
Pixels.resize(size);
hFile.seekg (PixelsOffset, std::ios::beg);
hFile.read(reinterpret_cast<char*>(Pixels.data()), size);
hFile.close();
}
int main()
{
BMP info = BMP("Fixedsys16x28.bmp");
//info.
std::cout << "getwidth: " << info.GetWidth() << endl;
std::cout << "getheight: " << info.GetHeight()<< endl;
// for(auto i : info.GetPixels()){
// //float i_float = float(i);
// //float res = i_float / 15.0;
// //std::cout << std::hex << (int)i << " " << endl;
// std::cout << setprecision(2) << fixed << (float) i / 255 << " " << endl;
// }
std::cout << endl;
std::cout << "size: " << info.GetPixels().size() << endl;
std::cout << "alpha: " << info.HasAlphaChannel() << endl;
std::cout << setprecision(2) << fixed;
for(int i = 0; i < static_cast<int>(info.GetPixels().size()); i++){
for(int j = 0; j < 3; j++){
if(j == 0){
std::cout << (float) info.GetPixels()[i]/ 255 << " ";
}
else if(j == 1){
std::cout << (float) info.GetPixels()[i] / 255 << " ";
}
else{
std::cout << (float) info.GetPixels()[i] / 255;
}
}
std::cout << endl;
}
getchar();
GLuint texture = 0;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, info.HasAlphaChannel() ? GL_RGBA : GL_RGB, info.GetWidth(), info.GetWidth(), 0, info.HasAlphaChannel() ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, info.GetPixels().data());
}
how to change this line of code?
hFile.read(reinterpret_cast<char*>(Pixels.data()), size);
so that vector gets filed with ...
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
float B, G, R, A;
};
} *PPixelInfo;
collections.
after changing the setup of vector off course or use another.
what kind of of method / function, could replace Pixels.data() , so it collect these 4 bytes and assembles them accordingly in the type of struct PixelInfo?
since Pixels.data() basically provide a pointer to the pixel vector.
how does a method that can perform the requested task look like?
collect 4 bytes of data and format them accordingly and put them in a vector<PixelInfo> while converting "std::uint8_t" into float. fast and efficiently.
it should take this in consideration "info.HasAlphaChannel()".
the accompanied image data is:
512×84 and the pixel vector size is "129024".
43008×3 bytes = 129024.
i made this loop on the pixel vector:
std::cout << setprecision(2) << fixed;
for(int i = 0; i < static_cast<int>(info.GetPixels().size()); i++){
for(int j = 0; j < 3; j++){
if(j == 0){
std::cout << (float) info.GetPixels()[i]/ 255 << " ";
}
else if(j == 1){
std::cout << (float) info.GetPixels()[i] / 255 << " ";
}
else{
std::cout << (float) info.GetPixels()[i] / 255;
}
}
std::cout << endl;
}
it seams to have worked correctly and even the last line contains 3 "bytes" of data.
this routine takes, like 20 seconds for an image 400 x 400 px.
vector<PixelInfo> pixelStore (info->GetPixels().size() / 3);
int counter = 0;
for(int i = 0; i < size; i+=3){
PixelInfo temp;
temp.r = 0;
temp.g = 0;
temp.b = 0;
for(int j = 0; j < 3; j++){
if(j == 0){
temp.r = info->GetPixels()[i + j];
}
else if(j == 1){
temp.g = info->GetPixels()[i + j];
}
else{
temp.b = info->GetPixels()[i + j];
}
if(j == 2){
//pixelStore.push_back(temp);
pixelStore[counter] = temp;
counter++;
}
}
}
Your code shows std::vector<std::uint8_t> Pixels; which is a sequence of 8-bit pixels I suppose. You want a vector of PixelInfo instead? Now does that structure match the format of bytes in the file? If that's the case, then what's the problem?
If the bytes in the file match the layout of your structure (including the machine's Endian!) just do it the same way:
vector<PixelInfo> Pixels;
size_t pixels_to_read = ....??? ;
Pixels.resize(pixels_to_read);
hFile.read (reinterpret_cast<char*>(Pixels.data()), pixels_to_read*sizeof(PixelInfo));
update
collect 4 bytes of data and format them accordingly and put them in a vector while converting "std::uint8_t" into float. fast and efficiently.
std::byte buf[4];
hFile.read (reinterpret_cast<char*>(buf),4); // read 4 bytes from file
PixelInfo px;
px.R = buf[0]; // converts 8-bit integer to float
px.G = buf[1];
px.B = buf[2];
px.A = buf[3];
Note that your PixelInfo union doesn't make sense -- how does the 32-bit color match the layout of the 4 32-bit components? Furthermore, I don't think that definition is legal, as there is no such thing as an anonymous struct. If that compiles, it must be a compiler extension.
As for doing it fast :
Read in a lot at a time, rather than one pixel. A whole row is good, if it's not practical to read the entire file into memory. Then you just step along the buffer you had loaded.
You can hope that the 4 individual statements get auto-vectorized by the optimizer. If you have a tight and simple loop around just that part (to process a whole row) you might get the compiler to vectorize it.
The fact that you have to write 4 separate statements because the left hand side uses different names rather than an index should not affect the overall speed.
20 seconds for a 400×400 pixel image? I would think it would be instantaneous. I have experience loading/converting/decoding graphics files, having written a library for DOS and bare metal (16-bit x86 code) back in the early '90's. You might try benchmarking code that only does this conversion listed above, to see if that's your bottleneck or not. Look at the code generated (in the debugger, or using Compiler Explorer) to see if it's vectorizing. Look into calling the vector primitives intrinsic instructions directly, if your compiler has that available. See if you're using all the right compiler optimization flags.
essentially, I'm making a class that takes a BMP file for the constructor. From THIS POST I get all of the header data out of the way and then read RGB data into a vector. I then calculate the intensities as 0.25R + 0.5G + 0.25B. I put these numbers into a space-separated file, line by line. With the original above my result below and Using GNUPlot to open and plot the image gives me this result.
original
distortion
As you can see, the right side of the image is consistently being wrapped around further as the image is written to file (or somewhere before this process). I've pasted the code below, any help?
std::vector<char> MImage::readBMP(std::string const file){
static constexpr size_t HEADER_SIZE = 54;
std::ifstream bmp(file, std::ios::binary);
std::array<char, HEADER_SIZE> header;
bmp.read(header.data(), header.size());
auto fileSize = *reinterpret_cast<uint32_t*>(&header[2]);
auto dataOffset = *reinterpret_cast<uint32_t*>(&header[10]);
auto width = *reinterpret_cast<uint32_t*>(&header[18]);
auto height = *reinterpret_cast<uint32_t*>(&header[22]);
auto depth = *reinterpret_cast<uint16_t*>(&header[28]);
/*
std::cout << "fileSize: " << fileSize << std::endl;
std::cout << "dataOffset: " << dataOffset << std::endl;
std::cout << "width: " << width << std::endl;
std::cout << "height: " << height << std::endl;
std::cout << "depth: " << depth << "-bit" << std::endl;
*/
std::vector<char> img(dataOffset - HEADER_SIZE);
//bmp.read(img.data(), img.size());
auto dataSize = ((width * 3 + 3) & (~3)) * height;
img.resize(dataSize);
bmp.read(img.data(), img.size());
char temp = 0;
for (int i = dataSize - 4; i >= 0; i -= 3)
{
temp = img[i];
img[i] = img[i + 2];
img[i + 2] = temp;
}
// Convert to intensity
int k = 0;
int size = (int)img.size();
for (int j = 0; k+2 < size; j++)
{
//0.25B + 0.5G + 0.25R
img[j] = ((abs(img[k]) >> 2) + (abs(img[k + 1]) >> 1) + (abs(img[k + 2]) >> 2));
//OutputDebugStringA((to_string(img[j]) + "\n").c_str());
k += 3;
}
img.resize(dataSize / 3);
//OutputDebugStringA((to_string(img.size()) + "\n").c_str());
int a, b, c = 0;
//Testing #img data
ofstream TestPic;
TestPic.open("testpic.txt");
for (a = 0; a < HEIGHT; a++) {
for (b = 0; b < WIDTH; b++) {
TestPic << (int)img[c];
if (b < WIDTH-1) {
TestPic << " ";
}
c++;
}
TestPic << "\n";
}
TestPic.close();
return img; }
GNUPlot command: plot [0:630] [0:354] 'testpic.txt' matrix with image pixels
The problem you are seeing is caused by improper data alignment. Each scanline of .bmp file must have a byte-size divisible by 4. You are calculating the input data size correctly with this line:
auto dataSize = ((width * 3 + 3) & (~3)) * height;
However, while converting the img array you do not compensate/throw away the padding at the end of each scanline.
The best advice is either to use a standard bmp loader/converter like the one in STB libraries (the stb_image.h file) or, if you want to do it yourself, allocate the other array (img2 or something like that) and iterate img array scanline by scanline, writing greyscale values to img2.
By the way, this answer, which you have mentioned, contains a solution for your exact problem (it is called the "padding fix").
I am trying to create RGB image in C++. I am not using any image libraries like OpenCv.
Firstly, I tried to create grayscale image. I want to draw rectangle to image. I am giving parameters at function like starting points, width, height etc. This code lines are working good for this grayscale challenge but I am trying to increase color channels to 3 like RGB. Then, I am setting Red, Green and Blue values but it is not working. This is my problem.
How can I work correctly?
x => starting point where x coordinate
y => starting point where y coordinate
width => rectangle width
height => rectangle height
value => RGB or Grayscale color value
My codes
Image::Image(int width, int height, int n_channels, int step)
{
cout << "Image constructor is running!" << endl;
m_width = width;
m_height = height;
m_n_channels = n_channels;
m_step = m_width*m_n_channels;
if (m_step < step)
m_step = step;
m_data = new uchar[m_step*height];
}
Image* Image::new_gray(int width, int height)
{
cout << "New gray image is creating!" << endl;
return new Image(width, height, 1);
}
Image* Image::new_rgb(int width, int height)
{
cout << "New RGB image is creating!" << endl;
return new Image(width, height, 3);
}
void Image::set_rect(int x, int y, int width, int height, uchar value)
{
if (x < 0) {
width += x;
x = 0;
}
if (y < 0) {
height += y;
y = 0;
}
for (int j = y; j < y+height; ++j) {
if (j >= m_height)
break;
uchar* row_data = data(j);
for (int i = x; i < x+width; ++i) {
if (i >= m_width)
break;
for (int c = 0; c < m_n_channels; ++c)
if (c == 0) {
row_data[i*m_n_channels + c] = value;
} else if (c == 1) {
row_data[i*m_n_channels + c] = value;
} else if (c == 2) {
row_data[i*m_n_channels + c] = value;
}
}
}
}
bool Image::write_pnm(const std::string& filename) const
{
if (m_n_channels != 1) {
const string magic_head = "P6";
ofstream fout;
string extended_name = filename + ".ppm";
fout.open(extended_name.c_str(), ios::out | ios::binary);
fout << magic_head << "\n";
fout << m_width << " " << m_height << " 255\n";
for (int y = 0; y < m_height; ++y) {
const uchar *row_data = data(y);
cout << reinterpret_cast<const char*>(row_data);
fout.write(reinterpret_cast<const char*>(row_data), m_width*sizeof(uchar));
}
fout.close();
return true;
}
const string magic_head = "P5";
ofstream fout;
string extended_name = filename + ".pgm";
fout.open(extended_name.c_str(), ios::out | ios::binary);
fout << magic_head << "\n";
fout << m_width << " " << m_height << " 255\n";
for (int y = 0; y < m_height; ++y) {
const uchar *row_data = data(y);
fout.write(reinterpret_cast<const char*>(row_data), m_width*sizeof(uchar));
}
fout.close();
return true;
}
My main function
#include <cstdlib>
#include <iostream>
#include "image.h"
using std::cout;
using std::endl;
using ceng391::Image;
int main(int argc, char** argv)
{
Image* gray = Image::new_gray(128, 128);
cout << "(" << gray->w() << "x" << gray->h() << ") channels: "
<< gray->n_ch() << " step: " << gray->step() << endl;
gray->set_zero();
gray->set_rect(32, 32, 64, 64, 255);
gray->write_pnm("/tmp/test_image");
Image* rgb_image = Image::new_rgb(128,128);
cout << "(" << rgb_image->w() << "x" << rgb_image->h() << ") channels: "
<< rgb_image->n_ch() << " step: " << rgb_image->step() << endl;
rgb_image->set_zero_rgb();
rgb_image->set_rect(32, 32, 64, 64, 150);
rgb_image->write_pnm("/tmp/test_image_rgb");
delete gray;
delete rgb_image;
return EXIT_SUCCESS;
}
This code is working for grayscale images because grayscale images have same number of pixels with width.
fout.write(reinterpret_cast<const char*>(row_data), m_width*sizeof(uchar));
But when I wanted to save RGB images, increased number of pixels 3 times. One pixel respresents via 3 channels so need to multiply stream size with 3 (R,G,B channels) for one pixel.
fout.write(reinterpret_cast<const char*>(row_data), m_width*sizeof(uchar)*3);
When I compile my program I get this unhandled exception that I don't understand or know how to fix.
Exception thrown at 0x00007FF6DFF937FE in My
Code.exe: 0xC0000005: Access violation writing location
0x000002E07396F000.
If there is a handler for this exception, the program may be safely
continued.
//main.cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <vector>
#include <string>
#include "Image.h"
Image readPPM(const char* file);
void writePPM(const Image &img, const char* file);
int main()
{
std::vector<Image> inputImages;
inputImages.resize(13);
Image *outputImage;
for (int x = 1; x < 14; x++)
{
inputImages.push_back(Image(3264, 2448));
inputImages[x] = readPPM(std::string("Images/ImageStacker_set1/IMG_" + std::to_string(x) + ".ppm").c_str());
}
outputImage = new Image(3264, 2448);
outputImage->pixels = new Image::Rgb[3264, 2448];
for (int x = 0; x < 3264 * 2448; x++) {
float sumR = 0.f;
float sumG = 0.f;
float sumB = 0.f;
for (int i = 0; i < 12; i++) {
sumR += inputImages[i].pixels[x].r;
sumG += inputImages[i].pixels[x].g;
sumB += inputImages[i].pixels[x].b;
}
outputImage->pixels[x].r = sumR / 13;
outputImage->pixels[x].g = sumG / 13;
outputImage->pixels[x].b = sumB / 13;
}
writePPM(*outputImage, "testPPM.ppm");
return 0;
}
Image readPPM(const char *filename)
{
//Remove this cout to prevent multiple outputs
std::cout << "Reading image ..." << std::endl;
std::ifstream ifs;
ifs.open(filename, std::ios::binary);
Image src;
try {
if (ifs.fail()) {
throw("Can't open the input file - is it named correctly/is it in the right directory?");
}
std::string header;
int w, h, b;
ifs >> header;
if (strcmp(header.c_str(), "P6") != 0) throw("Can't read the input file - is it in binary format (Has P6 in the header)?");
ifs >> w >> h >> b;
src.w = w;
src.h = h;
//std::cout << w << " " << h << std::endl;
src.pixels = new Image::Rgb[w * h]; // this is throw an exception if bad_alloc
ifs.ignore(256, '\n'); // skip empty lines in necessary until we get to the binary data
unsigned char pix[3]; // read each pixel one by one and convert bytes to floats
for (int i = 0; i < w * h; ++i) {
ifs.read(reinterpret_cast<char *>(pix), 3);
src.pixels[i].r = pix[0] / 255.f;
src.pixels[i].g = pix[1] / 255.f;
src.pixels[i].b = pix[2] / 255.f;
}
ifs.close();
}
catch (const char *err) {
fprintf(stderr, "%s\n", err);
ifs.close();
}
//Confirm image read
//Delete this to prevent multiple lines output
std::cout << "Image read" << std::endl;
return src;
}
//Write data out to a ppm file
//Constructs the header as above
void writePPM(const Image &img, const char *filename)
{
//std::cout << filename << std::endl;
std::cout << "Writing image ..." << std::endl;
if (img.w == 0 || img.h == 0) { fprintf(stderr, "Can't save an empty image\n"); return; }
std::ofstream ofs;
try {
ofs.open(filename, std::ios::binary); // need to spec. binary mode for Windows users
if (ofs.fail()) throw("Can't open output file");
ofs << "P6\n" << img.w << " " << img.h << "\n255\n";
//std::cout << "P6\n" << img.w << " " << img.h << "\n255\n";
unsigned char r, g, b;
// loop over each pixel in the image, clamp and convert to byte format
for (int i = 0; i < img.w * img.h; ++i) {
r = static_cast<unsigned char>(std::min(1.f, img.pixels[i].r) * 255);
g = static_cast<unsigned char>(std::min(1.f, img.pixels[i].g) * 255);
b = static_cast<unsigned char>(std::min(1.f, img.pixels[i].b) * 255);
ofs << r << g << b;
}
ofs.close();
//Confirm image write
std::cout << "Image written" << std::endl;
}
catch (const char *err) {
fprintf(stderr, "%s\n", err);
ofs.close();
}
}
Image.h
#pragma once
//*********************************************
//Image class to hold and allow manipulation of images once read into the
code
//from https://www.scratchapixel.com/
//*********************************************
#include <cstdlib>
#include <cstdio>
class Image
{
public:
// Rgb structure, i.e. a pixel
struct Rgb
{
Rgb() : r(0), g(0), b(0) {}
Rgb(float c) : r(c), g(c), b(c) {}
Rgb(float _r, float _g, float _b) : r(_r), g(_g), b(_b) {}
bool operator != (const Rgb &c) const
{
return c.r != r || c.g != g || c.b != b;
}
Rgb& operator *= (const Rgb &rgb)
{
r *= rgb.r, g *= rgb.g, b *= rgb.b; return *this;
}
Rgb& operator += (const Rgb &rgb)
{
r += rgb.r, g += rgb.g, b += rgb.b; return *this;
}
friend float& operator += (float &f, const Rgb rgb)
{
f += (rgb.r + rgb.g + rgb.b) / 3.f; return f;
}
float r, g, b;
};
Image() : w(0), h(0), pixels(nullptr) { /* empty image */ }
Image(const unsigned int &_w, const unsigned int &_h, const Rgb &c =
kBlack) :
w(_w), h(_h), pixels(NULL)
{
pixels = new Rgb[w * h];
for (int i = 0; i < w * h; ++i)
pixels[i] = c;
}
//copy constructor
Image(const Image &im)
{
w = im.w;
h = im.h;
pixels = new Rgb[im.w * im.h];
for (int i = 0; i < im.w * im.h; ++i)
pixels[i] = im.pixels[i];
}
//copy assignment operator
Image& operator=(const Image& other)
{
w = other.w;
h = other.h;
pixels = new Rgb[other.w * other.h];
for (int i = 0; i < other.w * other.h; ++i)
pixels[i] = other.pixels[i];
return *this;
}
const Rgb& operator [] (const unsigned int &i) const
{
return pixels[i];
}
Rgb& operator [] (const unsigned int &i)
{
return pixels[i];
}
~Image()
{
if (pixels != NULL) delete[] pixels;
//delete[] pixels;
}
//unsigned int w, h; // Image resolution
int w, h; // Image resolution
Rgb *pixels; // 1D array of pixels
static const Rgb kBlack, kWhite, kRed, kGreen, kBlue; // Preset colors
};
const Image::Rgb Image::kBlack = Image::Rgb(0);
const Image::Rgb Image::kWhite = Image::Rgb(1);
const Image::Rgb Image::kRed = Image::Rgb(1, 0, 0);
const Image::Rgb Image::kGreen = Image::Rgb(0, 1, 0);
const Image::Rgb Image::kBlue = Image::Rgb(0, 0, 1);
Here's the code that appears to be causing the problem. When debugging it seems to be that there's something wrong with the pointers here:
outputImage->pixels[x].r = sumR / 13;
outputImage->pixels[x].g = sumG / 13;
outputImage->pixels[x].b = sumB / 13;
Sorry I can't really provide anything else to go on but I have never had an error like this before so I don't know where to start in fixing it.
There's some level of bad here. But I believe at the core you meant to write:
inputImages[x] = Image(3264, 2448);
inputImages[x].pixels = readPPM(std::string("Images/ImageStacker_set1/IMG_" + std::to_string(x) + ".ppm").c_str());
Stepping through your code:
std::vector<Image> inputImages - Leaves inputImages with a size of 0
inputImages.resize(13); - Leaves inputImages with a size of 13 default constructed Images
inputImages.push_back(Image(3264, 2448)); - Increments the size of inputImages by 1 by adding a default image onto the tail end, ultimately increasing the size of inputImages to 26
inputImages[x] = readPPM(std::string("Images/ImageStacker_set1/IMG_" + std::to_string(x) + ".ppm").c_str()); - Modifies one of the default constructed Images in inputImages' [1 - 12] then finally modifies the first Image(3264, 2448) in inputImages
This means that at a minimum you have 13 Image(3264, 2448) objects that are never accessed. The 1st element that is accessed by the code in question, inputImage[0] is a default constructed Image. And unless readPPM returns an Image with an allocated pixels field, there are no Images in inputImages [1 - 12] range which can support access to their pixels field.
I would like to weigh values of luminance on a new image.
I have an image (5px.jpg) of 5 pixels with these luminance :50,100,150,200,250.
I have a vector of coefficient.
I created a new Mat Z which combine luminance of 5px.jpg and the coefficient.
So, my first value of luminance is 50 (lum[0]=50) and I want it to be applied on the 5.1 (coef[0]=5.1) first pixel of my matrix. To do that, I need to weight the 6th pixel with the first and the second value of luminance. In my case,the luminance of my 6th pixel will be 95 because (0.1*50)+(0.9*100)=95
And so on...
But I do not know why my code does not works.
I had already asked a similar question for a vector here and now, I'm try to adapt to an image.
My picture in input :
My output :
#define MPI 3.14159265358979323846264338327950288419716939937510
#define RAD2DEG (180./MPI)
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/opencv.hpp>
#include <iostream>
#include <cmath>
#include <math.h>
#include <string.h>
using namespace cv;
using namespace std;
int main()
{
Mat image = imread("5px.jpg", 1);
if (image.empty())
{
cout << "Couldn't load " << image << endl;
}
else
{
cout << "Image upload, go" << endl;
}
namedWindow("ImageIn", CV_WINDOW_AUTOSIZE);
imshow("ImageIn", image);
Mat imgGrayScale;
cvtColor(image, imgGrayScale, CV_BGR2GRAY);
float *deltaP = new float[imgGrayScale.cols];
float *angle = new float[imgGrayScale.cols];
float *coeff = new float[imgGrayScale.cols];
int col;
for (col = 0; col < imgGrayScale.cols; ++col)
{
//cout << "position x = " << col << endl;
deltaP[col] = imgGrayScale.at<uchar>(0, col);
//cout << "luminance = " << deltaP[col] << endl;
angle[col] = acos(deltaP[col] / 255);
//cout << "angle =" << angle[col] << endl;
coeff[col] = (1 / cos(angle[col]));
cout << "coeff = " << coeff[col] << endl;
}
int width = imgGrayScale.size().width;
int height = imgGrayScale.size().height;
int width2 = width * 5;
int idx_coef = 0;
Mat Z = Mat::zeros(height, width2, CV_8UC1);
//for (int r = 0; r < imgGrayScale.rows; r++)
//{
//cout << "Saut de ligne " << endl << endl << endl;
for (int t = 0; t < imgGrayScale.cols; t++)
{
//cout << "Saut de colonne " << endl;
// Attribue le coeff à une variable
int c = int(coeff[idx_coef]);
//cout << "x" << t << endl;
for (int i = 0; i < c; ++i)
{
Z.at<uchar>(0, c) = imgGrayScale.at<uchar>(0, t);
}
float alpha = fmod(coeff[idx_coef], 1.f);
float beta = 1.f - alpha;
Z.at<uchar>(0, c + 1) = (alpha * imgGrayScale.at<uchar>(0, t) + beta * imgGrayScale.at<uchar>(0, t + 1));
idx_coef++;
coeff[idx_coef] = coeff[idx_coef] - beta;
if (idx_coef >= width - 1)
{
int cc = int(coeff[idx_coef]);
for (int i = 0; i < cc; ++i)
{
Z.at<uchar>(0, c) = imgGrayScale.at<uchar>(0, t);
}
idx_coef = 0;
break;
}
}
//}
namedWindow("m", CV_WINDOW_AUTOSIZE);
imshow("m", Z);
imwrite("lumianacetest.jpg", Z);
int t = waitKey();
if ((char)t == 27)
return 0;
}
You messed up with the indices while accessing the matrix Z. You shoudn't access Z at column c, but you need access the current column (as a vector::push_back would do). So you can keep the current index column in a variable, here idx_z, and increment it every time you access Z
Here your Z is CV_8U, so you lose accuracy since your values are float. You can create Z as CV_32F, and if you need to store values in CV_8U format to save the image, you can convert to CV_8U later, eventually.
The last columns of Z won't be set to any value (so I initialized them with value 0). If you need them to have the last value as in the imgGrayScale, just decomment the relevant part of the code.
Here the code:
#define MPI 3.14159265358979323846264338327950288419716939937510
#define RAD2DEG (180./MPI)
#include <opencv2\opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
int main()
{
Mat1b imgGrayScale = (Mat1b(2, 5) << 50, 100, 150, 200, 250,
50, 100, 150, 200, 250);
vector<float> deltaP(imgGrayScale.cols);
vector<float> angle(imgGrayScale.cols);
vector<float> coeff(imgGrayScale.cols);
int col;
for (col = 0; col < imgGrayScale.cols; ++col)
{
//cout << "position x = " << col << endl;
deltaP[col] = imgGrayScale.at<uchar>(0, col);
//cout << "luminance = " << deltaP[col] << endl;
angle[col] = acos(deltaP[col] / 255);
//cout << "angle =" << angle[col] << endl;
coeff[col] = (1 / cos(angle[col]));
cout << "coeff = " << coeff[col] << endl;
}
int width = imgGrayScale.size().width;
int height = imgGrayScale.size().height;
int width2 = width * 5;
Mat1f Z(height, width2, 0.f);
for (int r = 0; r < imgGrayScale.rows; r++)
{
int idx_lum = 0;
int idx_coef = 0;
int idx_z = 0;
vector<float> coef = coeff;
// Set all values in Z to the last value in imgGrayScale
Z.row(r) = imgGrayScale(r, imgGrayScale.cols-1);
while (true)
{
int c = int(coef[idx_coef]);
for (int i = 0; i < c; ++i)
{
Z(r, idx_z++) = imgGrayScale(r, idx_lum);
}
float alpha = fmod(coef[idx_coef], 1.f);
float beta = 1.f - alpha;
Z(r, idx_z++) = (alpha * imgGrayScale(r, idx_lum) + beta * imgGrayScale(r, idx_lum + 1));
idx_coef++;
idx_lum++;
coef[idx_coef] = coef[idx_coef] - beta;
if (idx_lum >= imgGrayScale.cols - 1 || idx_coef >= coef.size() - 1)
{
int cc = int(coef[idx_coef]);
for (int i = 0; i < cc; ++i)
{
Z(r, idx_z++) = imgGrayScale(r, idx_lum);
}
idx_coef = 0;
break;
}
}
}
Mat1b ZZ;
Z.convertTo(ZZ, CV_8U);
cout << "Float values:" << endl;
cout << Z << endl << endl;
cout << "Uchar values:" << endl;
cout << ZZ << endl << endl;
namedWindow("m", CV_WINDOW_AUTOSIZE);
imshow("m", Z);
imwrite("lumianacetest.png", ZZ);
waitKey();
return 0;
}