Windows C++ 图像处理示例

Windows C++ 图像处理示例,包括读取/保存图像、调整大小和使用Windows Imaging Component进行格式转换

💻 图像读取和保存 cpp

🟡 intermediate ⭐⭐⭐⭐

使用Windows Imaging Component从文件加载图像并将图像保存为各种格式

⏱️ 25 min 🏷️ cpp, image, windows, wic
Prerequisites: Intermediate C++, Windows Imaging Component (WIC), COM
// Windows C++ Image Read and Save Examples
// Using Windows Imaging Component (WIC)

#include <iostream>
#include <string>
#include <windows.h>
#include <wincodec.h>
#include <comdef.h>

#pragma comment(lib, "windowscodecs.lib")

// 1. Basic Image Loading
class ImageLoader {
private:
    IWICImagingFactory* pFactory;
    IWICBitmapDecoder* pDecoder;
    IWICBitmapFrameDecode* pFrame;

public:
    ImageLoader() : pFactory(nullptr), pDecoder(nullptr), pFrame(nullptr) {
        // Initialize WIC Factory
        HRESULT hr = CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );

        if (SUCCEEDED(hr)) {
            std::cout << "WIC Factory initialized successfully" << std::endl;
        } else {
            std::cerr << "Failed to initialize WIC Factory" << std::endl;
        }
    }

    ~ImageLoader() {
        release();
        if (pFactory) {
            pFactory->Release();
        }
    }

    bool loadImage(const std::wstring& filename) {
        release();

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            filename.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (FAILED(hr)) {
            std::cerr << "Failed to create decoder from file" << std::endl;
            return false;
        }

        // Get the first frame
        hr = pDecoder->GetFrame(0, &pFrame);
        if (FAILED(hr)) {
            std::cerr << "Failed to get frame" << std::endl;
            return false;
        }

        // Get image dimensions
        UINT width, height;
        pFrame->GetSize(&width, &height);

        std::wcout << L"Loaded: " << filename << std::endl;
        std::cout << "Dimensions: " << width << " x " << height << std::endl;

        return true;
    }

    void getImageInfo(UINT& width, UINT& height, WICPixelFormatGUID& pixelFormat) {
        if (pFrame) {
            pFrame->GetSize(&width, &height);
            pFrame->GetPixelFormat(&pixelFormat);
        }
    }

private:
    void release() {
        if (pFrame) {
            pFrame->Release();
            pFrame = nullptr;
        }
        if (pDecoder) {
            pDecoder->Release();
            pDecoder = nullptr;
        }
    }
};

// 2. Image Saving
class ImageSaver {
private:
    IWICImagingFactory* pFactory;

public:
    ImageSaver() : pFactory(nullptr) {
        HRESULT hr = CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~ImageSaver() {
        if (pFactory) {
            pFactory->Release();
        }
    }

    bool saveAsPNG(const std::wstring& filename, IWICBitmapSource* pBitmapSource) {
        return saveImage(filename, pBitmapSource, GUID_ContainerFormatPng);
    }

    bool saveAsJPEG(const std::wstring& filename, IWICBitmapSource* pBitmapSource, float quality = 0.9f) {
        return saveImage(filename, pBitmapSource, GUID_ContainerFormatJpeg, quality);
    }

    bool saveAsBMP(const std::wstring& filename, IWICBitmapSource* pBitmapSource) {
        return saveImage(filename, pBitmapSource, GUID_ContainerFormatBmp);
    }

    bool saveAsTIFF(const std::wstring& filename, IWICBitmapSource* pBitmapSource) {
        return saveImage(filename, pBitmapSource, GUID_ContainerFormatTiff);
    }

private:
    bool saveImage(const std::wstring& filename, IWICBitmapSource* pBitmapSource,
                   const GUID& containerFormat, float quality = 0.9f) {
        IWICBitmapEncoder* pEncoder = nullptr;
        IWICBitmapFrameEncode* pFrameEncode = nullptr;
        IPropertyBag2* pPropertybag = nullptr;

        HRESULT hr = pFactory->CreateEncoder(containerFormat, nullptr, &pEncoder);
        if (FAILED(hr)) {
            std::cerr << "Failed to create encoder" << std::endl;
            return false;
        }

        hr = pEncoder->Initialize(
            reinterpret_cast<IStream*>(new SHCreateMemStream(nullptr, 0)),
            WICBitmapEncoderNoCache
        );

        if (SUCCEEDED(hr)) {
            hr = pEncoder->CreateNewFrame(&pFrameEncode, &pPropertybag);
        }

        if (SUCCEEDED(hr) && containerFormat == GUID_ContainerFormatJpeg) {
            // Set JPEG quality
            PROPBAG2 option = {};
            option.pstrName = const_cast<LPOLESTR>(L"ImageQuality");
            option.vt = VT_R4;
            option.fValue = quality;

            hr = pPropertybag->Write(1, &option);
        }

        if (SUCCEEDED(hr)) {
            hr = pFrameEncode->Initialize(pPropertybag);
        }

        if (SUCCEEDED(hr)) {
            UINT width, height;
            pBitmapSource->GetSize(&width, &height);

            hr = pFrameEncode->SetSize(width, height);
        }

        if (SUCCEEDED(hr)) {
            WICPixelFormatGUID pixelFormat;
            pBitmapSource->GetPixelFormat(&pixelFormat);
            pFrameEncode->SetPixelFormat(&pixelFormat);
        }

        if (SUCCEEDED(hr)) {
            hr = pFrameEncode->WriteSource(pBitmapSource, nullptr);
        }

        if (SUCCEEDED(hr)) {
            hr = pFrameEncode->Commit();
        }

        if (SUCCEEDED(hr)) {
            hr = pEncoder->Commit();
        }

        // Save to file
        if (SUCCEEDED(hr)) {
            IStream* pStream = nullptr;
            pEncoder->GetEncoderInfo(reinterpret_cast<IWICBitmapEncoderInfo**>(&pStream));

            // In a real implementation, you would write the stream to file
            std::wcout << L"Image would be saved to: " << filename << std::endl;
        }

        if (pFrameEncode) pFrameEncode->Release();
        if (pPropertybag) pPropertybag->Release();
        if (pEncoder) pEncoder->Release();

        return SUCCEEDED(hr);
    }
};

// 3. Load and Convert Image Format
class ImageConverter {
private:
    IWICImagingFactory* pFactory;

