Skip to content

Instantly share code, notes, and snippets.

@sysint64
Created May 25, 2016 17:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sysint64/f95f1c016f2aa091820919afd590784a to your computer and use it in GitHub Desktop.
Save sysint64/f95f1c016f2aa091820919afd590784a to your computer and use it in GitHub Desktop.
bmp_palette.cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <array>
#include <math.h>
// Палитры цветов
#include "palettes.h"
class Bitmap {
public:
uint32_t width, height;
uint16_t bitsPerPixel;
std::vector<uint8_t> pixels;
std::vector<uint8_t> fileInfo;
Bitmap() {}
Bitmap (const std::string &fileName) {
loadFromFile(fileName);
}
void loadFromFile (const std::string &fileName);
void saveToFile (const std::string &fileName);
void changePalette (uint8_t palette[][3], size_t size);
};
void Bitmap::loadFromFile (const std::string &fileName) {
std::fstream hFile (fileName, std::ios::in | std::ios::binary);
if (!hFile.is_open())
throw std::invalid_argument ("Error: File Not Found.");
hFile.seekg (0, std::ios::end);
size_t length = hFile.tellg();
hFile.seekg (0, std::ios::beg);
fileInfo.resize (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.");
}
bitsPerPixel = fileInfo[28];
width = fileInfo[18] + (fileInfo[19] << 8);
height = fileInfo[22] + (fileInfo[23] << 8);
uint32_t pixelsOffset = fileInfo[10] + (fileInfo[11] << 8);
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 ();
}
void Bitmap::saveToFile (const std::string &fileName) {
std::fstream hFile (fileName, std::ios::out | std::ios::binary);
fileInfo[28] = bitsPerPixel;
uint32_t pixelsOffset = fileInfo[10] + (fileInfo[11] << 8);
hFile.write (reinterpret_cast<char*>(fileInfo.data()), pixelsOffset);
hFile.write (reinterpret_cast<char*>(pixels .data()), pixels .size());
hFile.close ();
}
std::array<double, 3> RGBtoXYZ (uint8_t* color) {
double R = static_cast<double>(color[0]) / 255.0;
double G = static_cast<double>(color[1]) / 255.0;
double B = static_cast<double>(color[2]) / 255.0;
if (R > 0.04045 ) R = pow((R + 0.055) / 1.055, 2.4); else R = R / 12.92;
if (G > 0.04045 ) G = pow((G + 0.055) / 1.055, 2.4); else G = G / 12.92;
if (B > 0.04045 ) B = pow((B + 0.055) / 1.055, 2.4); else B = B / 12.92;
R *= 100; G *= 100; B *= 100;
double X = R * 0.4124 + G * 0.3576 + B * 0.1805;
double Y = R * 0.2126 + G * 0.7152 + B * 0.0722;
double Z = R * 0.0193 + G * 0.1192 + B * 0.9505;
return { X, Y, Z };
}
std::array<double, 3> XYZtoLab (const std::array<double, 3> &color) {
double X = color[0] / 95.047;
double Y = color[1] / 100.000;
double Z = color[2] / 108.883;
if (X > 0.008856) X = pow(X, 1.0/3.0); else X = (7.787 * X) + (16.0 / 116.0);
if (Y > 0.008856) Y = pow(Y, 1.0/3.0); else Y = (7.787 * Y) + (16.0 / 116.0);
if (Z > 0.008856) Z = pow(Z, 1.0/3.0); else Z = (7.787 * Z) + (16.0 / 116.0);
double L = (116.0 * Y) - 16.0;
double a = 500.0 * (X - Y);
double b = 200.0 * (Y - Z);
return { L, a, b };
}
inline std::array<double, 3> RGBtoLab (uint8_t* color) {
auto XYZ = RGBtoXYZ (color);
return XYZtoLab (XYZ);
}
inline double sqr (double x) { return x*x; }
// Использовал саму простую формулу CIE76
// https://en.wikipedia.org/wiki/Color_difference
inline double deltaE (std::array<double, 3> c1, std::array<double, 3> c2) {
const int L = 0; const int a = 1; const int b = 2;
return sqrt(sqr(c2[L]-c1[L]) + sqr(c2[a]-c1[a]) + sqr(c2[b]-c1[b]));
}
uint8_t getColor (uint8_t* color, uint8_t palette[][3], size_t size) {
auto LabTarget = RGBtoLab (color);
double minDeltaE = 999.0;
int target = 0;
for (int i = 0; i < size; ++i) {
auto LabPalette = RGBtoLab(&palette[i][0]);
auto cpDeltaE = deltaE (LabTarget, LabPalette);
if (minDeltaE > cpDeltaE) {
minDeltaE = cpDeltaE;
target = i;
}
}
return target;
}
void Bitmap::changePalette (uint8_t palette[][3], size_t size) {
std::vector<uint8_t> newPixels;
uint8_t byte = 0;
uint8_t c = 0;
uint8_t c1 = 0;
uint8_t c2 = 0;
for (int i = 0; i < pixels.size(); i += 3) {
int paletteIdx = getColor (&pixels[i], palette, size);
newPixels.push_back (palette[paletteIdx][0]);
newPixels.push_back (palette[paletteIdx][1]);
newPixels.push_back (palette[paletteIdx][2]);
}
std::cout << std::endl;
pixels = newPixels;
}
int main() {
Bitmap bmp ("turtles.bmp");
bmp.changePalette (RGB4_Palette, 16); // 4 битная палтира
// bmp.changePalette (RGB8_Palette, 256); // 8 битная палитра
bmp.saveToFile ("test.bmp");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment