Загрузка данных



#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;
}