// It captures an image from a connected camera (webcam) and stores it in a 24 bit BMP file

#include <iostream>
#include <string>
#include <fstream>
#include <vector>

#include <cstdio>

#include "opencv2/opencv.hpp"
// Extract files to a directory (DIR) from: https://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.4.3/opencv-3.4.3-vc14_vc15.exe
// Documentation: https://docs.opencv.org/3.4.3/
// In the properties of an Empty Project of Visual C++ (Release x64):
// + Compiler options, include directories: DIR\build\include
// + Linker options, library directories:   DIR\build\x64\vc15\lib
// + Linker options, library:               DIR\build\x64\vc15\lib\opencv_world343.lib
// To execute: DIR\build\x64\vc15\bin\opencv_world343.dll


typedef unsigned char tbyte;
typedef unsigned int t4bytes;


class Pixel24bit {
public:
  Pixel24bit(tbyte R, tbyte G, tbyte B) : R(R), G(G), B(B) { }
  tbyte R;
  tbyte G;
  tbyte B;
};

class RowPixel24bit {
public:
  RowPixel24bit(t4bytes width, Pixel24bit pixel) : pixels(width, pixel) { }
  std::vector<Pixel24bit> pixels;
};

class Image24bit {
public:
  Image24bit(t4bytes width, t4bytes height, tbyte R = 255, tbyte G = 255, tbyte B = 255) : width(width), height(height) {
    rows = std::vector<RowPixel24bit>(height, RowPixel24bit(width, Pixel24bit(R, G, B)));
  }
  Image24bit(const cv::Mat & frame);
  bool writeToFile(const std::string & fileName);
  std::vector<RowPixel24bit> rows;
  t4bytes width;
  t4bytes height;
};


int main(int argc, char **argv) {
  if (argc != 5) {
    std::fprintf(stderr, "Syntax: %s index width height outputfile\n\n", argv[0]);
    std::fprintf(stderr, "With default camera (index = 0)\n", argv[0]);
    std::fprintf(stderr, "Example: %s 0 640 480 output.bmp\n\n", argv[0]);
    std::fprintf(stderr, "With the following camera (index = 1)\n", argv[0]);
    std::fprintf(stderr, "Example: %s 1 640 480 output.bmp\n", argv[0]);
    return 1;
  }
  int index = std::stoi(argv[1]);
  double dwidth = std::stod(argv[2]);
  double dheight = std::stod(argv[3]);
  std::string spathfile(argv[4]);

  std::cout << "Opening camera " << index << "...\n";
  cv::VideoCapture camera(index);
  if (!camera.isOpened()) {
    return 1;
  }
  std::cout << "Camera " << index << " open\n";

  camera.set(cv::CAP_PROP_FRAME_WIDTH, dwidth);
  camera.set(cv::CAP_PROP_FRAME_HEIGHT, dheight);

  cv::Mat frame;
  camera >> frame;
  std::cout << "Frame captured\n";

  Image24bit image(frame);
  image.writeToFile(spathfile);
  std::cout << "Frame stored in file " << spathfile << "\n";

  return 0;
}


Image24bit::Image24bit(const cv::Mat & frame) : Image24bit(frame.cols, frame.rows) {
  for (std::size_t yy = 0; yy < frame.rows; yy++) {
    for (std::size_t xx = 0; xx < frame.cols; xx++) {
      rows[yy].pixels[xx].B = frame.at<cv::Vec3b>(frame.rows - 1 - yy, xx)[0];
      rows[yy].pixels[xx].G = frame.at<cv::Vec3b>(frame.rows - 1 - yy, xx)[1];
      rows[yy].pixels[xx].R = frame.at<cv::Vec3b>(frame.rows - 1 - yy, xx)[2];
    }
  }
}


bool Image24bit::writeToFile(const std::string & fileName) {
  t4bytes hh = rows.size();
  if (hh < 1) {
    return false;
  }
  t4bytes ww = rows[0].pixels.size();
  if (ww < 1) {
    return false;
  }

  int nBytesPaddingPerRow = (3 * ww % 4 == 0) ? 0 : (4 - 3 * ww % 4);
  t4bytes restSize = 3 * ww * hh + hh * nBytesPaddingPerRow;
  t4bytes fileSize = restSize + 54;

  unsigned char header[54] = { 0 };
  header[0] = 66;
  header[1] = 77;
  header[10] = 54;
  header[14] = 40;
  header[26] = 1;
  header[28] = 24;

  header[2] = *(reinterpret_cast<unsigned char *>(&fileSize) + 0); // File Size byte 0 (LSB)
  header[3] = *(reinterpret_cast<unsigned char *>(&fileSize) + 1); // File Size byte 1
  header[4] = *(reinterpret_cast<unsigned char *>(&fileSize) + 2); // File Size byte 2
  header[5] = *(reinterpret_cast<unsigned char *>(&fileSize) + 3); // File Size byte 3 (MSB)

  header[18] = *(reinterpret_cast<unsigned char *>(&ww) + 0); // Width byte 0 (LSB)
  header[19] = *(reinterpret_cast<unsigned char *>(&ww) + 1); // Width byte 1
  header[20] = *(reinterpret_cast<unsigned char *>(&ww) + 2); // Width byte 2
  header[21] = *(reinterpret_cast<unsigned char *>(&ww) + 3); // Width byte 3 (MSB)

  header[22] = *(reinterpret_cast<unsigned char *>(&hh) + 0); // Height byte 0 (LSB)
  header[23] = *(reinterpret_cast<unsigned char *>(&hh) + 1); // Height byte 1
  header[24] = *(reinterpret_cast<unsigned char *>(&hh) + 2); // Height byte 2
  header[25] = *(reinterpret_cast<unsigned char *>(&hh) + 3); // Height byte 3 (MSB)

  header[34] = *(reinterpret_cast<unsigned char *>(&restSize) + 0); // Rest Size byte 0 (LSB)
  header[35] = *(reinterpret_cast<unsigned char *>(&restSize) + 1); // Rest Size byte 1
  header[36] = *(reinterpret_cast<unsigned char *>(&restSize) + 2); // Rest Size byte 2
  header[37] = *(reinterpret_cast<unsigned char *>(&restSize) + 3); // Rest Size byte 3 (MSB)

  std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);

  fout.write(reinterpret_cast<char *>(&header[0]), 54);

  tbyte * rest = new tbyte[restSize];

  t4bytes yy;
  t4bytes xx;
  t4bytes pos = 0;
  for (yy = 0; yy < hh; yy++) {
    for (xx = 0; xx < ww; xx++) {
      rest[pos++] = rows[yy].pixels[xx].B;
      rest[pos++] = rows[yy].pixels[xx].G;
      rest[pos++] = rows[yy].pixels[xx].R;
    }
    for (xx = 0; xx < nBytesPaddingPerRow; xx++) {
      rest[pos++] = '\x0';
    }
  }

  fout.write(reinterpret_cast<char *>(rest), restSize);

  fout.close();

  delete[] rest;

  return true;
}
      

// Generated by xformulas.net on 20191022_191636