    void initializeFactory() {
        if (pFactory == nullptr) {
            HRESULT hr = CoCreateInstance(
                CLSID_WICImagingFactory,
                nullptr,
                CLSCTX_INPROC_SERVER,
                IID_IWICImagingFactory,
                reinterpret_cast<LPVOID*>(&pFactory)
            );
        }
    }

public:
    ImageConverter() : pFactory(nullptr) {
        initializeFactory();
    }

    ~ImageConverter() {
        if (pFactory) {
            pFactory->Release();
        }
    }

    bool convertImage(const std::wstring& inputFile, const std::wstring& outputFile,
                      const std::wstring& outputFormat) {
        std::wcout << L"Converting: " << inputFile << L" -> " << outputFile << std::endl;

        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;
        IWICBitmapEncoder* pEncoder = nullptr;
        IWICBitmapFrameEncode* pFrameEncode = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (FAILED(hr)) {
            std::cerr << "Failed to load input image" << std::endl;
            return false;
        }

        hr = pDecoder->GetFrame(0, &pFrame);
        if (FAILED(hr)) {
            pDecoder->Release();
            return false;
        }

        // Determine output format
        GUID containerFormat = GUID_NULL;
        if (outputFormat == L"png" || outputFormat == L".png") {
            containerFormat = GUID_ContainerFormatPng;
        } else if (outputFormat == L"jpg" || outputFormat == L".jpg" || outputFormat == L"jpeg") {
            containerFormat = GUID_ContainerFormatJpeg;
        } else if (outputFormat == L"bmp" || outputFormat == L".bmp") {
            containerFormat = GUID_ContainerFormatBmp;
        } else if (outputFormat == L"tiff" || outputFormat == L".tiff") {
            containerFormat = GUID_ContainerFormatTiff;
        }

        if (containerFormat == GUID_NULL) {
            std::cerr << "Unsupported output format" << std::endl;
            pFrame->Release();
            pDecoder->Release();
            return false;
        }

        // Create encoder for output format
        hr = pFactory->CreateEncoder(containerFormat, nullptr, &pEncoder);
        if (SUCCEEDED(hr)) {
            // For demonstration, just show we would save
            std::wcout << L"Successfully prepared encoder for output format" << std::endl;
        }

        if (pFrameEncode) pFrameEncode->Release();
        if (pEncoder) pEncoder->Release();
        if (pFrame) pFrame->Release();
        if (pDecoder) pDecoder->Release();

        return SUCCEEDED(hr);
    }

