I'm supposed to create some code to stitch together N bmp images found in a folder. At the moment I just want to add the images together, side by side, don't care yet about common regions in them (I'm referring to how panoramic images are made).
I have tried to use some examples online for different functions that i need, examples which I've partially understood. I'm currently stuck because I can't really figure out what's wrong.
The basis of the bmp.h file is this page:
https://solarianprogrammer.com/2018/11/19/cpp-reading-writing-bmp-images/
I'm attaching my code and a screenshot of the exception VS throws.
main:
#include "bmp.h"
#include <fstream>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
int totalImages = 0;
int width = 0;
int height;
int count = 0;
std::string path = "imagini";
//Here i count the total number of images in the directory.
//I need this to know the width of the composed image that i have to produce.
for (const auto & entry : fs::directory_iterator(path))
totalImages++;
//Here i thought about going through the directory and finding out the width of the images inside it.
//I haven't managed to think of a better way to do this (which is probably why it doesn't work, i guess).
//Ideally, i would have taken one image from the directory and multiply it's width
//by the total number of images in the said directory, thus getting the width of the resulting image i need.
for (auto& p : fs::directory_iterator(path))
{
std::string s = p.path().string();
const char* imageName = s.c_str();
BMP image(imageName);
width = width + image.bmp_info_header.width;
height = image.bmp_info_header.height;
}
BMP finalImage(width, height);
//Finally, I was going to pass the directory again, and for each image inside of it, i would call
//the create_data function that i wrote in bmp.h.
for (auto& p : fs::directory_iterator(path))
{
count++;
std::string s = p.path().string();
const char* imageName = s.c_str();
BMP image(imageName);
//I use count to point out which image the iterator is currently at.
finalImage.create_data(count, image, totalImages);
}
//Here i would write the finalImage to a bmp image.
finalImage.write("textura.bmp");
}
bmp.h (I have only added the part I wrote, the rest of the code is found at the link I've provided above):
// This is where I try to copy the pixel RGBA values from the image passed as parameter (from it's data vector) to my
// resulting image (it's data vector) .
// The math should be right, I've gone through it with pen&paper, but right now I can't test it because the code doesn't work for other reasons.
void create_data(int count, BMP image, int totalImages)
{
int w = image.bmp_info_header.width * 4;
int h = image.bmp_info_header.height;
int q = 0;
int channels = image.bmp_info_header.bit_count / 8;
int startJ = w * channels * (count - 1);
int finalI = w * channels * totalImages* (h - 1);
int incrementI = w * channels * totalImages;
for(int i = 0; i <= finalI; i+incrementI)
for (int j = i + startJ; j < i + startJ + w * channels; j+4)
{
data[j] =image.data[q];
data[j+1]=image.data[q+1];
data[j+2]=image.data[q+2];
data[j+3]=image.data[q+3];
q = q + 4;
}
}
Error I get: https://imgur.com/7fq9BH4
This is the first time I post a question, I've only looked up answers here. If I don't provide enough info to my problem, or something I've done is not ok I apologize.
Also, English is my second language, so I hope I got my points across pretty clear.
EDIT: Since I forgot to mention, I would like to do this code without using external libraries like OpenCV or ImageMagick.
Related
I am working in c++ trying to create an image and save it to a given directory. I don't care what type of image file it is (PNG, jpeg, bitmap, etc.), I just want it to be viewable in Windows 10. The data stream is in the following form: std::vector<unsigned char>.
I would like to do this natively in c++, but I am not against using a library if it is straightforward to implement and lightweight.
I have this working in C# using the following code, but I don't know if there is a direct translation into c++
// C# code to translate into c++?
var image = new BitmapImage();
using (var ms = new MemoryStream(message.ImageData))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
image.EndInit();
image.Freeze();
}
C# implementations have image routines in the language's standard library. C++ does not. So there is no equivalent code in standard C++: you need to use a third party library. On Windows you could use Win32.
Below I use stb_image_write.h, which can be found here; the stb libraries are barebones 1-file or 2-file libraries typically used in independent game development where having a dependency on libPNG et. al. would be overkill.
#include <vector>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
std::vector<unsigned char> generate_some_image(int wd, int hgt)
{
// this is just an example for tutorial purposes ... generate a red circle
// in a white field.
std::vector<unsigned char> data(wd * hgt * 4);
int c_x = wd / 2;
int c_y = hgt / 2;
int radius = c_x;
int i = 0;
for (int y = 0; y < hgt; y++) {
for (int x = 0; x < wd; x++) {
if ((x - c_x) * (x - c_x) + (y - c_y) * (y - c_y) <= radius * radius) {
data[i++] = 255;
data[i++] = 0;
data[i++] = 0;
data[i++] = 255;
} else {
data[i++] = 255;
data[i++] = 255;
data[i++] = 255;
data[i++] = 255;
}
}
}
return data;
}
int main()
{
const int wd = 128;
const int hgt = 128;
std::vector<unsigned char> data = generate_some_image(wd, hgt);
return stbi_write_png( "c:\\test\\foo.png", wd, hgt, 4, &(data[0]), 4*wd);
}
There is no default standard C++ library for creating an image file with a specific format (PNG, Bitmap ... etc). However, there are tricks to create an image and make it viewable if this is all that you need. The ways are discussed below:
Use a library as jwezorek! mentioned in his answer. There are tons of libraries that can help: OpenCV, STB ... etc
Create a function that creates a file and streams the pixels data to that file in a specific format (Can be fun for some and a headache for others).
Save the image data as raw data (pixels data only) in a file, and use a simpler programming language to read the file data and view the image. For instance, let's assume that you will use "python" as the simpler language to create a simple image viewer on windows 10, the code will look as follows:
import NumPy as np
import scipy.misc as smp
from PIL import Image
w = 1066
h = 600
with open('rgb.raw', 'r') as file:
data = file.read().replace('\n', ' ') # Replacing new lines with spaces
rgbs = data.split(' ') # Get a 1D array of all the numbers (pixels) in file
rgbs = [ int(x) for x in rgbs if x != ''] # Change the data into integers
nprgbs = np.array(rgbs, dtype=np.uint8) # Define a numpy array
arr = np.reshape(nprgbs, (h,w,3)) # Reorder the 1D array to 3D matrix (width, height, channels)
img = Image.fromarray(arr) # Create an Image from the pixels.
img.show() # View the image in a window (It uses the default viewer of the OS)
The above code will read a file containing the RGB channels of an image and view it on the default viewer.
As you can see, you can do it in a variety of ways, choose the simplest and the most suitable for you. In conclusion, the answer to your question is "No" unless you use a custom library, create your own functions, or use simpler programming languages to read the stream of data and use them to create the file.
The following code is just supposed to load an image, fill it with a constant value and save it again.
Of course that doesn't have a purpose yet, but still it just doesn't work.
I can read the pixel values in the loop, but all changes are without effect and saves the file as it was loaded.
Think I followed the "efficient way" here accurately: http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html
int main()
{
Mat im = imread("C:\\folder\\input.jpg");
int channels = im.channels();
int pixels = im.cols * channels;
if (!im.isContinuous())
{ return 0; } // Just to show that I've thought of that. It never exits here.
uchar* f = im.ptr<uchar>(0);
for (int i = 0; i < pixels; i++)
{
f[i] = (uchar)100;
}
imwrite("C:\\folder\\output.jpg", im);
return 0;
}
Normal cv functions like cvtColor() are taking effect as expected.
Are the changes through the array happening on a buffer somehow?
Huge thanks in advance!
The problem is that you are not looking at all pixels in the image. Your code only looks at im.cols*im.channels() which is a relatively small number as compared to the size of the image (im.cols*im.rows*im.channels()). When used in the for loop using the pointer, it only sets a value for couple of rows in an image ( if you look closely you will notice the saved image will have these set ).
Below is the corrected code:
int main()
{
Mat im = imread("C:\\folder\\input.jpg");
int channels = im.channels();
int pixels = im.cols * im.rows * channels;
if (!im.isContinuous())
{ return 0; } // Just to show that I've thought of that. It never exits here.
uchar* f = im.ptr<uchar>(0);
for (int i = 0; i < pixels; i++)
{
f[i] = (uchar)100;
}
imwrite("C:\\folder\\output.jpg", im);
return 0;
}
I am working on a project for my computer science class where we create a photomosaic of a source image. We have been given a base image class. Along with the standard constructors, this class contains getter functions to return the private pixel array data and image height and width. We were told to create a derived class for all of the filler images, which need to be stored in a linked list. I've got the majority of the program worked out, but I don't know how to replace blocks of the source image with the pixel data from a filler image.
#include "stdafx.h" //needed for the Windows display
#include <cstdlib>
#include <cmath>
#include <string>
#include <iostream>
using namespace std;
#include "globals.h" //some global variables are included here
#include "pixel.h" //includes the pixel class for storing individual pixels
#include "image.h" //includes the image class we will be using.
#include "fillerImage.h" //includes the fillerImage
#include "fillNode.h"
#include <sstream>
image* mosaic = displayed; //displayed is the current image in the gui, mosaic is the copy that will be the photomosaic
pixel** mospix = mosaic->getPixels();
pixel** headpix = head->data->pix; //I created a public member pixel** in the filler image class with the hope that I could use it to access the private pixel data of the base image class.
int mosaicWidth = displayed->getWidth() / blockWidth * FILLER_IMAGE_WIDTH;
int mosaicHeight = displayed->getHeight() / blockHeight * FILLER_IMAGE_HEIGHT;
//mosaic->createNewImage(mosaicWidth, mosaicHeight);
for (int i = 0; i < mosaicHeight; i = i + blockHeight)
{
for (int j = 0; j < mosaicWidth; j = j + blockWidth)
{
for (int blockrow = 0; blockrow < blockHeight; blockrow++)
{
for (int blockcol = 0; blockcol < blockWidth; blockcol++)
{
mospix = headpix;
}
}
}
displayed = mosaic;
}
displayed = mosaic;
I keep running into the problem of attempting to write to protected data; how can I modify the pixel data of mosaic? I'm not allowed to edit the base image class, just the filler image class. I apologize if my post is poorly formatted, this is my first time asking for help. Thanks in advance!
EDIT: Here is the image class header. I'm not allowed to modify this in any way, and it does not contain a setter for the pixel data.
#include "globals.h"
#include "pixel.h"
using namespace std;
class image {
public:
image(); //the image constructor (initializes everything)
image(string filename); //a image constructor that directly loads an image from disk
~image(); //the image destructor (deletes the dynamically created pixel array)
void createNewImage(int width, int height); //this function deletes any current image data and creates a new blank image
//with the specified width/height and allocates the needed number of pixels
//dynamically.
bool loadImage(string filename); //load an image from the specified file path. Return true if it works, false if it is not a valid image.
//Note that we only accept images of the RGB 8bit colorspace!
void saveImage(string filename); //Save an image to the specified path
pixel** getPixels(); //return the 2-dimensional pixels array
int getWidth(); //return the width of the image
int getHeight(); //return the height of the image
void viewImage(CImage* myImage); //This function is called by the windows GUI. It returns the image in format the GUI understands.
private:
void pixelsToCImage(CImage* myImage); //this function is called internally by the image class.
//it converts our pixel object array to a standard BGR uchar array with word spacing.
//(Don't worry about what this does)
pixel** pixels; // pixel data array for image
int width, height; // stores the image dimensions
};
#endif
The only way to access to the pixels is via getPixel():
You can definitively read from it.
As this member function doesn't return a const pointer, and in absence of documentation, I assume that you can write on it as well.
With the following statement, you get access to the pixel array of mosaic.
pixel** mospix = mosaic->getPixels();
From that moment onwards, you can read/write any pixel directly by using mospix[i][j], as long as 0 <= i && i < mosaic->getHeight() && 0<=j && j<mosaic->getWidth().
There is however a serious problem in your code. In the nested loops, your inner statement is:
mospix = headpix;
This doesn't copy the current pixel as you seem to expect. It replaces the pointer that you had correctly initialized, with a copy of the pointer headpix. From then onwards, if you would access to a mospix array element, you would in reality let the picture inside mosaic unchanged, but you would access pixels in head->data !
I don't know exactly how you want to create the mosaic, but I think that there is a mismatch in the way you loop and could imagine that you wanted to say woemething like:
for (int i = 0; i < mosaic->getHeight(); i = i + blockHeight)
{
for (int blockrow = 0; blockrow < blockHeight; blockrow++)
{
for (int j = 0; j < mosaic->getWidth(); j = j + blockWidth)
{
for (int blockcol = 0; blockcol < blockWidth; blockcol++)
{
mospix[i+blockrow][j+blockcol] = headpix[blockrow][blockcol];
}
}
}
}
displayed = mosaic; // Not sure it's needed: both are pointers pointing to the same object
I guess it's such an easy question (I'm coming from Java), but I can't figure out how it works.
I simply want to increment an vector element by one. The reason for this is, that I want to compute a histogram out of image values. But whatever I try I just can accomplish to assign a value to the vector. But not to increment it by one!
This is my histogram function:
void histogram(unsigned char** image, int height,
int width, vector<unsigned char>& histogramArray) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
// histogramArray[1] = (int)histogramArray[1] + (int)1;
// add histogram position by one if greylevel occured
histogramArray[(int)image[i][j]]++;
}
}
// display output
for (int i = 0; i < 256; i++) {
cout << "Position: " << i << endl;
cout << "Histogram Value: " << (int)histogramArray[i] << endl;
}
}
But whatever I try to add one to the histogramArray position, it leads to just 0 in the output. I'm only allowed to assign concrete values like:
histogramArray[1] = 2;
Is there any simple and easy way? I though iterators are hopefully not necesarry at this point, because I know the exakt index position where I want to increment something.
EDIT:
I'm so sorry, I should have been more precise with my question, thank you for your help so far! The code above is working, but it shows a different mean value out of the histogram (difference of around 90) than it should. Also the histogram values are way different than in a graphic program - even though the image values are exactly the same! Thats why I investigated the function and found out if I set the histogram to zeros and then just try to increase one element, nothing happens! This is the commented code above:
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
histogramArray[1]++;
// add histogram position by one if greylevel occured
// histogramArray[(int)image[i][j]]++;
}
}
So the position 1 remains 0, instead of having the value height*width. Because of this, I think the correct calculation histogramArray[image[i][j]]++; is also not working properly.
Do you have any explanation for this? This was my main question, I'm sorry.
Just for completeness, this is my mean function for the histogram:
unsigned char meanHistogram(vector<unsigned char>& histogram) {
int allOccurences = 0;
int allValues = 0;
for (int i = 0; i < 256; i++) {
allOccurences += histogram[i] * i;
allValues += histogram[i];
}
return (allOccurences / (float) allValues) + 0.5f;
}
And I initialize the image like this:
unsigned char** image= new unsigned char*[width];
for (int i = 0; i < width; i++) {
image[i] = new unsigned char[height];
}
But there shouldn't be any problem with the initialization code, since all other computations work perfectly and I am able to manipulate and safe the original image. But it's true, that I should change width and height - since I had only square images it didn't matter so far.
The Histogram is created like this and then the function is called like that:
vector<unsigned char> histogramArray(256);
histogram(array, adaptedHeight, adaptedWidth, histogramArray);
So do you have any clue why this part histogramArray[1]++; don't increases my histogram? histogramArray[1] remains 0 all the time! histogramArray[1] = 2; is working perfectly. Also histogramArray[(int)image[i][j]]++; seems to calculate something, but as I said, I think it's wrongly calculating.
I appreciate any help very much! The reason why I used a 2D Array is simply because it is asked for. I like the 1D version also much more, because it's way simpler!
You see, the current problem in your code is not incrementing a value versus assigning to it; it's the way you index your image. The way you've written your histogram function and the image access part puts very fine restrictions on how you need to allocate your images for this code to work.
For example, assuming your histogram function is as you've written it above, none of these image allocation strategies will work: (I've used char instead of unsigned char for brevity.)
char image [width * height]; // Obvious; "char[]" != "char **"
char * image = new char [width * height]; // "char*" != "char **"
char image [height][width]; // Most surprisingly, this won't work either.
The reason why the third case won't work is tough to explain simply. Suffice it to say that a 2D array like this will not implicitly decay into a pointer to pointer, and if it did, it would be meaningless. Contrary to what you might read in some books or hear from some people, in C/C++, arrays and pointers are not the same thing!
Anyway, for your histogram function to work correctly, you have to allocate your image like this:
char** image = new char* [height];
for (int i = 0; i < height; ++i)
image[i] = new char [width];
Now you can fill the image, for example:
for (int i = 0; i < height; ++i)
for (int j = 0; j < width; ++j)
image[i][j] = rand() % 256; // Or whatever...
On an image allocated like this, you can call your histogram function and it will work. After you're done with this image, you have to free it like this:
for (int i = 0; i < height; ++i)
delete[] image[i];
delete[] image;
For now, that's enough about allocation. I'll come back to it later.
In addition to the above, it is vital to note the order of iteration over your image. The way you've written it, you iterate over your columns on the outside, and your inner loop walks over the rows. Most (all?) image file formats and many (most?) image processing applications I've seen do it the other way around. The memory allocations I've shown above also assume that the first index is for the row, and the second is for the column. I suggest you do this too, unless you've very good reasons not to.
No matter which layout you choose for your images (the recommended row-major, or your current column-major,) it is in issue that you should always keep in your mind and take notice of.
Now, on to my recommended way of allocating and accessing images and calculating histograms.
I suggest that you allocate and free images like this:
// Allocate:
char * image = new char [height * width];
// Free:
delete[] image;
That's it; no nasty (de)allocation loops, and every image is one contiguous block of memory. When you want to access row i and column j (note which is which) you do it like this:
image[i * width + j] = 42;
char x = image[i * width + j];
And you'd calculate the histogram like this:
void histogram (
unsigned char * image, int height, int width,
// Note that the elements here are pixel-counts, not colors!
vector<unsigned> & histogram
) {
// Make sure histogram has enough room; you can do this outside as well.
if (histogram.size() < 256)
histogram.resize (256, 0);
int pixels = height * width;
for (int i = 0; i < pixels; ++i)
histogram[image[i]]++;
}
I've eliminated the printing code, which should not be there anyway. Note that I've used a single loop to go through the whole image; this is another advantage of allocating a 1D array. Also, for this particular function, it doesn't matter whether your images are row-major or column major, since it doesn't matter in what order we go through the pixels; it only matters that we go through all the pixels and nothing more.
UPDATE: After the question update, I think all of the above discussion is moot and notwithstanding! I believe the problem could be in the declaration of the histogram vector. It should be a vector of unsigned ints, not single bytes. Your problem seems to be that the value of the vector elements seem to stay at zero when your simplify the code and increment just one element, and are off from the values they need to be when you run the actual code. Well, this could be a symptom of numeric wrap-around. If the number of pixels in your image are a a multiple of 256 (e.g. 32x32 or 1024x1024 image) then it is natural that the sum of their number would be 0 mod 256.
I've already alluded to this point in my original answer. If you read my implementation of the histogram function, you see in the signature that I've declared my vector as vector<unsigned> and have put a comment above it that says this victor counts pixels, so its data type should be suitable.
I guess I should have made it bolder and clearer! I hope this solves your problem.
I have spent probably too many hours looking for tutorials on image processing (WITHOUT the use of external libraries) with no real success. If anyone knows any good tutorials that can be found that can help in this way, I'd really appreciate that.
I am pretty new to coding (this is my first year in college), and the assignment our professor is asking for requires original code to transform 24-bit bitmap images.
I found a question in StackExchange that shows rotation of an image without use of external libraries:
My code rotates a bmp picture correctly but only if the number of pixels is a muliple of 4... can anyone see whats wrong?
Using this code (with the starter project we were given and I had to build upon), I was able to create this code:
Byte is defined as a typedef of unsigned chars.
void BMPImage::RotateImage()
{
vector<byte> newBMP(m_BIH.biWidth * m_BIH.biHeight);
long newHeight = m_BIH.biWidth; /* Preserving the original width */
m_BIH.biWidth = m_BIH.biHeight; /* Setting the width as the height*/
m_BIH.biHeight = newHeight; /* Using the value of the original width, we set it as the new height */
for (int r = 0; r < m_BIH.biHeight; r++)
{
for (int c = 0; c < m_BIH.biWidth; c++)
{
long y = c + (r*m_BIH.biHeight);
long x = c + (r*m_BIH.biWidth - r - 1) + (m_BIH.biHeight*c);
newBMP[y] = m_ImageData[x];
}
}
m_ImageData = newBMP;
}
This code doesn't show any red squigglies, but when I try to execute the rotation, I get a vector subscript out of range error message pop-up. I've only used vectors in one assignment before, so I don't know where the issue is. Help please!
I think the issue might be here:
m_ImageData = newBMP;
Assume your newBMP has width = 1 and height = 2 then
vector<byte> newBMP(m_BIH.biWidth * m_BIH.biHeight);
would be an array of size 2 with valid indexrange [0 1]. Your index calculation
long y = c + (r*m_BIH.biHeight);
would be 2 for c = 0 and r = 1. But 2 is not a valid index for your vector and with
newBMP[y] = ...
you access an element that is not part of the vector. Your index x would be -1 for this example.