Загрузка данных
#include "iostream"
#include "vector"
#include "fstream"
#include "string"
#include "windows.h"
#include "GL/glut.h"
using namespace std;
class Matrix {
public:
int width;
int height;
vector<unsigned char> data;
Matrix() : width(0), height(0) {}
Matrix(int w, int h) : width(w), height(h), data(w * h, 0) {}
bool fromBmp(const string& filePath) {
ifstream file(filePath.c_str(), ios::binary);
if (!file) return false;
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
file.read(reinterpret_cast<char*>(&fileHeader), sizeof(fileHeader));
file.read(reinterpret_cast<char*>(&infoHeader), sizeof(infoHeader));
if (fileHeader.bfType != 0x4D42) return false;
width = infoHeader.biWidth;
height = infoHeader.biHeight;
data.resize(width * height);
file.seekg(fileHeader.bfOffBits, ios::beg);
int padding = (4 - (width * (infoHeader.biBitCount / 8)) % 4) % 4;
if (infoHeader.biBitCount == 24) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
unsigned char bgr[3];
file.read(reinterpret_cast<char*>(bgr), 3);
data[y * width + x] = static_cast<unsigned char>(0.299f * bgr[2] + 0.587f * bgr[1] + 0.114f * bgr[0]);
}
file.seekg(padding, ios::cur);
}
} else if (infoHeader.biBitCount == 8) {
file.seekg(sizeof(RGBQUAD) * 256, ios::cur);
for (int y = 0; y < height; ++y) {
file.read(reinterpret_cast<char*>(&data[y * width]), width);
file.seekg(padding, ios::cur);
}
}
file.close();
return true;
}
};
Matrix imgOriginal;
Matrix imgBinary;
Matrix imgEroded;
Matrix imgEdges;
Matrix binarizeMatrix(const Matrix& matrix, unsigned char threshold = 127) {
Matrix res(matrix.width, matrix.height);
for (int i = 0; i < matrix.width * matrix.height; ++i) {
res.data[i] = (matrix.data[i] >= threshold) ? 255 : 0;
}
return res;
}
/* =========================================================================
АЛГОРИТМ МОРФОЛОГИЧЕСКОЙ ЭРОЗИИ СТРУКТУРНЫМ ЭЛЕМЕНТОМ 3X3 ИЗ ЕДИНИЦ
Реализует операцию логического "И" (белый пиксель остается только тогда,
когда вся его окрестность 3х3 также заполнена белыми пикселями).
========================================================================= */
Matrix erodeMatrix(const Matrix& matrix) {
Matrix res(matrix.width, matrix.height);
// Проход по внутренним пикселям изображения (пропуская внешнюю рамку в 1 пиксель)
for (int y = 1; y < matrix.height - 1; ++y) {
for (int x = 1; x < matrix.width - 1; ++x) {
bool allWhite = true;
// Наложение квадратного структурного элемента 3х3
for (int ky = -1; ky <= 1; ++ky) {
for (int kx = -1; kx <= 1; ++kx) {
if (matrix.data[(y + ky) * matrix.width + (x + kx)] != 255) {
allWhite = false;
break;
}
}
if (!allWhite) break;
}
// Пиксель на выходе становится белым только при полном совпадении маски
res.data[y * matrix.width + x] = allWhite ? 255 : 0;
}
}
return res;
}
/* =========================================================================
АЛГОРИТМ ВЫДЕЛЕНИЯ ГРАНИЦ НА ОСНОВЕ ПОПИКСЕЛЬНОЙ ЛОГИЧЕСКОЙ ОПЕРАЦИИ XOR
Вычисляется по формуле: Edges = Binary XOR Eroded (оператор ^).
Оставляет включенными только те пиксели, которые изменили цвет после эрозии.
========================================================================= */
Matrix findEdgesXor(const Matrix& binary, const Matrix& eroded) {
Matrix res(binary.width, binary.height);
// Попиксельное применение операции исключающего ИЛИ ко всему массиву данных
for (int i = 0; i < binary.width * binary.height; ++i) {
res.data[i] = binary.data[i] ^ eroded.data[i];
}
return res;
}
void drawImageQuad(const Matrix& matrix, int xOffset, int yOffset, int quadW, int quadH) {
glViewport(xOffset, yOffset, quadW, quadH);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, matrix.width, 0, matrix.height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRasterPos2i(0, 0);
glDrawPixels(matrix.width, matrix.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, &matrix.data[0]);
}
void display() {
glClear(GL_COLOR_BUFFER_BIT);
int windowW = glutGet(GLUT_WINDOW_WIDTH);
int windowH = glutGet(GLUT_WINDOW_HEIGHT);
int quadW = windowW / 2;
int quadH = windowH / 2;
drawImageQuad(imgOriginal, 0, quadH, quadW, quadH);
drawImageQuad(imgBinary, quadW, quadH, quadW, quadH);
drawImageQuad(imgEroded, 0, 0, quadW, quadH);
drawImageQuad(imgEdges, quadW, 0, quadW, quadH);
glutSwapBuffers();
}
int main(int argc, char** argv) {
if (!imgOriginal.fromBmp("lena1_1.bmp")) {
cout << "Error: Cannot open 'lena1_1.bmp'. Check file path!" << endl;
system("pause");
return -1;
}
imgBinary = binarizeMatrix(imgOriginal, 127);
imgEroded = erodeMatrix(imgBinary);
imgEdges = findEdgesXor(imgBinary, imgEroded);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(imgOriginal.width * 2, imgOriginal.height * 2);
glutCreateWindow("Laboratory 7: C++ Image Processing (XOR Borders)");
glClearColor(0.15f, 0.15f, 0.15f, 1.0f);
glutDisplayFunc(display);
cout << "OpenGL Window opened successfully. Close window to exit." << endl;
glutMainLoop();
return 0;
}