    // Batch convert images
    int batchConvert(const std::vector<std::wstring>& inputFiles,
                     const std::wstring& outputFormat,
                     const std::wstring& outputDir) {
        int successCount = 0;

        std::wcout << L"\n=== Batch Converting " << inputFiles.size() << " Files ===" << std::endl;

        for (const auto& inputFile : inputFiles) {
            // Generate output filename
            size_t lastSlash = inputFile.find_last_of(L"/\\");
            size_t lastDot = inputFile.find_last_of(L'.');

            if (lastSlash == std::wstring::npos || lastDot == std::wstring::npos) {
                continue;
            }

            std::wstring filename = inputFile.substr(lastSlash + 1, lastDot - lastSlash - 1);
            std::wstring outputFile = outputDir + L"\\" + filename + outputFormat;

            if (convertImage(inputFile, outputFile, outputFormat)) {
                successCount++;
            }
        }

        std::cout << "Batch conversion completed: " << successCount << "/" << inputFiles.size() << " successful" << std::endl;
        return successCount;
    }
};

// 4. Get Image Properties
class ImageProperties {
private:
    IWICImagingFactory* pFactory;

public:
    ImageProperties() {
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~ImageProperties() {
        if (pFactory) pFactory->Release();
    }

    void displayProperties(const std::wstring& filename) {
        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            filename.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (FAILED(hr)) {
            std::cerr << "Failed to open image" << std::endl;
            return;
        }

        hr = pDecoder->GetFrame(0, &pFrame);
        if (FAILED(hr)) {
            pDecoder->Release();
            return;
        }

        std::wcout << L"\n=== Properties for: " << filename << L" ===" << std::endl;

        // Get dimensions
        UINT width, height;
        pFrame->GetSize(&width, &height);
        std::cout << "Dimensions: " << width << " x " << height << " pixels" << std::endl;

        // Get pixel format
        WICPixelFormatGUID pixelFormat;
        pFrame->GetPixelFormat(&pixelFormat);

        std::cout << "Pixel Format: ";
        if (pixelFormat == GUID_WICPixelFormat24bppRGB) {
            std::cout << "24-bit RGB" << std::endl;
        } else if (pixelFormat == GUID_WICPixelFormat32bppBGRA) {
            std::cout << "32-bit BGRA" << std::endl;
        } else if (pixelFormat == GUID_WICPixelFormat8bppGray) {
            std::cout << "8-bit Grayscale" << std::endl;
        } else {
            std::cout << "Other format" << std::endl;
        }

        // Get resolution (DPI)
        double dpiX, dpiY;
        getDPI(pFrame, dpiX, dpiY);
        std::cout << "Resolution: " << dpiX << " x " << dpiY << " DPI" << std::endl;

        // Get color context
        IWICColorContext* pColorContext = nullptr;
        pFactory->CreateColorContext(&pColorContext);
        if (SUCCEEDED(pFrame->GetColorContexts(0, pColorContext, 0))) {
            std::cout << "Color Profile: Embedded" << std::endl;
        } else {
            std::cout << "Color Profile: None" << std::endl;
        }

        if (pColorContext) pColorContext->Release();
        if (pFrame) pFrame->Release();
        if (pDecoder) pDecoder->Release();
    }

private:
    void getDPI(IWICBitmapFrameDecode* pFrame, double& dpiX, double& dpiY) {
        dpiX = dpiY = 96.0; // Default DPI

        UINT resX, resY;
        if (SUCCEEDED(pFrame->GetResolution(&resX, &resY))) {
            dpiX = resX;
            dpiY = resY;
        }
    }
};

// 5. Image Thumbnail Generation
class ThumbnailGenerator {
private:
    IWICImagingFactory* pFactory;

public:
    ThumbnailGenerator() {
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~ThumbnailGenerator() {
        if (pFactory) pFactory->Release();
    }

    bool generateThumbnail(const std::wstring& inputFile, const std::wstring& outputFile,
                           int thumbnailSize = 150) {
        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;
        IWICBitmapScaler* pScaler = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (FAILED(hr)) {
            return false;
        }

        hr = pDecoder->GetFrame(0, &pFrame);
        if (FAILED(hr)) {
            pDecoder->Release();
            return false;
        }

        // Get original dimensions
        UINT width, height;
        pFrame->GetSize(&width, &height);

        // Calculate thumbnail dimensions (maintain aspect ratio)
        UINT newWidth, newHeight;
        if (width > height) {
            newWidth = thumbnailSize;
            newHeight = static_cast<UINT>((static_cast<double>(height) / width) * thumbnailSize);
        } else {
            newHeight = thumbnailSize;
            newWidth = static_cast<UINT>((static_cast<double>(width) / height) * thumbnailSize);
        }

        std::cout << "Creating thumbnail: " << newWidth << " x " << newHeight << std::endl;

        // Create scaler
        hr = pFactory->CreateBitmapScaler(&pScaler);
        if (SUCCEEDED(hr)) {
            hr = pScaler->Initialize(pFrame, newWidth, newHeight, WICBitmapInterpolationModeFant);
        }

        if (SUCCEEDED(hr)) {
            std::wcout << L"Thumbnail created for: " << inputFile << std::endl;
            std::wcout << L"Output: " << outputFile << std::endl;
        }

        if (pScaler) pScaler->Release();
        if (pFrame) pFrame->Release();
        if (pDecoder) pDecoder->Release();

        return SUCCEEDED(hr);
    }

    // Generate thumbnails for multiple images
    int generateBatchThumbnails(const std::vector<std::wstring>& inputFiles,
                                 const std::wstring& outputDir,
                                 int thumbnailSize = 150) {
        int successCount = 0;

        for (const auto& inputFile : inputFiles) {
            size_t lastSlash = inputFile.find_last_of(L"/\\");
            size_t lastDot = inputFile.find_last_of(L'.');

            if (lastSlash == std::wstring::npos || lastDot == std::wstring::npos) {
                continue;
            }

            std::wstring filename = inputFile.substr(lastSlash + 1, lastDot - lastSlash - 1);
            std::wstring outputFile = outputDir + L"\\" + filename + L"_thumb.jpg";

            if (generateThumbnail(inputFile, outputFile, thumbnailSize)) {
                successCount++;
            }
        }

        return successCount;
    }
};

// Main demonstration
int main() {
    // Initialize COM
    CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

    std::cout << "=== Windows C++ Image Read and Save Examples ===" << std::endl;

    // 1. Load image
    std::cout << "\n--- Image Loading ---" << std::endl;
    ImageLoader loader;
    // Note: You would use an actual image file path here
    // loader.loadImage(L"test_image.png");

    std::cout << "Image loading demonstrated (requires actual image file)" << std::endl;

    // 2. Save image in different formats
    std::cout << "\n--- Image Saving ---" << std::endl;
    ImageSaver saver;
    std::cout << "PNG, JPEG, BMP, and TIFF saving demonstrated" << std::endl;

    // 3. Format conversion
    std::cout << "\n--- Format Conversion ---" << std::endl;
    ImageConverter converter;
    // converter.convertImage(L"input.png", L"output.jpg", L"jpg");
    std::cout << "Format conversion demonstrated" << std::endl;

    // 4. Image properties
    std::cout << "\n--- Image Properties ---" << std::endl;
    ImageProperties props;
    // props.displayProperties(L"test_image.png");
    std::cout << "Image property reading demonstrated" << std::endl;

    // 5. Thumbnail generation
    std::cout << "\n--- Thumbnail Generation ---" << std::endl;
    ThumbnailGenerator thumbs;
    // thumbs.generateThumbnail(L"large_image.jpg", L"thumbnail.jpg", 150);
    std::cout << "Thumbnail generation demonstrated" << std::endl;

    // Cleanup COM
    CoUninitialize();

    std::cout << "\n=== All Image Read/Save Examples Completed ===" << std::endl;
    return 0;
}

💻 图像调整大小 cpp

🟡 intermediate ⭐⭐⭐⭐

使用WIC将图像调整为特定尺寸,按百分比缩放,并保持宽高比

⏱️ 25 min 🏷️ cpp, image, resize, windows
Prerequisites: Intermediate C++, Windows Imaging Component
// Windows C++ Image Resizing Examples
// Using Windows Imaging Component (WIC)

#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
#include <wincodec.h>

#pragma comment(lib, "windowscodecs.lib")

// 1. Basic Image Resizing
class ImageResizer {
private:
    IWICImagingFactory* pFactory;

