품질 개선 방안을 계속 모색 해보고 있습니다...!
Console 프로그램으로 실행 예시는 아래와 같습니다.
#> WindowDataPdf.exe "1" "C:\Users\user\Downloads\test.pdf" "C:\Users\user\Downloads"
#include "pch.h"
#include <iostream>
#include <vector>
#include <tiff.h>
#include <tiffio.h>
#include <wrl/client.h>
namespace winrt
{
using namespace Windows::Foundation;
using namespace Windows::Storage;
using namespace Windows::Data::Pdf;
using namespace Windows::Graphics::Imaging;
}
winrt::Windows::Graphics::Imaging::SoftwareBitmap FloydSteinbergDithering(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& inputBitmap);
winrt::Windows::Graphics::Imaging::SoftwareBitmap ConvertGrayScale(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& bitmap);
std::vector<uint8_t> ConvertToWhiteAndBlack(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& bitmap, uint32_t& width, uint32_t& height, uint8_t threshold = 128);
struct __declspec(uuid("5b0d3235-4dba-4d44-865e-8f1d0e4fd04d")) IMemoryBufferByteAccess : public IUnknown {
virtual HRESULT __stdcall GetBuffer(BYTE** value, UINT32* capacity) = 0;
};
/*
*
* [0]. id
* [1]. source file fullpath
* [2]. bmp files save path
*
* return value
* 결과|PageCount|DESC
*
*/
int wmain(int argc, wchar_t* argv[])
{
if (argc != 4) { // 첫번째 매개변수는 실행되는 프로세스 명
std::wcout << L"FAILED|0|매개변수가 3개가 전달 되지 않았습니다." << std::endl;
return -1;
}
winrt::hstring Id;
winrt::hstring sourcePath;
winrt::hstring savePath;
//std::wstring tempStr;
//std::wstring replaceStr;
for (int i = 0; i < argc; i++) {
//tempStr = argv[i];
//replaceStr = std::regex_replace(tempStr, std::wregex(L"\\\\"), L"/");
if (i == 1) Id = winrt::hstring(argv[i]);
if (i == 2) sourcePath = winrt::hstring(argv[i]);
if (i == 3) savePath = winrt::hstring(argv[i]);
}
//std::wcout << Id.c_str() << "," << sourcePath.c_str() << "," << savePath.c_str() << std::endl;
winrt::init_apartment();
//Uri uri(L"http://aka.ms/cppwinrt");
//printf("Hello, %ls!\n", uri.AbsoluteUri().c_str());
//try {
winrt::Windows::Storage::StorageFile storagePdfFile = winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(sourcePath).get();
winrt::Windows::Data::Pdf::PdfDocument pdfDocument = winrt::Windows::Data::Pdf::PdfDocument::LoadFromFileAsync(storagePdfFile, L"").get();
int nCount = pdfDocument.PageCount();
int nPageIndex = 0;
/*
try {
StorageFolder storageFolder = ApplicationData::Current().LocalFolder();
}
catch (hresult_error const& e) {
wprintf_s(L"error 0x%08X - %ls\n", e.code() , e.message().c_str());
}
*/
winrt::Windows::Storage::StorageFolder storageFolder = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(savePath).get();
TIFF* tiff = TIFFOpenW(L"C:\\Users\\user\\Downloads\\output.tif", "w");
while (nPageIndex < nCount) {
winrt::Windows::Data::Pdf::PdfPage pdfPage = pdfDocument.GetPage(nPageIndex);
winrt::Windows::Storage::Streams::IRandomAccessStream outputStream = winrt::Windows::Storage::Streams::InMemoryRandomAccessStream();
pdfPage.RenderToStreamAsync(outputStream).get();
pdfPage.Close();
// 비트맵으로 변환
auto decoder = winrt::Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(outputStream).get();
uint32_t width, height , faxTiffWidth , faxTiffHeight;
winrt::Windows::Graphics::Imaging::SoftwareBitmap grayBitmap = FloydSteinbergDithering(decoder.GetSoftwareBitmapAsync(
winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Bgra8,
winrt::Windows::Graphics::Imaging::BitmapAlphaMode::Ignore
).get());
std::vector<uint8_t> bitmapData = ConvertToWhiteAndBlack(grayBitmap, width ,height , 128);
std::wcout << bitmapData.size() << std::endl;
// TIFF 파일 생성
if (!tiff)
std::wcout << L"FAILED OPEN TO TIFF FILE" << std::endl;
/*
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4); // BGRA (4 channels)
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
*/
faxTiffWidth = 1728;
faxTiffHeight = 2292;
uint32_t rowSize = (width + 7) / 8; //줄당 바이트 수 ( 1비트 씩 )
uint32_t faxTiffRowSize = (faxTiffWidth + 7) / 8;
if (bitmapData.size() < rowSize * height) {
std::wcout << "BITMAPDATA SIZE IS INSUFFICIENT FOR THE IMAGE DIMENTIONS" << std::endl;
return -1;
}
//이미지 크기를 늘린 데이터를 저장할 Vector
std::vector<uint8_t> faxTiffData(faxTiffHeight * faxTiffRowSize, 0xFF); // 하얀색으로 채우기
// 확대 비율 계산
double scaleX = static_cast<double>(width) / faxTiffWidth;
double scaleY = static_cast<double>(height) / faxTiffHeight;
// 확대 진행
for (uint32_t newY = 0; newY < faxTiffHeight; ++newY) {
for (uint32_t newX = 0; newX < faxTiffWidth; ++newX) {
//원본 이미지에서 대응되는 좌표 계산
uint32_t origX = static_cast<uint32_t>(newX * scaleX);
uint32_t origY = static_cast<uint32_t>(newY * scaleY);
//원본 데이터의 픽셀 값을 가져옴
uint32_t origIndex = origY * rowSize + origX / 8;
uint8_t origPixel = (bitmapData[origIndex] >> (7 - (origX % 8))) & 1;
// vector에 픽셀 값 저장
uint32_t faxTiffIndex = newY * faxTiffRowSize + newX / 8;
if (origPixel == 0)
faxTiffData[faxTiffIndex] |= (0x80 >> (newX % 8)); //1로
else
faxTiffData[faxTiffIndex] &= ~(0x80 >> (newX % 8)); // 0으로
}
}
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, faxTiffWidth);
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, faxTiffHeight);
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 1);
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4); // CCITT Group 4 압축
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE); // 흰색: 1, 검은색: 0
TIFFSetField(tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); // 비트 순서
TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tiff, TIFFTAG_XRESOLUTION, static_cast<float>(204));
TIFFSetField(tiff, TIFFTAG_YRESOLUTION, static_cast<float>(196));
TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
for (uint32_t row = 0; row < faxTiffHeight; ++row)
TIFFWriteScanline(tiff, &faxTiffData[row * faxTiffRowSize], row, 0);
TIFFWriteDirectory(tiff);
nPageIndex++;
}
TIFFClose(tiff);
std::wcout << "SUCCESS|" << nCount << "|SUCCESS" << std::endl;
/*
} catch (hresult_error const& e) {
std::wcout << "FAIL|" << 0 << "|" << e.message().c_str() << std::endl;
}
*/
}
winrt::Windows::Graphics::Imaging::SoftwareBitmap FloydSteinbergDithering(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& inputBitmap)
{
if (inputBitmap.BitmapPixelFormat() != winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Bgra8) {
std::wcout << "FAILED UNSUPPORTED PIXEL FORMAT" << std::endl;
}
uint32_t width = inputBitmap.PixelWidth();
uint32_t height = inputBitmap.PixelHeight();
winrt::Windows::Graphics::Imaging::SoftwareBitmap resultBitmap(winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Gray8, width, height);
auto inputBuffer = inputBitmap.LockBuffer(winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Read);
auto outputBuffer = resultBitmap.LockBuffer(winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Write);
auto inputReference = inputBuffer.CreateReference();
auto outputReference = outputBuffer.CreateReference();
uint8_t* inputData = nullptr;
uint8_t* outputData = nullptr;
uint32_t inputCapacity = 0;
uint32_t outputCapacity = 0;
auto inputByteAccess = inputReference.as<IMemoryBufferByteAccess>();
inputByteAccess->GetBuffer(&inputData, &inputCapacity);
auto outputByteAccess = outputReference.as<IMemoryBufferByteAccess>();
outputByteAccess->GetBuffer(&outputData, &outputCapacity);
std::vector<float> errorBuffer(width * height, 0.0f);
for (uint32_t y = 0; y < height; ++y) {
for (uint32_t x = 0; x < width; ++x) {
//Bgra8 에서 값 추출
uint32_t index = (y * width + x) * 4;
uint8_t blue = inputData[index];
uint8_t green = inputData[index + 1];
uint8_t red = inputData[index + 2];
//밝기 계산
float originalGray = 0.299f * red + 0.587f * green + 0.114f * blue;
float correctedGray = originalGray + errorBuffer[y * width + x];
//디더링 된 값 계산
uint8_t newPixel = correctedGray >= 128 ? 255 : 0;
//에러 계산
float error = correctedGray - newPixel;
//결과 저장
outputData[y * width + x] = newPixel;
//주변 픽셀에 에러 분산
if (x + 1 < width)
errorBuffer[y * width + (x + 1)] += error * 7 / 16.0f;
if (y + 1 < height) {
if (x > 0)
errorBuffer[(y + 1) * width + (x - 1)] += error * 3 / 16.0f;
errorBuffer[(y + 1) * width + x] += error * 5 / 16.0f;
if (x + 1 < width)
errorBuffer[(y + 1) * width + (x + 1)] += error * 1 / 16.0f;
}
}
}
return resultBitmap;
}
winrt::Windows::Graphics::Imaging::SoftwareBitmap ConvertGrayScale(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& bitmap)
{
if (bitmap.BitmapPixelFormat() != winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Bgra8) {
std::wcout << "FAILED UNSUPPORTED PIXEL FORMAT" << std::endl;
}
uint32_t width = bitmap.PixelWidth();
uint32_t height = bitmap.PixelHeight();
auto buffer = bitmap.LockBuffer(winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Read);
winrt::Windows::Graphics::Imaging::SoftwareBitmap outBitmap(winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Gray8, width, height);
auto outBuffer = outBitmap.LockBuffer(winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Write);
auto reference = buffer.CreateReference();
auto outReference = outBuffer.CreateReference();
//Microsoft::WRL::ComPtr<IMemoryBufferByteAccess> bufferByteAccess;
//auto interop = reference.as<IMemoryBufferByteAccess>();
uint8_t* data = nullptr;
uint32_t capaCity = 0;
uint8_t* outData = nullptr;
uint32_t outCapaCity = 0;
//interop->GetBuffer(&data, &capacity);
auto memBuffer = reference.as<IMemoryBufferByteAccess>();
memBuffer->GetBuffer(&data, &capaCity);
auto outMemBuffer = outReference.as<IMemoryBufferByteAccess>();
outMemBuffer->GetBuffer(&outData, &capaCity);
//GrayScale 변환
for (uint32_t y = 0; y < height; ++y) {
for (uint32_t x = 0; x < width; ++x) {
uint32_t index = (y * width + x) * 4;
uint8_t blue = data[index];
uint8_t green = data[index + 1];
uint8_t red = data[index + 2];
//GrayScale 값 계산 ( 가중치 기반 )
uint8_t gray = static_cast<uint8_t>(0.299 * red + 0.587 * green + 0.114 * blue);
outData[y * width + x] = gray;
}
}
return outBitmap;
}
std::vector<uint8_t> ConvertToWhiteAndBlack(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& bitmap, uint32_t& width, uint32_t& height, uint8_t threshold)
{
if (bitmap.BitmapPixelFormat() != winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Gray8) {
std::wcout << "FAILED UNSUPPORTED PIXEL FORMAT" << std::endl;
}
width = bitmap.PixelWidth();
height = bitmap.PixelHeight();
auto buffer = bitmap.LockBuffer(winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Read);
uint8_t* data = nullptr;
uint32_t capaCity = 0;
auto reference = buffer.CreateReference();
auto memBuffer = reference.as<IMemoryBufferByteAccess>();
memBuffer->GetBuffer(&data, &capaCity);
uint32_t rowSize = (width + 7) / 8;
std::vector<uint8_t> bitData(rowSize * height, 0x00);
winrt::Windows::Graphics::Imaging::BitmapPlaneDescription desc = buffer.GetPlaneDescription(0);
//픽셀 데이터를 1비트로 변환
for (uint32_t y = 0; y < desc.Height; ++y) {
for (uint32_t x = 0; x < desc.Width; ++x) {
uint32_t grayIndex = y * width + x;
uint8_t grayValue = data[grayIndex];
if (grayValue >= threshold)
bitData[y * rowSize + (x / 8)] |= (0x80 >> (x % 8));
}
}
return bitData;
}
'C++' 카테고리의 다른 글
[C++/WinRt] Windows Toolkit - Windows.Data.Pdf 를 이용한 PDF To Bitmap 변환 예제 (0) | 2024.07.29 |
---|---|
코딩테스트 연습->탐욕법(Greedy)->체육복 (0) | 2021.09.01 |
코딩테스트 연습->해시->위장 (0) | 2021.08.30 |
코딩테스트연습->정렬->K번째수 (0) | 2021.07.22 |
코딩테스트연습->전화번호 목록 (0) | 2021.07.20 |