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.
Related
Making Mandelbrot with MPI
So I've made a Mandelbrot generator and everything worked fine. Now I'm throwing in a speedup from MPI. Process 0 generates a file name mbrot.ppm and adds the appropriate metadata, then divides up the workload into chunks.
Each process receives the chunk's starting and ending positions and gets to work calculating its portion of the Mandelbrot set. To write to the mbrot.ppm file, each process saves its data in an array so it doesn't write to the file before the previous process finishes.
My Problem
Its a runtime error that says:
Primary job terminated normally, but 1 process returned
a non-zero exit code. Per user-direction, the job has been aborted.
--------------------------------------------------------------------------
--------------------------------------------------------------------------
mpirun noticed that process rank 0 with PID 0 on node Lenovo exited on signal 11 (Segmentation fault).
I believe it comes from the line int data[3][xrange][yrange]; (line 120) since the print statement after this line never executes. Would there be an obvious reason I'm missing why this multi-dimensional array is causing me problems?
Full Code
#include <iostream>
#include <mpi.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#include <fstream>
#define MCW MPI_COMM_WORLD
using namespace std;
struct Complex {
double r;
double i;
};
Complex operator + (Complex s, Complex t) {
Complex v;
v.r = s.r + t.r;
v.i = s.i + t.i;
return v;
};
Complex operator * (Complex s, Complex t) {
Complex v;
v.r = s.r * t.r - s.i * t.i;
v.i = s.r * t.i + s.i * t.r;
return v;
};
int rcolor(int iters) {
if (iters == 255) return 0;
return 32 * (iters % 8);
};
int gcolor(int iters) {
if (iters == 255) return 0;
return 32 * (iters % 8);
};
int bcolor(int iters) {
if (iters == 255) return 0;
return 32 * (iters % 8);
};
int mbrot(Complex c, int maxIters) {
int i = 0;
Complex z;
z = c;
while (i < maxIters && z.r * z.r + z.i * z.i < 4) {
z = z * z + c;
i++;
}
return i;
};
int main(int argc, char * argv[]) {
int rank, size;
MPI_Init( & argc, & argv);
MPI_Comm_rank(MCW, & rank);
MPI_Comm_size(MCW, & size);
if (size < 2) {
printf("Not an MPI process if only 1 process runs.\n");
exit(1);
}
if (size % 2 != 0) {
printf("Please use a even number\n");
exit(1);
}
Complex c1, c2, c;
char path[] = "brot.ppm";
int DIM;
int chunk[4];
c1.r = -1;
c1.i = -1;
c2.r = 1;
c2.i = 1;
if (rank == 0) { //start the file
ofstream fout;
fout.open(path);
DIM = 2000; // pixel dimensions
fout << "P3" << endl; // The file type .ppm
fout << DIM << " " << DIM << endl; // dimensions of the image
fout << "255" << endl; // color depth
fout.close();
// making dimesions marks
for (int i = 0; i < size; i++) {
chunk[0] = 0; // startX
chunk[1] = DIM; // endX
chunk[2] = (DIM / size) * i; // startY
chunk[3] = (DIM / size) * (i + 1); // endY
MPI_Send(chunk, 4, MPI_INT, i, 0, MCW);
};
};
MPI_Recv(chunk, 4, MPI_INT, 0, 0, MCW, MPI_STATUS_IGNORE);
printf("Process %d recieved chunk\n\t StartX: %d, EndX: %d\n\t StartY: %d, EndY: %d\n", rank, chunk[0], chunk[1], chunk[2], chunk[3]);
// do stuff save in array
// data[3 elements][Xs][Ys]
int xrange = chunk[1] - chunk[0];
int yrange = chunk[3] - chunk[2];
printf("Process %d, x: %d, y: %d\n", rank, xrange, yrange);
int data[3][xrange][yrange];
printf("done\n");
// generate data for mandlebrot
for (int j = chunk[2]; j < chunk[3]; ++j) {
for (int i = chunk[0]; i < chunk[1]; ++i) {
// calculate one pixel of the DIM x DIM image
c.r = (i * (c1.r - c2.r) / DIM) + c2.r;
c.i = (j * (c1.i - c2.i) / DIM) + c2.i;
int iters = mbrot(c, 255);
data[0][i][j] = rcolor(iters);
data[1][i][j] = gcolor(iters);
data[2][i][j] = bcolor(iters);
}
}
printf("here2\n");
// taking turns to write their data to file
for (int k = 0; k < size; k++) {
if (rank == k) {
ofstream fout;
fout.open(path, ios::app);
fout << rank << " was here" << endl;
for (int j = chunk[2]; j < chunk[3]; ++j) {
for (int i = chunk[0]; i < chunk[1]; ++i) {
fout << data[0][i][j] << " " << data[1][i][j] << " " << data[2][i][j] << " ";
}
fout << endl;
}
printf("Process %d done and waiting\n", rank);
} else {
MPI_Barrier(MCW);
}
}
MPI_Finalize();
};
How to Run
$ mpic++ -o mbrot.out mbrot.cpp
$ mpirun -np 4 mbrot.out
Is there a way to scale down with highest quality a font which is png image in opengl at startup? I tried gluScaleImage but there are many artefacts. Is there anything that uses lanczos or something like that? I don't want to write a shader or anything that does the scaling runtime.
This is based on an algorithm, I copied decades ago from the German c't Magazin, and still use it from time to time for similar issues like described by OP.
bool scaleDown(
const Image &imgSrc,
Image &imgDst,
int w, int h,
int align)
{
const int wSrc = imgSrc.w(), hSrc = imgSrc.h();
assert(w > 0 && w <= wSrc && h > 0 && h <= hSrc);
// compute scaling factors
const double sx = (double)wSrc / (double)w;
const double sy = (double)hSrc / (double)h;
const double sxy = sx * sy;
// prepare destination image
imgDst.resize(w, h, (w * 3 + align - 1) / align * align);
// cache some data
const uint8 *const dataSrc = imgSrc.data();
const int bPRSrc = imgSrc.bPR();
// perform scaling
for (int y = 0; y < h; ++y) {
const double yStart = sy * y;
const double yEnd = std::min(sy * (y + 1), (double)hSrc);
const int yStartInt = (int)yStart;
const int yEndInt = (int)yEnd - (yEndInt == yEnd);
const double tFrm = 1 + yStartInt - yStart, bFrm = yEnd - yEndInt;
for (int x = 0; x < w; ++x) {
const double xStart = sx * x;
const double xEnd = std::min(sx * (x + 1), (double)wSrc);
const int xStartInt = (int)xStart;
const int xEndInt = (int)xEnd - (xEndInt == xEnd);
double lFrm = 1 + xStartInt - xStart, rFrm = xEnd - xEndInt;
double pixel[3] = { 0.0, 0.0, 0.0 }; // values of target pixel
for (int i = yStartInt; i <= yEndInt; ++i) {
int jData = i * bPRSrc + xStartInt * 3;
for (int j = xStartInt; j <= xEndInt; ++j) {
double pixelAdd[3];
for (int k = 0; k < 3; ++k) {
pixelAdd[k] = (double)dataSrc[jData++] / sxy;
}
if (j == xStartInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= lFrm;
} else if (j == xEndInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= rFrm;
}
if (i == yStartInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= tFrm;
} else if (i == yEndInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= bFrm;
}
for (int k = 0; k < 3; ++k) pixel[k] += pixelAdd[k];
}
}
imgDst.setPixel(x, y,
(uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]);
}
}
// done
return true;
}
If I got it right, this implements a bilinear interpolation.
I don't dare to call it a Minimal Complete Verifiable Example although this is what I intended to do.
The complete sample application:
A simplified class Image
image.h:
#ifndef IMAGE_H
#define IMAGE_H
#include <vector>
// convenience type for bytes
typedef unsigned char uint8;
// image helper class
class Image {
private: // variables:
int _w, _h; // image size
size_t _bPR; // bytes per row
std::vector<uint8> _data; // image data
public: // methods:
// constructor.
Image(): _w(0), _h(0), _bPR(0) { }
// destructor.
~Image() = default;
// copy constructor.
Image(const Image&) = delete; // = default; would work as well.
// copy assignment.
Image& operator=(const Image&) = delete; // = default; would work as well.
// returns width of image.
int w() const { return _w; }
// returns height of image.
int h() const { return _h; }
// returns bytes per row.
size_t bPR() const { return _bPR; }
// returns pointer to image data.
const uint8* data(
int y = 0) // row number
const {
return &_data[y * _bPR];
}
// returns data size (in bytes).
size_t size() const { return _data.size(); }
// clears image.
void clear();
// resizes image.
uint8* resize( // returns allocated buffer
int w, // image width
int h, // image height
int bPR); // bytes per row
// returns pixel.
int getPixel(
int x, // column
int y) // row
const;
// sets pixel.
void setPixel(
int x, // column
int y, // row
uint8 r, uint8 g, uint8 b);
// sets pixel.
void setPixel(
int x, // column
int y, // row
int value) // RGB value
{
setPixel(x, y, value & 0xff, value >> 8 & 0xff, value >> 16 & 0xff);
}
};
// helper functions:
inline uint8 getR(int value) { return value & 0xff; }
inline uint8 getG(int value) { return value >> 8 & 0xff; }
inline uint8 getB(int value) { return value >> 16 & 0xff; }
#endif // IMAGE_H
image.cc:
#include <cassert>
#include "image.h"
// clears image.
void Image::clear()
{
_data.clear(); _w = _h = _bPR = 0;
}
// allocates image data.
uint8* Image::resize( // returns allocated buffer
int w, // image width
int h, // image height
int bPR) // bits per row
{
assert(w >= 0 && 3 * w <= bPR);
assert(h >= 0);
_w = w; _h = h; _bPR = bPR;
const size_t size = h * bPR;
_data.resize(size);
return _data.data();
}
// returns pixel.
int Image::getPixel(
int x, // column
int y) // row
const {
assert(x >= 0 && x < _w);
assert(y >= 0 && y < _h);
const size_t offs = y * _bPR + 3 * x;
return _data[offs + 0]
| _data[offs + 1] << 8
| _data[offs + 2] << 16;
}
// sets pixel.
void Image::setPixel(
int x, // column
int y, // row
uint8 r, uint8 g, uint8 b) // R, G, B values
{
assert(x >= 0 && x < _w);
assert(y >= 0 && y < _h);
const size_t offs = y * _bPR + 3 * x;
_data[offs + 0] = r;
_data[offs + 1] = g;
_data[offs + 2] = b;
}
Image Scaling
imageScale.h:
#ifndef IMAGE_SCALE_H
#define IMAGE_SCALE_H
#include "image.h"
/* scales an image to a certain width and height.
*
* Note:
* imgSrc and imgDst may not be identical.
*/
bool scaleTo( // returns true if successful
const Image &imgSrc, // source image
Image &imgDst, // destination image
int w, int h, // destination width and height
int align = 4); // row alignment
/* scales an image about a certain horizontal/vertical scaling factor.
*
* Note:
* imgSrc and imgDst may not be identical.
*/
inline bool scaleXY( // returns true if successful
const Image &imgSrc, // source image
Image &imgDst, // destination image
double sX, // horizontal scaling factor (must be > 0 but not too large)
double sY, // vertical scaling factor (must be > 0 but not too large)
int align = 4) // row alignment
{
return sX > 0.0 && sY > 0.0
? scaleTo(imgSrc, imgDst,
(int)(sX * imgSrc.w()), (int)(sY * imgSrc.h()), align)
: false;
}
/* scales an image about a certain scaling factor.
*
* Note:
* imgSrc and imgDst may not be identical.
*/
inline bool scale( // returns true if successful
const Image &imgSrc, // source image
Image &imgDst, // destination image
double s, // scaling factor (must be > 0 but not too large)
int align = 4) // row alignment
{
return scaleXY(imgSrc, imgDst, s, s, align);
}
#endif // IMAGE_SCALE_H
imageScale.cc:
#include <cassert>
#include <algorithm>
#include "imageScale.h"
namespace {
template <typename VALUE>
VALUE clip(VALUE value, VALUE min, VALUE max)
{
return value < min ? min : value > max ? max : value;
}
bool scaleDown(
const Image &imgSrc,
Image &imgDst,
int w, int h,
int align)
{
const int wSrc = imgSrc.w(), hSrc = imgSrc.h();
assert(w > 0 && w <= wSrc && h > 0 && h <= hSrc);
// compute scaling factors
const double sx = (double)wSrc / (double)w;
const double sy = (double)hSrc / (double)h;
const double sxy = sx * sy;
// prepare destination image
imgDst.resize(w, h, (w * 3 + align - 1) / align * align);
// cache some data
const uint8 *const dataSrc = imgSrc.data();
const int bPRSrc = imgSrc.bPR();
// perform scaling
for (int y = 0; y < h; ++y) {
const double yStart = sy * y;
const double yEnd = std::min(sy * (y + 1), (double)hSrc);
const int yStartInt = (int)yStart;
const int yEndInt = (int)yEnd - (yEndInt == yEnd);
const double tFrm = 1 + yStartInt - yStart, bFrm = yEnd - yEndInt;
for (int x = 0; x < w; ++x) {
const double xStart = sx * x;
const double xEnd = std::min(sx * (x + 1), (double)wSrc);
const int xStartInt = (int)xStart;
const int xEndInt = (int)xEnd - (xEndInt == xEnd);
double lFrm = 1 + xStartInt - xStart, rFrm = xEnd - xEndInt;
double pixel[3] = { 0.0, 0.0, 0.0 }; // values of target pixel
for (int i = yStartInt; i <= yEndInt; ++i) {
int jData = i * bPRSrc + xStartInt * 3;
for (int j = xStartInt; j <= xEndInt; ++j) {
double pixelAdd[3];
for (int k = 0; k < 3; ++k) {
pixelAdd[k] = (double)dataSrc[jData++] / sxy;
}
if (j == xStartInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= lFrm;
} else if (j == xEndInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= rFrm;
}
if (i == yStartInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= tFrm;
} else if (i == yEndInt) {
for (int k = 0; k < 3; ++k) pixelAdd[k] *= bFrm;
}
for (int k = 0; k < 3; ++k) pixel[k] += pixelAdd[k];
}
}
imgDst.setPixel(x, y,
(uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]);
}
}
// done
return true;
}
bool scaleUp(
const Image &imgSrc,
Image &imgDst,
int w, int h,
int align)
{
const int wSrc = imgSrc.w(), hSrc = imgSrc.h();
assert(w && w >= wSrc && h && h >= hSrc);
// compute scaling factors
const double sx = (double)wSrc / (double)w;
const double sy = (double)hSrc / (double)h;
// prepare destination image
imgDst.resize(w, h, (w * 3 + align - 1) / align * align);
// cache some data
const uint8 *const dataSrc = imgSrc.data();
const int bPRSrc = imgSrc.bPR();
// perform scaling
for (int y = 0; y < h; ++y) {
const double yStart = sy * y;
const double yEnd = std::min(sy * (y + 1), (double)hSrc - 1);
const int yStartInt = (int)yStart;
const int yEndInt = (int)yEnd;
if (yStartInt < yEndInt) {
const double bFract = clip((double)((yEnd - yEndInt) / sy), 0.0, 1.0);
const double tFract = 1.0 - bFract;
for (int x = 0; x < w; ++x) {
const double xStart = sx * x;
const double xEnd = std::min(sx * (x + 1), (double)wSrc - 1);
const int xStartInt = (int)xStart, xEndInt = (int)xEnd;
double pixel[4];
if (xStartInt < xEndInt) {
const double rFract
= clip((double)((xEnd - xEndInt) / sx), 0.0, 1.0);
const double lFract = 1.0 - rFract;
int jData = yStartInt * bPRSrc + xStartInt * 3;
for (int k = 0; k < 3; ++k) {
pixel[k] = tFract * lFract * dataSrc[jData++];
}
for (int k = 0; k < 3; ++k) {
pixel[k] += tFract * rFract * dataSrc[jData++];
}
jData = yEndInt * bPRSrc + xStartInt * 3;
for (int k = 0; k < 3; ++k) {
pixel[k] += bFract * lFract *dataSrc[jData++];
}
for (int k = 0; k < 3; ++k) {
pixel[k] += bFract * rFract *dataSrc[jData++];
}
} else {
int jData = yStartInt * bPRSrc + xStartInt * 3;
for (int k = 0; k < 3; ++k) {
pixel[k] = tFract * dataSrc[jData++];
}
jData = yEndInt * bPRSrc + xStartInt * 3;
for (int k = 0; k < 3; ++k) {
pixel[k] += bFract * dataSrc[jData++];
}
}
imgDst.setPixel(x, y,
(uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]);
}
} else {
for (int x = 0; x < w; ++x) {
const double xStart = sx * x;
const double xEnd = std::min(sx * (x + 1), (double)wSrc - 1);
const int xStartInt = (int)xStart, xEndInt = (int)xEnd;
double pixel[3];
if (xStartInt < xEndInt) {
const double rFract
= clip((double)((xEnd - xEndInt) / sx), 0.0, 1.0);
const double lFract = 1.0 - rFract;
int jData = yStartInt * bPRSrc + xStartInt * 3;
for (int k = 0; k < 3; ++k) {
pixel[k] = lFract * dataSrc[jData++];
}
for (int k = 0; k < 3; ++k) {
pixel[k] += rFract * dataSrc[jData++];
}
} else {
int jData = yStartInt * bPRSrc + xStartInt * 3;
for (int k = 0; k < 3; ++k) pixel[k] = dataSrc[jData++];
}
imgDst.setPixel(x, y,
(uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]);
}
}
}
// done
return true;
}
} // namespace
bool scaleTo(const Image &imgSrc, Image &imgDst, int w, int h, int align)
{
Image imgTmp;
return w <= 0 || h <= 0 ? false
: w >= imgSrc.w() && h >= imgSrc.h()
? scaleUp(imgSrc, imgDst, w, h, align)
: w <= imgSrc.w() && h <= imgSrc.h()
? scaleDown(imgSrc, imgDst, w, h, align)
: w >= imgSrc.w()
? scaleUp(imgSrc, imgTmp, w, imgSrc.h(), 1)
&& scaleDown(imgTmp, imgDst, w, h, align)
: scaleDown(imgSrc, imgTmp, w, imgSrc.h(), 1)
&& scaleUp(imgTmp, imgDst, w, h, align);
}
PPM file IO
imagePPM.h:
#ifndef IMAGE_PPM_H
#define IMAGE_PPM_H
#include <iostream>
#include "image.h"
// reads a binary PPM file.
bool readPPM( // returns true if successful
std::istream &in, // input stream (must be opened with std::ios::binary)
Image &img, // image to read into
int align = 4); // row alignment
// writes binary PPM file.
bool writePPM( // returns true if successful
std::ostream &out, // output stream (must be opened with std::ios::binary)
const Image &img); // image to write from
#endif // IMAGE_PPM_H
imagePPM.cc:
#include <sstream>
#include <string>
#include "imagePPM.h"
// reads a binary PPM file.
bool readPPM( // returns true if successful
std::istream &in, // input stream (must be opened with std::ios::binary)
Image &img, // image to read into
int align) // row alignment
{
// parse header
std::string buffer;
if (!getline(in, buffer)) return false;
if (buffer != "P6") {
std::cerr << "Wrong header! 'P6' expected.\n";
return false;
}
int w = 0, h = 0, t = 0;
for (int i = 0; i < 3;) {
if (!getline(in, buffer)) return false;
if (buffer.empty()) continue; // skip empty lines
if (buffer[0] == '#') continue; // skip comments
std::istringstream str(buffer);
switch (i) {
case 0:
if (!(str >> w)) continue;
++i;
case 1:
if (!(str >> h)) continue;
++i;
case 2:
if (!(str >> t)) continue;
++i;
}
}
if (t != 255) {
std::cerr << "Unsupported format! t = 255 expected.\n";
return false;
}
// allocate image buffer
uint8 *data = img.resize(w, h, (w * 3 + align - 1) / align * align);
// read data
for (int i = 0; i < h; ++i) {
if (!in.read((char*)data, 3 * img.w())) return false;
data += img.bPR();
}
// done
return true;
}
// writes binary PPM file.
bool writePPM( // returns true if successful
std::ostream &out, // output stream (must be opened with std::ios::binary)
const Image &img) // image to write from
{
// write header
if (!(out << "P6\n" << img.w() << ' ' << img.h() << " 255\n")) return false;
// write image data
for (size_t y = 0; y < img.h(); ++y) {
const uint8 *const data = img.data(y);
if (!out.write((const char*)data, 3 * img.w())) return false;
}
// done
return true;
}
The main application
scaleRGBImg.cc:
#include <iostream>
#include <fstream>
#include <string>
#include "image.h"
#include "imagePPM.h"
#include "imageScale.h"
int main(int argc, char **argv)
{
// read command line arguments
if (argc <= 3) {
std::cerr << "Missing arguments!\n";
std::cout
<< "Usage:\n"
<< " scaleRGBImg IN_FILE SCALE OUT_FILE\n";
return 1;
}
const std::string inFile = argv[1];
char *end;
const double s = std::strtod(argv[2], &end);
if (end == argv[2] || *end != '\0') {
std::cerr << "Invalid scale factor '" << argv[2] << "'!\n";
return 1;
}
if (s <= 0.0) {
std::cerr << "Invalid scale factor " << s << "!\n";
return 1;
}
const std::string outFile = argv[3];
// read image
Image imgSrc;
{ std::ifstream fIn(inFile.c_str(), std::ios::binary);
if (!readPPM(fIn, imgSrc)) {
std::cerr << "Reading '" << inFile << "' failed!\n";
return 1;
}
}
// scale image
Image imgDst;
if (!scale(imgSrc, imgDst, s)) {
std::cerr << "Scaling failed!\n";
return 1;
}
// write image
{ std::ofstream fOut(outFile.c_str(), std::ios::binary);
if (!writePPM(fOut, imgDst) || (fOut.close(), !fOut.good())) {
std::cerr << "Writing '" << outFile << "' failed!\n";
return 1;
}
}
// done
return 0;
}
Test
Compiled in cygwin64:
$ g++ -std=c++11 -o scaleRGBImg scaleRGBImg.cc image.cc imagePPM.cc imageScale.cc
$
A sample image test.ppm for a test – converted to PPM in GIMP:
Test with the sample image:
$ for I in 0.8 0.6 0.4 0.2 ; do echo ./scaleRGBImg test.ppm $I test.$I.ppm ; done
./scaleRGBImg test.ppm 0.8 test.0.8.ppm
./scaleRGBImg test.ppm 0.6 test.0.6.ppm
./scaleRGBImg test.ppm 0.4 test.0.4.ppm
./scaleRGBImg test.ppm 0.2 test.0.2.ppm
$ for I in 0.8 0.6 0.4 0.2 ; do ./scaleRGBImg test.ppm $I test.$I.ppm ; done
$
This is what came out:
test.0.8.ppm:
test.0.6.ppm:
test.0.4.ppm:
test.0.2.ppm:
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);
I want to read and write a ppm image and calculate the average number of color.
My read method works fine and also the average number of each color is correct, but my write method gives me a wrong result.
The buffer which holds the data has type Vec3<float>*. Every 3D vector holds the r, g and b values.
Here is the read method:
Image * ReadPPM(const char * filename) {
Image* image;
ifstream file;
unsigned int width, height;
string version;
float maxvalue;
file.open(filename, ios::in | ios::binary);
if (!file) {
cerr << "file could not be open" << endl;
exit(EXIT_FAILURE);
}
// read the header
file >> version;
// check the header
// PPM "header" is valid
if (version.compare("P6") != 0)
{
cout << "Invalid image format (must be 'P6')";
exit(EXIT_FAILURE);
}
file >> width >> height >> maxvalue;
size_t size = height * width*3;
unsigned char * buffer = new unsigned char[size];
Vec3<float>* finalBuffer = new Vec3<float>[size / 3];
file.get();
file.read( (char *)buffer, size);
int j = 0;
for (int i = 0; i < size; i+=3) {
Vec3<float> vec(
(int)buffer[i]/maxvalue,
(int)buffer[i+1] / maxvalue,
(int)buffer[i+2] / maxvalue
);
finalBuffer[j] = vec;
j++;
}
file.clear();
file.close();
image = new Image(width, height, finalBuffer);
return image;
}
Here is the write method:
bool Image::operator >> (string filename) {
ofstream file;
file.open(filename,ios::out | ios::binary);
if (!file) {
cerr << "Cannot open file" << endl;
return false;
}
file << "P6" <<"\n";
file << getWidth() << "\n";
file << getHeight() << "\n";
file << "255" << "\n";
size_t size = height * width;
void*img = getRawDataPtr(); //returns void* buffer
unsigned char * temp = new unsigned char[size*3];
Vec3<float>* buff = (Vec3<float>*)img;
for (int i = 0; i<size; i++) {
Vec3<float> vector;
vector[0] =buff[i].x;
vector[1] = buff[i].y;
vector[2] = buff[i].z; // /255.f;
buff[i] = vector;
}
for (int i = 0; i < size; i++) {
temp[i * 3] = static_cast<unsigned char>(buff[i].x);
temp[i * 3 + 1] = static_cast<unsigned char>(buff[i].y);
temp[i * 3 + 2] = static_cast<unsigned char>(buff[i].z);
}
file.write((char *)temp, size * 3);
if (file.fail()) {
cerr << "Could not write data" << endl;
return false;
}
file.clear();
file.close();
return true;
delete[] buff;
delete[] temp;
}
Your values are normalized between 0 and 1 when reading the file. When writing, you just cast the float values into unsigned char, so you can only obtain 0 and 1 values: I guess you final image is filled with black.
void*img = getRawDataPtr(); //returns void* buffer
unsigned char * temp = new unsigned char[size*3];
Vec3<float>* buff = (Vec3<float>*)img;
Why creating a dynamic array you will have to manage? You are coding in C++, so use a container like std::vector.
for (int i = 0; i<size; i++) {
Vec3<float> vector;
vector[0] =buff[i].x;
vector[1] = buff[i].y;
vector[2] = buff[i].z; // /255.f;
buff[i] = vector;
}
What is the purpose of this loop? You are creating a Vec3<float> filled without transformation from the Vec3<float> in the current pixel in which you assign without transformation your temporary value: this just does nothing.
return true;
delete[] buff;
delete[] temp;
As I said in the comments, the code after return is not executed, so you will never release the memory you allocated for temp. Use a std::vector and you will not have to do it. buff is your original image: you should not delete it here.
Once cleaned, your code may become:
std::vector<unsigned char> temp(size*3);
Vec3<float>* buff = static_cast<Vec3<float>*>(getRawDataPtr());
for (int i = 0; i < size; i++) {
temp[i * 3] = static_cast<unsigned char>(buff[i].x * 255);
temp[i * 3 + 1] = static_cast<unsigned char>(buff[i].y * 255);
temp[i * 3 + 2] = static_cast<unsigned char>(buff[i].z * 255);
}
file.write(reinterpret_cast<char*>(&temp[0]), size * 3);
if (file.fail()) {
cerr << "Could not write data" << endl;
return false;
}
file.close();
return true;
I'm creating a qrcode with the library qrencode.h
This creation is working nice but how would one output the qrcode to a BMP file within c++?
At this very moment i have this code:
const char* szSourceSring = QRCODE_TEXT;
unsigned int unWidth, x, y, l, n, unWidthAdjusted, unDataBytes;
unsigned char* pRGBData, *pSourceData, *pDestData;
QRcode* pQRC;
FILE* f;
if (pQRC = QRcode_encodeString(szSourceSring, 4, QR_ECLEVEL_H, QR_MODE_8, 1))
{
unWidth = pQRC->width;
unWidthAdjusted = unWidth * OUT_FILE_PIXEL_PRESCALER * 3;
if (unWidthAdjusted % 4)
unWidthAdjusted = (unWidthAdjusted / 4 + 1) * 4;
unDataBytes = unWidthAdjusted * unWidth * OUT_FILE_PIXEL_PRESCALER;
// Allocate pixels buffer
if (!(pRGBData = (unsigned char*)malloc(unDataBytes)))
{
printf("Out of memory");
}
// Preset to white
memset(pRGBData, 0xff, unDataBytes);
// Prepare bmp headers
BITMAPFILEHEADER kFileHeader;
kFileHeader.bfType = 0x4D42; // "BM"
kFileHeader.bfSize = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) +
unDataBytes;
kFileHeader.bfReserved1 = 0;
kFileHeader.bfReserved2 = 0;
kFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER);
BITMAPINFOHEADER kInfoHeader;
kInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
kInfoHeader.biWidth = unWidth * OUT_FILE_PIXEL_PRESCALER;
kInfoHeader.biHeight = -((int)unWidth * OUT_FILE_PIXEL_PRESCALER);
kInfoHeader.biPlanes = 1;
kInfoHeader.biBitCount = 24;
kInfoHeader.biCompression = BI_RGB;
kInfoHeader.biSizeImage = 0;
kInfoHeader.biXPelsPerMeter = 0;
kInfoHeader.biYPelsPerMeter = 0;
kInfoHeader.biClrUsed = 0;
kInfoHeader.biClrImportant = 0;
// Convert QrCode bits to bmp pixels
pSourceData = pQRC->data;
for(y = 0; y < unWidth; y++)
{
pDestData = pRGBData + unWidthAdjusted * y * OUT_FILE_PIXEL_PRESCALER;
for(x = 0; x < unWidth; x++)
{
if (*pSourceData & 1)
{
for(l = 0; l < OUT_FILE_PIXEL_PRESCALER; l++)
{
for(n = 0; n < OUT_FILE_PIXEL_PRESCALER; n++)
{
*(pDestData + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_B;
*(pDestData + 1 + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_G;
*(pDestData + 2 + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_R;
}
}
}
pDestData += 3 * OUT_FILE_PIXEL_PRESCALER;
pSourceData++;
}
}
// Output the bmp file
/*if (((f = fopen(OUT_FILE, "r")) != NULL))
{*/
f = fopen(OUT_FILE, "wb");
fwrite(&kFileHeader, sizeof(BITMAPFILEHEADER), 14, f);
fwrite(&kInfoHeader, sizeof(BITMAPINFOHEADER), 40, f);
fwrite(pRGBData, sizeof(unsigned char), unDataBytes, f);
fclose(f);
/* }
else
{
printf("Unable to open file");
}
*/
// Free data
free(pRGBData);
QRcode_free(pQRC);
}
else
{
printf("NULL returned");
}
But somehow this creates a BMP with corrupt headers. Whenever i'm opening the bmp file it says:
"BMP Image has unsupported header size"
What am i doing wrong?
And is it possible to save to png instead of BMP?
I have access to the libPNG library
Here is a code example which dumps a 24 bpp bmp file created from a QR-Code. The error you see is probably not caused by the QR-Code library, but rather something in the bmp file code.
The bmp file created by this example works fine with the image viewer packaged with my Windows 8.1. If you also do not see the error, you could check for differences in each binary output to pinpoint the problem. If you want.
This question is tagged "C++" and "C++11", so this example uses the C++ std library for file output, and doesn't use malloc. (But almost equally bad -- I use new and delete in some container code, where a std::vector member is preferred...don't tell anyone). Also, this example writes each piece of data directly to the file, instead of using a file-sized intermediate buffer, like pDestData.
#include <iostream>
#include <fstream>
// A fake (or "somewhat limited") QR Code data container
struct Qrc {
int dimsize; // the width and height
unsigned char* data; // buffer which contains the elements
Qrc() {
static const unsigned int bin[] = { // encodes an important secret message
0xfc8b7d7f,0xa801a83,0xd6e54d76,0xaa9eb2ed,0x43ed05db,0xb8786837,0x55555fe0,
0x5a4c807f,0xcf315c00,0x6e8019ce,0xc7819e0d,0xd4857ba8,0x4ac5e347,0xf6f349ba,
0xd433ccdd,0x2998361e,0x4453fab3,0x526d9085,0x81f38924,0xb4da0811,0x84b3131a,
0x9639915e,0x3b74a4ff,0x42aa0c11,0x4127be16,0x1f4350,0xff620296,0xad54de1,
0xd38c2272,0xa3f76155,0x5366a7ab,0x9bdd2257,0x300d5520,0x85842e7f,0 };
dimsize = 33;
data = new unsigned char[dimsize * dimsize];
auto p = data;
auto endp = p + dimsize * dimsize;
for(unsigned int b : bin) {
for(int i=0; i<32; ++i) {
if(p == endp) break;
*(p++) = b & (1 << i) ? 255 : 0;
} } }
Qrc(const Qrc&) = delete;
Qrc& operator = (const Qrc&) = delete;
~Qrc() { delete [] data; }
};
struct BIH { // a private definition of BITMAPINFOHEADER
unsigned int sz;
int width, height;
unsigned short planes;
short bits;
unsigned int compress, szimage;
int xppm, yppm;
unsigned int clrused, clrimp;
};
void SaveBmp(const char* filename, const Qrc& qrc) {
// Asker's Qrc struct delivered as a pointer, from a C API, but this example doesn't mimic that.
std::ofstream ofs(filename, std::ios_base::out | std::ios_base::binary);
if(!ofs) {
std::cout << "Writing " << filename << " failed\n";
return;
}
const int side_len = qrc.dimsize; // width and height of the (square) QR Code
const int pixel_side_len = 4; // QRC element's size in the bmp image (in pixels)
const int bmp_line_bytes = side_len * pixel_side_len * 3;
const int bmp_line_pad_bytes = (4 - bmp_line_bytes % 4) % 4; // bmp line data padding size
const int bmp_data_size = side_len * (bmp_line_bytes + bmp_line_pad_bytes);
BIH bih = { sizeof(bih) };
bih.width = side_len * pixel_side_len; // element count * element size
bih.height = -side_len * pixel_side_len; // negative height => data begins at top of image
bih.planes = 1;
bih.bits = 24;
const int header_size = sizeof(bih) + 14; // size of the bmp file header
const int filesize = header_size + bmp_data_size; // size of the whole file
ofs.write("BM", 2);
ofs.write(reinterpret_cast<const char*>(&filesize), 4);
ofs.write("\0\0\0\0", 4); // 2x 16-bit reserved fields
ofs.write(reinterpret_cast<const char*>(&header_size), 4);
ofs.write(reinterpret_cast<const char*>(&bih), sizeof(bih));
// pixel colors, as Blue, Green, Red char-valued triples
// the terminating null also makes these usable as 32bpp BGRA values, with Alpha always 0.
static const char fg_color[] = "\0\0\0";
static const char bg_color[] = "\xff\xff\xff";
auto pd = qrc.data;
// send pixel data directly to the bmp file
// QRC elements are expanded into squares
// whose sides are "pixel_side_len" in length.
for(int y=0; y<side_len; ++y) {
for(int j=0; j<pixel_side_len; ++j) {
auto pdj = pd;
for(int x=0; x<side_len; ++x) {
for(int i=0; i<pixel_side_len; ++i) {
// *pdj will be 0 or 255 (from "fake" Qrc)
// Using "*pdj & 1" here, just to match asker's code
// without knowing why this was done.
ofs.write(*pdj & 1 ? fg_color : bg_color, 3);
}
++pdj;
}
if(bmp_line_pad_bytes) {
ofs.write("\0\0\0", bmp_line_pad_bytes);
}
}
pd += side_len;
}
}
int main() {
SaveBmp("MyQrCode.bmp", Qrc());
}