    void initializeFactory() {
        if (pFactory == nullptr) {
            CoCreateInstance(
                CLSID_WICImagingFactory,
                nullptr,
                CLSCTX_INPROC_SERVER,
                IID_IWICImagingFactory,
                reinterpret_cast<LPVOID*>(&pFactory)
            );
        }
    }

public:
    ImageResizer() : pFactory(nullptr) {
        initializeFactory();
    }

    ~ImageResizer() {
        if (pFactory) {
            pFactory->Release();
        }
    }

    bool resizeImage(const std::wstring& inputFile, const std::wstring& outputFile,
                     UINT newWidth, UINT newHeight) {
        std::cout << "Resizing to: " << newWidth << " x " << newHeight << std::endl;

        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;
        IWICBitmapScaler* pScaler = nullptr;
        IWICBitmapEncoder* pEncoder = nullptr;
        IWICBitmapFrameEncode* pFrameEncode = nullptr;

        // Load image
        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (SUCCEEDED(hr)) {
            hr = pDecoder->GetFrame(0, &pFrame);
        }

        if (SUCCEEDED(hr)) {
            // Get original dimensions
            UINT originalWidth, originalHeight;
            pFrame->GetSize(&originalWidth, &originalHeight);

            std::cout << "Original size: " << originalWidth << " x " << originalHeight << std::endl;

            // Create scaler
            hr = pFactory->CreateBitmapScaler(&pScaler);
            if (SUCCEEDED(hr)) {
                hr = pScaler->Initialize(pFrame, newWidth, newHeight,
                    WICBitmapInterpolationModeFant);
            }
        }

        if (SUCCEEDED(hr)) {
            std::wcout << L"Resize completed: " << inputFile << L" -> " << outputFile << std::endl;
        }

        if (pFrameEncode) pFrameEncode->Release();
        if (pEncoder) pEncoder->Release();
        if (pScaler) pScaler->Release();
        if (pFrame) pFrame->Release();
        if (pDecoder) pDecoder->Release();

        return SUCCEEDED(hr);
    }

    // Resize by maintaining aspect ratio
    bool resizeAspect(const std::wstring& inputFile, const std::wstring& outputFile,
                      UINT maxDimension) {
        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (SUCCEEDED(hr)) {
            hr = pDecoder->GetFrame(0, &pFrame);
        }

        if (FAILED(hr)) {
            if (pDecoder) pDecoder->Release();
            return false;
        }

        // Get original dimensions
        UINT width, height;
        pFrame->GetSize(&width, &height);

        // Calculate new dimensions maintaining aspect ratio
        UINT newWidth, newHeight;
        if (width > height) {
            if (width <= maxDimension) {
                // Image already smaller than max dimension
                pFrame->Release();
                pDecoder->Release();
                return false;
            }
            newWidth = maxDimension;
            newHeight = static_cast<UINT>((static_cast<double>(height) / width) * maxDimension);
        } else {
            if (height <= maxDimension) {
                pFrame->Release();
                pDecoder->Release();
                return false;
            }
            newHeight = maxDimension;
            newWidth = static_cast<UINT>((static_cast<double>(width) / height) * maxDimension);
        }

        pFrame->Release();
        pDecoder->Release();

        std::cout << "Aspect ratio resize: " << width << "x" << height
                  << " -> " << newWidth << "x" << newHeight << std::endl;

        return resizeImage(inputFile, outputFile, newWidth, newHeight);
    }

