963 lines
31 KiB
C++
963 lines
31 KiB
C++
// Dear ImGui: standalone example application for DirectX 11
|
|
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
|
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
|
|
|
#include "imgui.h"
|
|
#include "imgui_impl_win32.h"
|
|
#include "imgui_impl_dx11.h"
|
|
#include <d3d11.h>
|
|
#include <tchar.h>
|
|
#include "implot.h"
|
|
#include <array>
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include "stb_image.h"
|
|
|
|
struct Pixel {
|
|
int x;
|
|
int y;
|
|
float value;
|
|
|
|
Pixel(int x, int y, float value) : x(x), y(y), value(value) {}
|
|
};
|
|
|
|
static ID3D11Device* g_pd3dDevice = nullptr;
|
|
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
|
|
static IDXGISwapChain* g_pSwapChain = nullptr;
|
|
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
|
|
|
|
bool CreateDeviceD3D(HWND hWnd);
|
|
void CleanupDeviceD3D();
|
|
void CreateRenderTarget();
|
|
void CleanupRenderTarget();
|
|
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
unsigned char* ImageData(const char* filename, int &width, int &height, int &channels)
|
|
{
|
|
unsigned char* image_data = stbi_load(filename, &width, &height, &channels, 4);
|
|
return image_data;
|
|
}
|
|
|
|
bool LoadTextureFromFile(unsigned char* image_data, int image_width, int image_height, ID3D11ShaderResourceView** out_srv)
|
|
{
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
ZeroMemory(&desc, sizeof(desc));
|
|
desc.Width = image_width;
|
|
desc.Height = image_height;
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
desc.CPUAccessFlags = 0;
|
|
|
|
ID3D11Texture2D *pTexture = NULL;
|
|
D3D11_SUBRESOURCE_DATA subResource;
|
|
subResource.pSysMem = image_data;
|
|
subResource.SysMemPitch = desc.Width * 4;
|
|
subResource.SysMemSlicePitch = 0;
|
|
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
|
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
|
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
|
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
|
srvDesc.Texture2D.MostDetailedMip = 0;
|
|
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, out_srv);
|
|
pTexture->Release();
|
|
|
|
return true;
|
|
}
|
|
|
|
#undef max;
|
|
#undef min;
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <queue>
|
|
|
|
//void watershedSegmentation(unsigned char* image, int width, int height) {
|
|
// const float INFINITY = std::numeric_limits<float>::max();
|
|
//
|
|
// int* labels = new int[width * height];
|
|
// for (int i = 0; i < width * height; ++i) {
|
|
// labels[i] = -1;
|
|
// }
|
|
//
|
|
// float* copy = new float[width * height];
|
|
// for (int i = 0; i < width * height; ++i) {
|
|
// copy[i] = static_cast<float>(image[i]);
|
|
// }
|
|
//
|
|
// std::priority_queue<Pixel, std::vector<Pixel>, [](const Pixel& p1, const Pixel& p2) {
|
|
// return p1.value < p2.value;
|
|
// }> queue;
|
|
//
|
|
// for (int i = 0; i < width * height; ++i) {
|
|
// queue.push(Pixel(i % width, i / width, copy[i]));
|
|
// }
|
|
//
|
|
// while (!queue.empty()) {
|
|
// Pixel pixel = queue.top();
|
|
// queue.pop();
|
|
//
|
|
// int x = pixel.x;
|
|
// int y = pixel.y;
|
|
// int index = y * width + x;
|
|
//
|
|
// for (int i = -1; i <= 1; ++i) {
|
|
// for (int j = -1; j <= 1; ++j) {
|
|
// int nx = x + i;
|
|
// int ny = y + j;
|
|
// int nIndex = ny * width + nx;
|
|
//
|
|
// if (nx >= 0 && nx < width && ny >= 0 && ny < height && labels[nIndex] != -1) {
|
|
// if (labels[index] == -1) {
|
|
// labels[index] = labels[nIndex];
|
|
// } else if (labels[index] != labels[nIndex]) {
|
|
// labels[index] = -2;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (labels[index] == -1) {
|
|
// labels[index] = index;
|
|
//
|
|
// copy[index] = INFINITY;
|
|
//
|
|
// for (int i = -1; i <= 1; ++i) {
|
|
// for (int j = -1; j <= 1; ++j) {
|
|
// int nx = x + i;
|
|
// int ny = y + j;
|
|
// int nIndex = ny * width + nx;
|
|
//
|
|
// if (nx >= 0 && nx < width && ny >= 0 && ny < height && labels[nIndex] == -1) {
|
|
// queue.push(Pixel(nx, ny, copy[nIndex]));
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// for (int i = 0; i < width * height; ++i) {
|
|
// if (labels[i] == -2) {
|
|
// image[i] = static_cast<unsigned char>(INFINITY);
|
|
// }
|
|
// }
|
|
//
|
|
// delete[] labels;
|
|
// delete[] copy;
|
|
//}
|
|
//
|
|
//void dilation(unsigned char* image, int width, int height) {
|
|
// int structuringElement[3][3] = {
|
|
// {0, 255, 0},
|
|
// {255, 255, 255},
|
|
// {0, 255, 0}
|
|
// };
|
|
//
|
|
// for (int y = 0; y < height; ++y) {
|
|
// for (int x = 0; x < width; ++x) {
|
|
// int maxPixelValue = 0;
|
|
// for (int j = -1; j <= 1; ++j) {
|
|
// for (int i = -1; i <= 1; ++i) {
|
|
// if (x + i >= 0 && x + i < width && y + j >= 0 && y + j < height) {
|
|
// int imagePixelValue = image[(y + j) * width + (x + i)];
|
|
// int structuringElementValue = structuringElement[j + 1][i + 1];
|
|
// int newPixelValue = imagePixelValue + structuringElementValue;
|
|
// if (newPixelValue > maxPixelValue) {
|
|
// maxPixelValue = newPixelValue;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// image[y * width + x] = static_cast<unsigned char>(maxPixelValue > 255 ? 255 : maxPixelValue);
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
|
|
void erosion(unsigned char* image, int width, int height) {
|
|
int structuringElement[3][3] = {
|
|
{0, 255, 0},
|
|
{255, 255, 255},
|
|
{0, 255, 0}
|
|
};
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
for (int x = 0; x < width; ++x) {
|
|
int minPixelValue = 255;
|
|
for (int j = -1; j <= 1; ++j) {
|
|
for (int i = -1; i <= 1; ++i) {
|
|
if (x + i >= 0 && x + i < width && y + j >= 0 && y + j < height) {
|
|
int imagePixelValue = image[(y + j) * width + (x + i)];
|
|
int structuringElementValue = structuringElement[j + 1][i + 1];
|
|
int newPixelValue = imagePixelValue - structuringElementValue;
|
|
if (newPixelValue < minPixelValue) {
|
|
minPixelValue = newPixelValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
image[y * width + x] = static_cast<unsigned char>(minPixelValue < 0 ? 0 : minPixelValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hitAndMiss(unsigned char* image, int width, int height) {
|
|
int structuringElement1[3][3] = {
|
|
{0, 0, 0},
|
|
{255, 255, 0},
|
|
{0, 255, 0}
|
|
};
|
|
|
|
int structuringElement2[3][3] = {
|
|
{255, 255, 255},
|
|
{0, 0, 255},
|
|
{0, 0, 0}
|
|
};
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
for (int x = 0; x < width; ++x) {
|
|
bool match1 = true;
|
|
bool match2 = true;
|
|
for (int j = -1; j <= 1; ++j) {
|
|
for (int i = -1; i <= 1; ++i) {
|
|
if (x + i >= 0 && x + i < width && y + j >= 0 && y + j < height) {
|
|
int imagePixelValue = image[(y + j) * width + (x + i)];
|
|
int structuringElement1Value = structuringElement1[j + 1][i + 1];
|
|
int structuringElement2Value = structuringElement2[j + 1][i + 1];
|
|
if (structuringElement1Value == 255 && imagePixelValue != 255) {
|
|
match1 = false;
|
|
}
|
|
if (structuringElement2Value == 255 && imagePixelValue != 0) {
|
|
match2 = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
image[y * width + x] = (match1 && match2) ? 255 : 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void thickening(unsigned char* image, int width, int height) {
|
|
int structuringElement[3][3] = {
|
|
{0, 255, 0},
|
|
{255, 255, 255},
|
|
{0, 255, 0}
|
|
};
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
for (int x = 0; x < width; ++x) {
|
|
if (image[y * width + x] == 255) {
|
|
for (int j = -1; j <= 1; ++j) {
|
|
for (int i = -1; i <= 1; ++i) {
|
|
if (x + i >= 0 && x + i < width && y + j >= 0 && y + j < height) {
|
|
int structuringElementValue = structuringElement[j + 1][i + 1];
|
|
if (structuringElementValue == 255) {
|
|
image[(y + j) * width + (x + i)] = 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void thinning(unsigned char* image, int width, int height) {
|
|
int structuringElement[3][3] = {
|
|
{0, 255, 0},
|
|
{255, 255, 255},
|
|
{0, 255, 0}
|
|
};
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
for (int x = 0; x < width; ++x) {
|
|
if (image[y * width + x] == 255) {
|
|
bool match = true;
|
|
for (int j = -1; j <= 1; ++j) {
|
|
for (int i = -1; i <= 1; ++i) {
|
|
if (x + i >= 0 && x + i < width && y + j >= 0 && y + j < height) {
|
|
int imagePixelValue = image[(y + j) * width + (x + i)];
|
|
int structuringElementValue = structuringElement[j + 1][i + 1];
|
|
if (structuringElementValue == 255 && imagePixelValue != 255) {
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!match) {
|
|
break;
|
|
}
|
|
}
|
|
image[y * width + x] = match ? 255 : 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void rgbToHsl(float r, float g, float b, float &_h, float &_s, float &_l)
|
|
{
|
|
r /= 255.f;
|
|
g /= 255.f;
|
|
b /= 255.f;
|
|
float max = std::max({r, g, b});
|
|
float min = std::min({r, g, b});
|
|
float h, s, l = (max + min) / 2.f;
|
|
|
|
if(max - min < .001f)
|
|
{
|
|
h = s = 0.f;
|
|
}
|
|
else
|
|
{
|
|
float d = max - min;
|
|
s = l > 0.5f ? d / (2.f - max - min) : d / (max + min);
|
|
if (max - r < .001f)
|
|
{
|
|
h = (g - b) / d + (g < b ? 6.f : 0.f);
|
|
}
|
|
else if (max - g < .001f)
|
|
{
|
|
h = (b - r) / d + 2.f;
|
|
}
|
|
else if (max - b < .001f)
|
|
{
|
|
h = (r - g) / d + 4.f;
|
|
}
|
|
h /= 6;
|
|
}
|
|
|
|
_h = h;
|
|
_s = s;
|
|
_l = l;
|
|
}
|
|
|
|
void hslToRgb(float h, float s, float l, int &_r, int &_g, int &_b){
|
|
float r, g, b = 0.f;
|
|
|
|
if(s < .001f)
|
|
{
|
|
r = g = b = l;
|
|
}
|
|
else
|
|
{
|
|
auto hue2rgb = [](float p, float q, float t) {
|
|
if (t < 0) t += 1.f;
|
|
if (t > 1) t -= 1.f;
|
|
if (t < 1.f / 6.f) return p + (q - p) * 6.f * t;
|
|
if (t < 1.f / 2.f) return q;
|
|
if (t < 2.f / 3.f) return p + (q - p) * (2.f / 3.f - t) * 6.f;
|
|
return p;
|
|
};
|
|
|
|
float q = l < 0.5f ? l * (1.f + s) : l + s - l * s;
|
|
float p = 2.f * l - q;
|
|
r = hue2rgb(p, q, h + 1.f/3.f);
|
|
g = hue2rgb(p, q, h);
|
|
b = hue2rgb(p, q, h - 1.f/3.f);
|
|
}
|
|
|
|
_r = std::clamp(int(r * 255), 0, 255);
|
|
_g = std::clamp(int(g * 255), 0, 255);
|
|
_b = std::clamp(int(b * 255), 0, 255);
|
|
}
|
|
|
|
void InvertImageColors(unsigned char* imageData, int width, int height)
|
|
{
|
|
for (int y = 0; y < height; ++y)
|
|
{
|
|
for (int x = 0; x < width; ++x)
|
|
{
|
|
int index = (y * width + x) * 4;
|
|
imageData[index] = 255 - imageData[index];
|
|
imageData[index + 1] = 255 - imageData[index + 1];
|
|
imageData[index + 2] = 255 - imageData[index + 2];
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sepia(unsigned char* imageData, int width, int height)
|
|
{
|
|
for (int y = 0; y < height; ++y)
|
|
{
|
|
for (int x = 0; x < width; ++x)
|
|
{
|
|
int index = (y * width + x) * 4;
|
|
imageData[index] = int(std::min((imageData[index] * .393f) + (imageData[index + 1] * .769f) + (imageData[index + 2] * .189f), 255.f));
|
|
imageData[index + 1] = int(std::min((imageData[index] * .349f) + (imageData[index + 1] * .686f) + (imageData[index + 2] * .168f), 255.f));
|
|
imageData[index + 2] = int(std::min((imageData[index] * .272f) + (imageData[index + 1] * .534f) + (imageData[index + 2] * .131f), 255.f));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Saturation(unsigned char* imageData, int width, int height, float scale)
|
|
{
|
|
float h, s, l = 0.f;
|
|
|
|
for (int y = 0; y < height; ++y)
|
|
{
|
|
for (int x = 0; x < width; ++x)
|
|
{
|
|
int index = (y * width + x) * 4;
|
|
int r = imageData[index];
|
|
int b = imageData[index + 1];
|
|
int g = imageData[index + 2];
|
|
rgbToHsl(r, g, b, h, s, l);
|
|
s *= scale;
|
|
hslToRgb(h, s, l, r, g, b);
|
|
imageData[index] = r;
|
|
imageData[index + 1] = g;
|
|
imageData[index + 2] = b;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Light(unsigned char* imageData, int width, int height, float scale)
|
|
{
|
|
float h, s, l = 0.f;
|
|
for (int y = 0; y < height; ++y)
|
|
{
|
|
for (int x = 0; x < width; ++x)
|
|
{
|
|
int index = (y * width + x) * 4;
|
|
int r = imageData[index];
|
|
int b = imageData[index + 1];
|
|
int g = imageData[index + 2];
|
|
rgbToHsl(r, g, b, h, s, l);
|
|
l *= scale;
|
|
hslToRgb(h, s, l, r, g, b);
|
|
imageData[index] = r;
|
|
imageData[index + 1] = g;
|
|
imageData[index + 2] = b;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::array<std::array<float, 7>, 7> KernelFunction(float sigma)
|
|
{
|
|
const int kernel_size = 7;
|
|
std::array<std::array<float, 7>, 7> kernel;
|
|
float sum = 0.0f;
|
|
|
|
for (int i = 0; i < kernel_size; i++) {
|
|
for (int j = 0; j < kernel_size; j++) {
|
|
int x = i - kernel_size / 2;
|
|
int y = j - kernel_size / 2;
|
|
kernel[i][j] = exp(-(x * x + y * y) / (2 * sigma * sigma));
|
|
sum += kernel[i][j];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < kernel_size; i++) {
|
|
for (int j = 0; j < kernel_size; j++) {
|
|
kernel[i][j] /= sum;
|
|
}
|
|
}
|
|
return kernel;
|
|
}
|
|
|
|
void GaussianKernel(unsigned char* imageData, int width, int height, int channels, int kernel_size, std::array<std::array<float, 7>, 7> kernel)
|
|
{
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
for (int c = 0; c < channels; c++) {
|
|
float sum = 0.0f;
|
|
for (int i = 0; i < kernel_size; i++) {
|
|
for (int j = 0; j < kernel_size; j++) {
|
|
int px = x + i - kernel_size / 2;
|
|
int py = y + j - kernel_size / 2;
|
|
if (px < 0 || px >= width || py < 0 || py >= height) {
|
|
continue;
|
|
}
|
|
sum += kernel[i][j] * imageData[(py * width + px) * channels + c];
|
|
}
|
|
}
|
|
imageData[(y * width + x) * channels + c] = (unsigned char)sum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned char* greyScale(const unsigned char* image, unsigned char* grayscaleImage, int width, int height, int numChannels) {
|
|
for (int i = 0; i < width * height; ++i) {
|
|
unsigned char r = image[i * numChannels];
|
|
unsigned char g = image[i * numChannels + 1];
|
|
unsigned char b = image[i * numChannels + 2];
|
|
|
|
grayscaleImage[i] = static_cast<unsigned char>(0.2989 * r + 0.587 * g + 0.114 * b);
|
|
}
|
|
return grayscaleImage;
|
|
}
|
|
|
|
void binaryTransform(unsigned char* grayscaleImage, unsigned char* binaryImage, int width, int height, int threshold) {
|
|
for (int i = 0; i < width * height; ++i) {
|
|
binaryImage[i] = (grayscaleImage[i] > threshold) ? 255 : 0;
|
|
}
|
|
}
|
|
|
|
//void binaryTransform(const unsigned char* image, unsigned char* binaryImage, int threshold, int width, int height, int channels)
|
|
//{
|
|
// for (int i = 0; i < width * height; i++) {
|
|
// unsigned char brightness = 0.2126f * image[i * channels] + 0.7152f * image[i * channels + 1] + 0.0722f * image[i * channels + 2];
|
|
//
|
|
// if (brightness < threshold) {
|
|
// binaryImage[i] = 0;
|
|
// }
|
|
// else {
|
|
// binaryImage[i] = 255;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
int calculateOtsuThreshold(const unsigned char* image, int width, int height) {
|
|
|
|
int histogram[256] = {0};
|
|
for (int i = 0; i < width * height; ++i) {
|
|
histogram[image[i]]++;
|
|
}
|
|
|
|
int totalPixels = width * height;
|
|
float sum = 0.0;
|
|
for (int i = 0; i < 256; ++i) {
|
|
sum += i * histogram[i];
|
|
}
|
|
|
|
float sumB = 0.0;
|
|
int wB = 0;
|
|
int wF = 0;
|
|
float maxVariance = 0.0;
|
|
int threshold = 0;
|
|
|
|
for (int i = 0; i < 256; ++i) {
|
|
wB += histogram[i];
|
|
if (wB == 0) continue;
|
|
|
|
wF = totalPixels - wB;
|
|
if (wF == 0) break;
|
|
|
|
sumB += i * histogram[i];
|
|
|
|
float meanB = sumB / wB;
|
|
float meanF = (sum - sumB) / wF;
|
|
|
|
float betweenVariance = wB * wF * (meanB - meanF) * (meanB - meanF);
|
|
|
|
if (betweenVariance > maxVariance) {
|
|
maxVariance = betweenVariance;
|
|
threshold = i;
|
|
}
|
|
}
|
|
|
|
return threshold;
|
|
}
|
|
|
|
void hough_transform(const unsigned char* input_image, int width, int height, int threshold, unsigned char* hough_image, int channel)
|
|
{
|
|
int hough_width = 2 * (width + height);
|
|
int hough_height = 180;
|
|
int* hough_space = new int[hough_width * hough_height];
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
if (input_image[(y * width + x)] > threshold)
|
|
{
|
|
for (int theta = 0; theta < hough_height; theta++)
|
|
{
|
|
double angle = theta * (3.14 / 180.0);
|
|
int rho = static_cast<int>((x * cos(angle)) + (y * sin(angle)));
|
|
rho += (hough_width / 2);
|
|
hough_space[(theta * hough_width) + rho]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int max_value = 0;
|
|
for (int i = 0; i < hough_width * hough_height; i++)
|
|
{
|
|
if (hough_space[i] > max_value)
|
|
{
|
|
max_value = hough_space[i];
|
|
}
|
|
}
|
|
|
|
hough_image = new unsigned char[hough_width * hough_height * channel];
|
|
for (int y = 0; y < hough_height; y++)
|
|
{
|
|
for (int x = 0; x < hough_width; x++)
|
|
{
|
|
int value = static_cast<int>(255.0 * hough_space[(y * hough_width) + x] / max_value);
|
|
hough_image[3 * (y * hough_width + x)] = value;
|
|
hough_image[3 * (y * hough_width + x) + 1] = value;
|
|
hough_image[3 * (y * hough_width + x) + 2] = value;
|
|
}
|
|
}
|
|
delete[] hough_space;
|
|
}
|
|
|
|
int main(int, char**)
|
|
{
|
|
WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
|
|
::RegisterClassExW(&wc);
|
|
HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr);
|
|
|
|
if (!CreateDeviceD3D(hwnd))
|
|
{
|
|
CleanupDeviceD3D();
|
|
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
|
return 1;
|
|
}
|
|
|
|
::ShowWindow(hwnd, SW_SHOWDEFAULT);
|
|
::UpdateWindow(hwnd);
|
|
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImPlot::CreateContext();
|
|
//ImPlot::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
ImGui_ImplWin32_Init(hwnd);
|
|
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
|
|
|
|
bool main_window = true;
|
|
bool show_another_window = false;
|
|
bool image_window = false;
|
|
bool histogram = false;
|
|
char image_location[MAX_PATH] = {};
|
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
|
|
|
bool negative = false;
|
|
bool sepia = false;
|
|
bool saturation = false;
|
|
bool blure = false;
|
|
float scaleS = 0.f;
|
|
bool light = false;
|
|
float scaleL = 0.f;
|
|
float sigma = 2.0f;
|
|
bool binary = false;
|
|
bool hough = false;
|
|
bool thining = false;
|
|
bool thick = false;
|
|
bool hitmiss = false;
|
|
bool erosi = false;
|
|
bool dil = false;
|
|
|
|
bool done = false;
|
|
|
|
int* colorR = nullptr;
|
|
int* colorG = nullptr;
|
|
int* colorB = nullptr;
|
|
|
|
static float sliderValue = 0.5f;
|
|
float xMin = 0;
|
|
float xMax = 256 * sliderValue;
|
|
|
|
while (!done)
|
|
{
|
|
MSG msg;
|
|
while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
|
|
{
|
|
::TranslateMessage(&msg);
|
|
::DispatchMessage(&msg);
|
|
if (msg.message == WM_QUIT)
|
|
done = true;
|
|
}
|
|
if (done)
|
|
break;
|
|
|
|
ImGui_ImplDX11_NewFrame();
|
|
ImGui_ImplWin32_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
if (main_window)
|
|
{
|
|
static float f = 0.0f;
|
|
static int counter = 0;
|
|
|
|
ImGui::Begin("App");
|
|
ImGui::InputText("Image location", image_location, sizeof(image_location), image_window ? ImGuiInputTextFlags_ReadOnly : 0);
|
|
|
|
if (!image_window)
|
|
{
|
|
if (ImGui::Button("Load Image"))
|
|
image_window = true;
|
|
}
|
|
else
|
|
{
|
|
if (ImGui::Button("Close Image"))
|
|
image_window = false;
|
|
|
|
if (colorR != NULL && colorB != NULL && colorG != NULL)
|
|
{
|
|
delete[] colorR;
|
|
delete[] colorG;
|
|
delete[] colorB;
|
|
|
|
colorR = NULL;
|
|
colorG = NULL;
|
|
colorB = NULL;
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
if (image_window)
|
|
{
|
|
int my_image_width = 0;
|
|
int my_image_height = 0;
|
|
int my_image_channels = 0;
|
|
unsigned char* image_data = ImageData(image_location, my_image_width, my_image_height, my_image_channels);
|
|
unsigned char* grayscaleImage = new unsigned char[my_image_width * my_image_height * my_image_channels];
|
|
unsigned char* binaryImage = new unsigned char[my_image_width * my_image_height * my_image_channels];
|
|
unsigned char* hough_image = nullptr;
|
|
if (colorR == NULL && colorB == NULL && colorG == NULL) {
|
|
colorR = new int[256];
|
|
colorG = new int[256];
|
|
colorB = new int[256];
|
|
|
|
int place = 0;
|
|
|
|
for (int i = 0; i < my_image_width * my_image_height * my_image_channels; i += my_image_channels) {
|
|
colorR[image_data[i]]++;
|
|
colorG[image_data[i + 1]]++;
|
|
colorB[image_data[i + 2]]++;
|
|
}
|
|
}
|
|
|
|
|
|
if (negative)
|
|
{
|
|
InvertImageColors(image_data, my_image_width, my_image_height);
|
|
}
|
|
if (sepia)
|
|
{
|
|
Sepia(image_data, my_image_width, my_image_height);
|
|
}
|
|
if (saturation)
|
|
{
|
|
Saturation(image_data, my_image_width, my_image_height, scaleS);
|
|
}
|
|
if (light)
|
|
{
|
|
Light(image_data, my_image_width, my_image_height, scaleL);
|
|
}
|
|
if (blure)
|
|
{
|
|
GaussianKernel(image_data, my_image_width, my_image_height, my_image_channels, 7, KernelFunction(sigma));
|
|
}
|
|
if (binary)
|
|
{
|
|
binaryTransform(greyScale(image_data, grayscaleImage, my_image_width, my_image_height, my_image_channels), binaryImage, my_image_width, my_image_height, calculateOtsuThreshold(image_data, my_image_width, my_image_height));
|
|
}
|
|
if (hough)
|
|
{
|
|
hough_transform(image_data, my_image_width, my_image_height, 125, hough_image, my_image_channels);
|
|
}
|
|
if (thining)
|
|
{
|
|
thinning(image_data, my_image_width, my_image_height);
|
|
}
|
|
if (thick)
|
|
{
|
|
thickening(image_data, my_image_width, my_image_height);
|
|
}
|
|
if (hitmiss)
|
|
{
|
|
hitAndMiss(image_data, my_image_width, my_image_height);
|
|
}
|
|
if (erosi)
|
|
{
|
|
erosion(image_data, my_image_width, my_image_height);
|
|
}
|
|
if (dil)
|
|
{
|
|
dilation(image_data, my_image_width, my_image_height);
|
|
}
|
|
if (histogram)
|
|
{
|
|
ImGui::Begin("Histogram");
|
|
float xMin = 0;
|
|
float xMax = 256 * sliderValue;
|
|
/*ImPlot::SetNextPlotLimitsX(xMin, xMax, ImGuiCond_Always);*/
|
|
if (ImPlot::BeginPlot("RGB Histogram")) {
|
|
ImPlot::PlotHistogram("Red", colorR, 256);
|
|
ImPlot::PlotHistogram("Green", colorG, 256);
|
|
ImPlot::PlotHistogram("Blue", colorB, 256);
|
|
ImPlot::EndPlot();
|
|
}
|
|
ImGui::SliderFloat("X-axis Range", &sliderValue, 0.0f, 1.0f);
|
|
ImGui::End();
|
|
}
|
|
if (!image_data)
|
|
{
|
|
ImGui::Begin("Error");
|
|
ImGui::Text("Wrong path");
|
|
if (ImGui::Button("Ok"))
|
|
image_window = false;
|
|
ImGui::End();
|
|
}
|
|
else
|
|
{
|
|
ID3D11ShaderResourceView* my_texture = NULL;
|
|
if (binary == TRUE) {
|
|
bool ret = LoadTextureFromFile(binaryImage, my_image_width, my_image_height, &my_texture);
|
|
delete[] binaryImage;
|
|
delete[] grayscaleImage;
|
|
stbi_image_free(image_data);
|
|
}
|
|
else if (hough == TRUE) {
|
|
bool ret = LoadTextureFromFile(hough_image, my_image_width, my_image_height, &my_texture);
|
|
delete[] hough_image;
|
|
stbi_image_free(image_data);
|
|
}
|
|
else {
|
|
bool ret = LoadTextureFromFile(image_data, my_image_width, my_image_height, &my_texture);
|
|
stbi_image_free(image_data);
|
|
}
|
|
ImGui::Begin("Image window");
|
|
ImGui::Image((void*)my_texture, ImVec2(my_image_width, my_image_height));
|
|
ImGui::End();
|
|
ImGui::Begin("Histograms");
|
|
ImGui::Checkbox("RGB", &histogram);
|
|
ImGui::End();
|
|
ImGui::Begin("Operatory");
|
|
ImGui::Checkbox("Dylacja", &dil);
|
|
ImGui::Checkbox("Erozja", &erosi);
|
|
ImGui::Checkbox("Hit and Miss", &hitmiss);
|
|
ImGui::Checkbox("Pogrubianie", &thick);
|
|
ImGui::Checkbox("Thining", &thining);
|
|
ImGui::End();
|
|
ImGui::Begin("Filters");
|
|
ImGui::Checkbox("Negative", &negative);
|
|
ImGui::Checkbox("Sepia", &sepia);
|
|
ImGui::Checkbox("Saturation", &saturation);
|
|
ImGui::SameLine();
|
|
ImGui::SliderFloat(" ", &scaleS, 0.f, 1.f);
|
|
ImGui::Checkbox("Light", &light);
|
|
ImGui::SameLine();
|
|
ImGui::SliderFloat(" ", &scaleL, 0.f, 1.f);
|
|
ImGui::Checkbox("Blure", &blure);
|
|
ImGui::SameLine();
|
|
ImGui::SliderFloat(" ", &sigma, 0.f, 10.f);
|
|
ImGui::Checkbox("Binaryzacja", &binary);
|
|
ImGui::Checkbox("Hough transform", &hough);
|
|
ImGui::End();
|
|
}
|
|
}
|
|
|
|
ImGui::Render();
|
|
const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
|
|
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
|
|
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
|
|
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
g_pSwapChain->Present(1, 0);
|
|
}
|
|
|
|
ImGui_ImplDX11_Shutdown();
|
|
ImGui_ImplWin32_Shutdown();
|
|
ImPlot::DestroyContext();
|
|
ImGui::DestroyContext();
|
|
|
|
CleanupDeviceD3D();
|
|
::DestroyWindow(hwnd);
|
|
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool CreateDeviceD3D(HWND hWnd)
|
|
{
|
|
DXGI_SWAP_CHAIN_DESC sd;
|
|
ZeroMemory(&sd, sizeof(sd));
|
|
sd.BufferCount = 2;
|
|
sd.BufferDesc.Width = 0;
|
|
sd.BufferDesc.Height = 0;
|
|
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
sd.BufferDesc.RefreshRate.Numerator = 60;
|
|
sd.BufferDesc.RefreshRate.Denominator = 1;
|
|
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
|
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
sd.OutputWindow = hWnd;
|
|
sd.SampleDesc.Count = 1;
|
|
sd.SampleDesc.Quality = 0;
|
|
sd.Windowed = TRUE;
|
|
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
|
|
|
UINT createDeviceFlags = 0;
|
|
D3D_FEATURE_LEVEL featureLevel;
|
|
const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
|
|
HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
|
|
if (res == DXGI_ERROR_UNSUPPORTED)
|
|
res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
|
|
if (res != S_OK)
|
|
return false;
|
|
|
|
CreateRenderTarget();
|
|
return true;
|
|
}
|
|
|
|
void CleanupDeviceD3D()
|
|
{
|
|
CleanupRenderTarget();
|
|
if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
|
|
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
|
|
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
|
|
}
|
|
|
|
void CreateRenderTarget()
|
|
{
|
|
ID3D11Texture2D* pBackBuffer;
|
|
g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
|
|
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
|
|
pBackBuffer->Release();
|
|
}
|
|
|
|
void CleanupRenderTarget()
|
|
{
|
|
if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
|
|
}
|
|
|
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
|
|
return true;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_SIZE:
|
|
if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED)
|
|
{
|
|
CleanupRenderTarget();
|
|
g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
|
|
CreateRenderTarget();
|
|
}
|
|
return 0;
|
|
case WM_SYSCOMMAND:
|
|
if ((wParam & 0xfff0) == SC_KEYMENU)
|
|
return 0;
|
|
break;
|
|
case WM_DESTROY:
|
|
::PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
return ::DefWindowProcW(hWnd, msg, wParam, lParam);
|
|
}
|