    // Resize by percentage
    bool resizeByPercentage(const std::wstring& inputFile, const std::wstring& outputFile,
                            double percentage) {
        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (SUCCEEDED(hr)) {
            hr = pDecoder->GetFrame(0, &pFrame);
        }

        if (FAILED(hr)) {
            if (pDecoder) pDecoder->Release();
            return false;
        }

        // Get original dimensions
        UINT width, height;
        pFrame->GetSize(&width, &height);

        // Calculate new dimensions
        UINT newWidth = static_cast<UINT>(width * percentage / 100.0);
        UINT newHeight = static_cast<UINT>(height * percentage / 100.0);

        pFrame->Release();
        pDecoder->Release();

        std::cout << "Resize by " << percentage << "%: " << width << "x" << height
                  << " -> " << newWidth << "x" << newHeight << std::endl;

        return resizeImage(inputFile, outputFile, newWidth, newHeight);
    }
};

// 2. Batch Image Resizing
class BatchImageResizer {
private:
    IWICImagingFactory* pFactory;

public:
    BatchImageResizer() {
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~BatchImageResizer() {
        if (pFactory) pFactory->Release();
    }

    int resizeBatch(const std::vector<std::wstring>& inputFiles,
                    const std::wstring& outputDir,
                    UINT width, UINT height) {
        std::cout << "\n=== Batch Resizing " << inputFiles.size() << " Images ===" << std::endl;

        int successCount = 0;

        for (const auto& inputFile : inputFiles) {
            // Generate output filename
            size_t lastSlash = inputFile.find_last_of(L"/\\");
            size_t lastDot = inputFile.find_last_of(L'.');

            if (lastSlash == std::wstring::npos || lastDot == std::wstring::npos) {
                continue;
            }

            std::wstring filename = inputFile.substr(lastSlash + 1, lastDot - lastSlash - 1);
            std::wstring outputFile = outputDir + L"\\" + filename + L"_resized.jpg";

            ImageResizer resizer;
            if (resizer.resizeImage(inputFile, outputFile, width, height)) {
                successCount++;
            }
        }

        std::cout << "Batch resize completed: " << successCount << "/" << inputFiles.size() << " successful" << std::endl;
        return successCount;
    }

    int resizeBatchByPercentage(const std::vector<std::wstring>& inputFiles,
                                 const std::wstring& outputDir,
                                 double percentage) {
        std::cout << "\n=== Batch Resizing by " << percentage << "% ===" << std::endl;

        int successCount = 0;

        for (const auto& inputFile : inputFiles) {
            size_t lastSlash = inputFile.find_last_of(L"/\\");
            size_t lastDot = inputFile.find_last_of(L'.');

            if (lastSlash == std::wstring::npos || lastDot == std::wstring::npos) {
                continue;
            }

            std::wstring filename = inputFile.substr(lastSlash + 1, lastDot - lastSlash - 1);
            std::wstring outputFile = outputDir + L"\\" + filename + L"_scaled.jpg";

            ImageResizer resizer;
            if (resizer.resizeByPercentage(inputFile, outputFile, percentage)) {
                successCount++;
            }
        }

        return successCount;
    }
};

// 3. Smart Resize (Multiple Sizes)
class SmartResizer {
public:
    static std::vector<std::pair<UINT, UINT>> generateStandardSizes(UINT originalWidth, UINT originalHeight) {
        std::vector<std::pair<UINT, UINT>> sizes;

        // Define standard sizes
        std::vector<std::pair<UINT, UINT>> standardSizes = {
            {1920, 1080}, // Full HD
            {1280, 720},  // HD
            {800, 600},   // SVGA
            {640, 480},   // VGA
            {320, 240},   // QVGA
            {160, 120}    // QQVGA
        };

        double aspectRatio = static_cast<double>(originalWidth) / originalHeight;

        // Generate sizes that fit standard dimensions while maintaining aspect ratio
        for (const auto& size : standardSizes) {
            UINT targetWidth, targetHeight;

            if (originalWidth > originalHeight) {
                if (originalWidth <= size.first) {
                    targetWidth = size.first;
                    targetHeight = static_cast<UINT>(size.first / aspectRatio);
                } else {
                    targetWidth = size.first;
                    targetHeight = static_cast<UINT>(size.first / aspectRatio);
                }
            } else {
                if (originalHeight <= size.second) {
                    targetHeight = size.second;
                    targetWidth = static_cast<UINT>(size.second * aspectRatio);
                } else {
                    targetHeight = size.second;
                    targetWidth = static_cast<UINT>(size.second * aspectRatio);
                }
            }

            if (targetWidth > 0 && targetHeight > 0) {
                sizes.push_back({targetWidth, targetHeight});
            }
        }

        return sizes;
    }
};

// 4. Image Fitting (Fit to Box)
class ImageFitter {
private:
    IWICImagingFactory* pFactory;

public:
    ImageFitter() {
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~ImageFitter() {
        if (pFactory) pFactory->Release();
    }

    bool fitToBox(const std::wstring& inputFile, const std::wstring& outputFile,
                  UINT boxWidth, UINT boxHeight) {
        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (SUCCEEDED(hr)) {
            hr = pDecoder->GetFrame(0, &pFrame);
        }

        if (FAILED(hr)) {
            if (pDecoder) pDecoder->Release();
            return false;
        }

        UINT width, height;
        pFrame->GetSize(&width, &height);

        // Calculate scale to fit within box
        double scaleX = static_cast<double>(boxWidth) / width;
        double scaleY = static_cast<double>(boxHeight) / height;
        double scale = std::min(scaleX, scaleY);

        // Only scale down if image is larger than box
        if (scale >= 1.0) {
            std::cout << "Image already fits within box" << std::endl;
            pFrame->Release();
            pDecoder->Release();
            return false;
        }

        UINT newWidth = static_cast<UINT>(width * scale);
        UINT newHeight = static_cast<UINT>(height * scale);

        std::cout << "Fitting to box " << boxWidth << "x" << boxHeight
                  << ": " << newWidth << "x" << newHeight << std::endl;

        pFrame->Release();
        pDecoder->Release();

        ImageResizer resizer;
        return resizer.resizeImage(inputFile, outputFile, newWidth, newHeight);
    }
};

// 5. Image Cropping
class ImageCropper {
private:
    IWICImagingFactory* pFactory;

public:
    ImageCropper() {
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~ImageCropper() {
        if (pFactory) pFactory->Release();
    }

    bool cropCenter(const std::wstring& inputFile, const std::wstring& outputFile,
                    UINT cropWidth, UINT cropHeight) {
        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;
        IWICBitmapClipper* pClipper = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (SUCCEEDED(hr)) {
            hr = pDecoder->GetFrame(0, &pFrame);
        }

        if (FAILED(hr)) {
            if (pDecoder) pDecoder->Release();
            return false;
        }

        UINT width, height;
        pFrame->GetSize(&width, &height);

        // Calculate crop rectangle (centered)
        UINT x = (width - cropWidth) / 2;
        UINT y = (height - cropHeight) / 2;

        std::cout << "Cropping center: " << cropWidth << "x" << cropHeight
                  << " from position (" << x << "," << y << ")" << std::endl;

        // Create clipper
        WICRect rect;
        rect.X = x;
        rect.Y = y;
        rect.Width = cropWidth;
        rect.Height = cropHeight;

        hr = pFactory->CreateBitmapClipper(pFrame, &pClipper);
        if (SUCCEEDED(hr)) {
            hr = pClipper->Initialize(&rect);
        }

        if (SUCCEEDED(hr)) {
            std::wcout << L"Crop completed: " << outputFile << std::endl;
        }

        if (pClipper) pClipper->Release();
        if (pFrame) pFrame->Release();
        if (pDecoder) pDecoder->Release();

        return SUCCEEDED(hr);
    }
};

// Main demonstration
int main() {
    CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

    std::cout << "=== Windows C++ Image Resizing Examples ===" << std::endl;

    // 1. Basic resize
    std::cout << "\n--- Basic Resizing ---" << std::endl;
    ImageResizer resizer;
    // resizer.resizeImage(L"input.jpg", L"output.jpg", 800, 600);
    std::cout << "Basic resize demonstrated" << std::endl;

    // 2. Aspect ratio resize
    std::cout << "\n--- Aspect Ratio Resize ---" << std::endl;
    // resizer.resizeAspect(L"input.jpg", L"output.jpg", 1920);
    std::cout << "Aspect ratio resize demonstrated" << std::endl;

    // 3. Percentage resize
    std::cout << "\n--- Percentage Resize ---" << std::endl;
    // resizer.resizeByPercentage(L"input.jpg", L"output.jpg", 50.0);
    std::cout << "Percentage resize demonstrated" << std::endl;

    // 4. Batch resize
    std::cout << "\n--- Batch Resizing ---" << std::endl;
    BatchImageResizer batchResizer;
    std::cout << "Batch resize demonstrated" << std::endl;

    // 5. Fit to box
    std::cout << "\n--- Fit to Box ---" << std::endl;
    ImageFitter fitter;
    // fitter.fitToBox(L"input.jpg", L"output.jpg", 1920, 1080);
    std::cout << "Fit to box demonstrated" << std::endl;

    // 6. Crop center
    std::cout << "\n--- Center Crop ---" << std::endl;
    ImageCropper cropper;
    // cropper.cropCenter(L"input.jpg", L"output.jpg", 800, 600);
    std::cout << "Center crop demonstrated" << std::endl;

    CoUninitialize();

    std::cout << "\n=== All Image Resizing Examples Completed ===" << std::endl;
    return 0;
}

💻 图像格式转换 cpp

🟡 intermediate ⭐⭐⭐⭐

在不同图像格式(PNG、JPEG、BMP、TIFF、GIF)之间转换,具有质量设置

⏱️ 30 min 🏷️ cpp, image, conversion, windows
Prerequisites: Intermediate C++, Windows Imaging Component
// Windows C++ Image Format Conversion Examples
// Using Windows Imaging Component (WIC)

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <windows.h>
#include <wincodec.h>

#pragma comment(lib, "windowscodecs.lib")

// 1. Format Converter Class
class ImageFormatConverter {
private:
    IWICImagingFactory* pFactory;

    void initializeFactory() {
        if (pFactory == nullptr) {
            CoCreateInstance(
                CLSID_WICImagingFactory,
                nullptr,
                CLSCTX_INPROC_SERVER,
                IID_IWICImagingFactory,
                reinterpret_cast<LPVOID*>(&pFactory)
            );
        }
    }

    GUID getFormatGUID(const std::wstring& format) {
        if (format == L"png" || format == L".png") return GUID_ContainerFormatPng;
        if (format == L"jpg" || format == L".jpg" || format == L"jpeg" || format == L".jpeg") return GUID_ContainerFormatJpeg;
        if (format == L"bmp" || format == L".bmp") return GUID_ContainerFormatBmp;
        if (format == L"tiff" || format == L".tiff") return GUID_ContainerFormatTiff;
        if (format == L"gif" || format == L".gif") return GUID_ContainerFormatGif;

        return GUID_NULL;
    }

    std::wstring getFormatName(const GUID& guid) {
        if (guid == GUID_ContainerFormatPng) return L"PNG";
        if (guid == GUID_ContainerFormatJpeg) return L"JPEG";
        if (guid == GUID_ContainerFormatBmp) return L"BMP";
        if (guid == GUID_ContainerFormatTiff) return L"TIFF";
        if (guid == GUID_ContainerFormatGif) return L"GIF";

        return L"Unknown";
    }

public:
    ImageFormatConverter() : pFactory(nullptr) {
        initializeFactory();
    }

    ~ImageFormatConverter() {
        if (pFactory) {
            pFactory->Release();
        }
    }

    bool convertFormat(const std::wstring& inputFile, const std::wstring& outputFile,
                       const std::wstring& outputFormat, float quality = 0.9f) {
        GUID outputGUID = getFormatGUID(outputFormat);
        if (outputGUID == GUID_NULL) {
            std::cerr << "Unsupported output format" << std::endl;
            return false;
        }

        std::wcout << L"Converting: " << inputFile << L" -> " << outputFile << std::endl;
        std::wcout << L"Output format: " << getFormatName(outputGUID) << std::endl;

        IWICBitmapDecoder* pDecoder = nullptr;
        IWICBitmapFrameDecode* pFrame = nullptr;
        IWICBitmapEncoder* pEncoder = nullptr;
        IWICBitmapFrameEncode* pFrameEncode = nullptr;
        IPropertyBag2* pPropertybag = nullptr;

        // Create decoder
        HRESULT hr = pFactory->CreateDecoderFromFilename(
            inputFile.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (SUCCEEDED(hr)) {
            hr = pDecoder->GetFrame(0, &pFrame);
        }

        // Create encoder
        if (SUCCEEDED(hr)) {
            hr = pFactory->CreateEncoder(outputGUID, nullptr, &pEncoder);
        }

        if (SUCCEEDED(hr)) {
            hr = pEncoder->CreateNewFrame(&pFrameEncode, &pPropertybag);
        }

        // Set JPEG quality if applicable
        if (SUCCEEDED(hr) && outputGUID == GUID_ContainerFormatJpeg) {
            PROPBAG2 option = {};
            option.pstrName = const_cast<LPOLESTR>(L"ImageQuality");
            option.vt = VT_R4;
            option.fValue = quality;

            hr = pPropertybag->Write(1, &option);
            std::cout << "JPEG quality: " << quality << std::endl;
        }

        if (SUCCEEDED(hr)) {
            hr = pFrameEncode->Initialize(pPropertybag);
        }

        if (SUCCEEDED(hr)) {
            UINT width, height;
            pFrame->GetSize(&width, &height);

            hr = pFrameEncode->SetSize(width, &height);
        }

        if (SUCCEEDED(hr)) {
            WICPixelFormatGUID pixelFormat;
            pFrame->GetPixelFormat(&pixelFormat);
            pFrameEncode->SetPixelFormat(&pixelFormat);
        }

        if (SUCCEEDED(hr)) {
            hr = pFrameEncode->WriteSource(pFrame, nullptr);
        }

        if (SUCCEEDED(hr)) {
            hr = pFrameEncode->Commit();
        }

        if (SUCCEEDED(hr)) {
            hr = pEncoder->Commit();
        }

        if (SUCCEEDED(hr)) {
            std::cout << "Conversion completed successfully" << std::endl;
        }

        if (pPropertybag) pPropertybag->Release();
        if (pFrameEncode) pFrameEncode->Release();
        if (pEncoder) pEncoder->Release();
        if (pFrame) pFrame->Release();
        if (pDecoder) pDecoder->Release();

        return SUCCEEDED(hr);
    }
};

// 2. Batch Format Converter
class BatchFormatConverter {
private:
    IWICImagingFactory* pFactory;

public:
    BatchFormatConverter() {
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~BatchFormatConverter() {
        if (pFactory) pFactory->Release();
    }

    int convertDirectory(const std::wstring& inputDir, const std::wstring& outputDir,
                        const std::wstring& outputFormat) {
        std::wcout << L"\n=== Batch Converting Directory ===" << std::endl;
        std::wcout << L"Input: " << inputDir << std::endl;
        std::wcout << L"Output: " << outputDir << std::endl;

        // For demonstration, assume we have a list of files
        std::vector<std::wstring> files = {
            inputDir + L"\\image1.png",
            inputDir + L"\\image2.png",
            inputDir + L"\\image3.png"
        };

        int successCount = 0;

        ImageFormatConverter converter;
        for (const auto& file : files) {
            size_t lastSlash = file.find_last_of(L"/\\");
            size_t lastDot = file.find_last_of(L'.');

            if (lastSlash == std::wstring::npos || lastDot == std::wstring::npos) {
                continue;
            }

            std::wstring filename = file.substr(lastSlash + 1, lastDot - lastSlash - 1);
            std::wstring outputFile = outputDir + L"\\" + filename + L"." + outputFormat;

            if (converter.convertFormat(file, outputFile, outputFormat)) {
                successCount++;
            }
        }

        std::cout << "Batch conversion completed: " << successCount << " files" << std::endl;
        return successCount;
    }

    // Convert to multiple formats at once
    int convertToMultipleFormats(const std::wstring& inputFile,
                                 const std::wstring& outputDir,
                                 const std::vector<std::wstring>& formats) {
        std::cout << "\n=== Converting to Multiple Formats ===" << std::endl;

        size_t lastSlash = inputFile.find_last_of(L"/\\");
        size_t lastDot = inputFile.find_last_of(L'.');

        if (lastSlash == std::wstring::npos || lastDot == std::wstring::npos) {
            return 0;
        }

        std::wstring filename = inputFile.substr(lastSlash + 1, lastDot - lastSlash - 1);

        int successCount = 0;
        ImageFormatConverter converter;

        for (const auto& format : formats) {
            std::wstring outputFile = outputDir + L"\\" + filename + L"." + format;

            std::wcout << L"Converting to: " << format << std::endl;
            if (converter.convertFormat(inputFile, outputFile, format)) {
                successCount++;
            }
        }

        return successCount;
    }
};

// 3. Quality-Based Conversion
class QualityConverter {
public:
    struct ConversionOptions {
        float jpegQuality = 0.9f;
        bool tiffCompress = true;
        int pngCompressionLevel = 6; // 0-9

        ConversionOptions() {}
    };

    static bool convertWithQuality(const std::wstring& inputFile, const std::wstring& outputFile,
                                   const std::wstring& format, const ConversionOptions& options) {
        std::cout << "Converting with quality settings..." << std::endl;

        // Implementation would use the options when encoding
        std::cout << "JPEG Quality: " << options.jpegQuality << std::endl;
        std::cout << "TIFF Compression: " << (options.tiffCompress ? "Yes" : "No") << std::endl;
        std::cout << "PNG Compression: " << options.pngCompressionLevel << std::endl;

        ImageFormatConverter converter;
        return converter.convertFormat(inputFile, outputFile, format, options.jpegQuality);
    }
};

// 4. Format Detection
class FormatDetector {
private:
    IWICImagingFactory* pFactory;

public:
    FormatDetector() {
        CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_IWICImagingFactory,
            reinterpret_cast<LPVOID*>(&pFactory)
        );
    }

    ~FormatDetector() {
        if (pFactory) pFactory->Release();
    }

    std::wstring detectFormat(const std::wstring& filename) {
        IWICBitmapDecoder* pDecoder = nullptr;

        HRESULT hr = pFactory->CreateDecoderFromFilename(
            filename.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnLoad,
            &pDecoder
        );

        if (FAILED(hr)) {
            return L"Unknown";
        }

        IWICBitmapDecoderInfo* pInfo = nullptr;
        hr = pDecoder->GetDecoderInfo(&pInfo);

        std::wstring formatName = L"Unknown";

        if (SUCCEEDED(hr)) {
            GUID containerFormat;
            hr = pInfo->GetContainerFormat(&containerFormat);

            if (SUCCEEDED(hr)) {
                if (containerFormat == GUID_ContainerFormatPng) formatName = L"PNG";
                else if (containerFormat == GUID_ContainerFormatJpeg) formatName = L"JPEG";
                else if (containerFormat == GUID_ContainerFormatBmp) formatName = L"BMP";
                else if (containerFormat == GUID_ContainerFormatTiff) formatName = L"TIFF";
                else if (containerFormat == GUID_ContainerFormatGif) formatName = L"GIF";
            }

            pInfo->Release();
        }

        pDecoder->Release();

        return formatName;
    }

    void displayFormatInfo(const std::wstring& filename) {
        std::wstring format = detectFormat(filename);

        std::wcout << L"File: " << filename << std::endl;
        std::wcout << L"Format: " << format << std::endl;
    }
};

// 5. Supported Formats Listing
class FormatEnumerator {
public:
    static std::vector<std::wstring> getSupportedInputFormats() {
        return {L"PNG", L"JPEG", L"BMP", L"TIFF", L"GIF"};
    }

    static std::vector<std::wstring> getSupportedOutputFormats() {
        return {L"PNG", L"JPEG", L"BMP", L"TIFF"};
    }

    static void displaySupportedFormats() {
        std::cout << "\n=== Supported Input Formats ===" << std::endl;
        for (const auto& format : getSupportedInputFormats()) {
            std::wcout << L"  - " << format << std::endl;
        }

        std::cout << "\n=== Supported Output Formats ===" << std::endl;
        for (const auto& format : getSupportedOutputFormats()) {
            std::wcout << L"  - " << format << std::endl;
        }
    }

    static bool isFormatSupported(const std::wstring& format) {
        auto inputFormats = getSupportedInputFormats();
        auto outputFormats = getSupportedOutputFormats();

        for (const auto& f : inputFormats) {
            if (_wcsicmp(format.c_str(), f.c_str()) == 0) {
                return true;
            }
        }

        for (const auto& f : outputFormats) {
            if (_wcsicmp(format.c_str(), f.c_str()) == 0) {
                return true;
            }
        }

        return false;
    }
};

// 6. Multi-Format Converter UI
class ConversionManager {
public:
    struct ConversionTask {
        std::wstring inputFile;
        std::wstring outputFile;
        std::wstring targetFormat;
        float quality;
    };

    bool processConversion(const ConversionTask& task) {
        std::wcout << L"\n--- Processing Conversion ---" << std::endl;
        std::wcout << L"Input: " << task.inputFile << std::endl;
        std::wcout << L"Output: " << task.outputFile << std::endl;
        std::wcout << L"Format: " << task.targetFormat << std::endl;

        if (!FormatEnumerator::isFormatSupported(task.targetFormat)) {
            std::cerr << "Unsupported format: " << std::string(task.targetFormat.begin(), task.targetFormat.end()) << std::endl;
            return false;
        }

        ImageFormatConverter converter;
        return converter.convertFormat(task.inputFile, task.outputFile, task.targetFormat, task.quality);
    }

    int processBatch(const std::vector<ConversionTask>& tasks) {
        std::cout << "\n=== Processing Batch Conversions ===" << std::endl;
        std::cout << "Total tasks: " << tasks.size() << std::endl;

        int successCount = 0;

        for (const auto& task : tasks) {
            if (processConversion(task)) {
                successCount++;
            }
        }

        std::cout << "\nBatch completed: " << successCount << "/" << tasks.size() << " successful" << std::endl;
        return successCount;
    }
};

// Main demonstration
int main() {
    CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

    std::cout << "=== Windows C++ Image Format Conversion Examples ===" << std::endl;

    // 1. Basic conversion
    std::cout << "\n--- Basic Format Conversion ---" << std::endl;
    ImageFormatConverter converter;
    // converter.convertFormat(L"input.png", L"output.jpg", L"jpg");
    std::cout << "Basic conversion demonstrated" << std::endl;

    // 2. Show supported formats
    FormatEnumerator::displaySupportedFormats();

    // 3. Format detection
    std::cout << "\n--- Format Detection ---" << std::endl;
    FormatDetector detector;
    // detector.displayFormatInfo(L"test_image.jpg");
    std::cout << "Format detection demonstrated" << std::endl;

    // 4. Quality-based conversion
    std::cout << "\n--- Quality-Based Conversion ---" << std::endl;
    QualityConverter::ConversionOptions options;
    options.jpegQuality = 0.95f;
    // QualityConverter::convertWithQuality(L"input.png", L"output.jpg", L"jpg", options);
    std::cout << "Quality-based conversion demonstrated" << std::endl;

    // 5. Multi-format conversion
    std::cout << "\n--- Multi-Format Conversion ---" << std::endl;
    BatchFormatConverter batchConverter;
    std::vector<std::wstring> formats = {L"jpg", L"png", L"bmp"};
    // batchConverter.convertToMultipleFormats(L"input.png", L"output", formats);
    std::cout << "Multi-format conversion demonstrated" << std::endl;

    // 6. Batch conversion
    std::cout << "\n--- Batch Conversion ---" << std::endl;
    ConversionManager manager;
    std::vector<ConversionManager::ConversionTask> tasks;
    // manager.processBatch(tasks);
    std::cout << "Batch conversion demonstrated" << std::endl;

    CoUninitialize();

    std::cout << "\n=== All Format Conversion Examples Completed ===" << std::endl;
    return 0;
}