cxnet 发表于 2024-11-14 13:58:07

C++图像保存问题

这C++ MFC里Mat图片我想保存为Postscript 3语言的*.ps文件应该怎样操作?网上查了很久没找到方法,请提供示例,谢谢!

FishC 发表于 2024-11-14 13:58:14

要在C++ MFC中将OpenCV的`Mat`图像保存为PostScript(.ps)文件,直接保存成这种格式可能比较复杂,因为OpenCV本身并不直接支持PostScript格式。

一种可行的办法是:
1. 将`Mat`图像先转换为一种中间格式,比如BMP或PNG。
2. 使用一个支持格式转换的第三方库或工具将该中间格式转换为PostScript格式。

使用ImageMagick是一个常见的选择。以下是简单操作步骤:

1. **安装ImageMagick**:
   确保系统上安装了ImageMagick。

2. **保存为中间格式**:

   使用OpenCV将图像保存为BMP文件:

   cv::Mat image;// 假设这是您的Mat图像
   imwrite("temp_image.bmp", image);

3. **转换为PostScript**:

   可以使用ImageMagick的命令行工具`convert`来完成格式转换:

   convert temp_image.bmp output.ps

4. **通过MFC调用系统命令**:

   可以在MFC中使用`system()`函数来调用命令行:

   system("convert temp_image.bmp output.ps");

请注意,这只是一个简单的方法示例。实际项目中,您可能需要处理文件路径问题和错误检查等。此外,若计算机上没有ImageMagick,可能需要事先安装其命令行工具才能正常使用。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-14 18:52:40

FishC 发表于 2024-11-14 13:58
要在C++ MFC中将OpenCV的`Mat`图像保存为PostScript(.ps)文件,直接保存成这种格式可能比较复杂,因为Ope ...

还有其它办法可以直接在C++里把图片输出成Postscript 3的*.ps文件吗?我试了通过ImageMagick的转换功能,文件小是可以,但文件几百兆或者几个G的文件转换起来非常慢

FishC 发表于 2024-11-14 18:52:57

cxnet 发表于 2024-11-14 18:52
还有其它办法可以直接在C++里把图片输出成Postscript...

在C++中直接将图像输出为PostScript 3(.ps)文件而不依赖于外部工具,您可能需要手动生成PostScript文件。这涉及用C++代码构建PostScript命令并写入文件。

以下是一个简化的示例,说明如何手动创建一个简单的PostScript文件:

#include <opencv2/opencv.hpp>
#include <fstream>
#include <iostream>

void writePostscript(const cv::Mat& image, const std::string& filename) {
    std::ofstream ofs(filename);

    if (!ofs.is_open()) {
      std::cerr << "Unable to open file for writing" << std::endl;
      return;
    }

    // 输出PostScript文件头信息
    ofs << "%!PS-Adobe-3.0\n";
    ofs << "%%BoundingBox: 0 0 " << image.cols << " " << image.rows << "\n";
   
    // 设置图像翻转和大小
    ofs << image.cols << " " << image.rows << " scale\n";
    ofs << image.cols << " " << "0 translate\n";
   
    // 输出图像数据
    ofs << "{<";
   
    for (int y = 0; y < image.rows; y++) {
      const uchar* row_ptr = image.ptr<uchar>(y);
      for (int x = 0; x < image.cols * image.channels(); x++) {
            ofs << std::hex << static_cast<int>(row_ptr);
      }
    }
    ofs << ">} imagemask\n";
   
    ofs << "showpage\n";
    ofs.close();
}

int main() {
    cv::Mat image = cv::imread("your_image.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "Image not found!" << std::endl;
      return -1;
    }
   
    writePostscript(image, "output.ps");

    return 0;
}

**说明:**
- 这里示例假定图像是单通道(灰度图像),因为PostScript中处理彩色图像会更加复杂。
- PostScript绘图命令需要您了解PostScript语言,它涉及坐标转换、色彩与路径等操作。
- 上述例子简单输出Hex格式,高效的PostScript文件生成通常需要压缩和多种优化。

**注意:**
这种方法灵活性高,但不够简单,特别对于大型图像,生成PostScript数据较为复杂。如果需要完整的PostScript 3功能,可能需要借助专门的第三方库实现。对于庞大文件的效率问题,可能得依赖更高效的算法或在应用层提前压缩处理。您的需求可以被认为是特殊情况,市面上有很多PDF/PostScript库可以用来实现类似功能。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-14 19:09:47

FishC 发表于 2024-11-14 18:52
在C++中直接将图像输出为PostScript 3(.ps)文件而不依赖于外部工具,您可能需要手动生成PostScript文 ...

这段代码不对,虽然能创建出*.ps文件,但它不能打开使用

FishC 发表于 2024-11-14 19:10:04

cxnet 发表于 2024-11-14 19:09
这段代码不对,虽然能创建出*.ps文件,但它不能打开使 ...

生成有效的PostScript文件需要遵循相应的PostScript语言规范,您必须在文件中手动定义图像数据结构。以下是一个简单的示例,说明如何使用PostScript语法将图像数据写入.ps文件:

#include <opencv2/opencv.hpp>
#include <fstream>
#include <iostream>
#include <string>

void writePostscript(const cv::Mat& image, const std::string& filename) {
    // 确保图像是灰度或彩色
    CV_Assert(image.type() == CV_8UC1 || image.type() == CV_8UC3);

    std::ofstream ofs(filename);
    if (!ofs.is_open()) {
      std::cerr << "Failed to open file for writing: " << filename << std::endl;
      return;
    }

    // PostScript Header
    ofs << "%!PS-Adobe-3.0\n";
    ofs << "%%BoundingBox: 0 0 " << image.cols << " " << image.rows << "\n";
    ofs << "/picstr " << image.cols * (image.type() == CV_8UC3 ? 3 : 1) << " string def\n";
    ofs << image.cols << " " << image.rows << " 8\n";

    if (image.type() == CV_8UC3) {
      ofs << "[ " << image.cols << " 0 0 -" << image.rows << " 0 " << image.rows << " ]\n";
      ofs << "{ currentfile picstr readhexstring pop }\nfalse 3 colorimage\n";
    } else {
      ofs << "[ " << image.cols << " 0 0 -" << image.rows << " 0 " << image.rows << " ]\n";
      ofs << "{ currentfile picstr readhexstring pop }\nimage\n";
    }

    // Write pixel data
    for (int y = 0; y < image.rows; ++y) {
      for (int x = 0; x < image.cols; ++x) {
            if (image.type() == CV_8UC3) {
                cv::Vec3b color = image.at<cv::Vec3b>(y, x);
                ofs << std::hex << (int)color << (int)color << (int)color;// BGR to RGB
            } else {
                uchar gray = image.at<uchar>(y, x);
                ofs << std::hex << (int)gray << (int)gray << (int)gray;
            }
      }
      ofs << "\n";
    }

    ofs << "showpage\n";
    ofs.close();
}

int main() {
    cv::Mat image = cv::imread("your_image.png");
    if (image.empty()) {
      std::cerr << "Image not loaded!" << std::endl;
      return -1;
    }
    writePostscript(image, "output.ps");
    return 0;
}

### 说明:
- 确保OpenCV和C++编译器环境已正确设置。
- 该代码首先检查图像类型(灰度图或彩色图),然后初始化PostScript文件头。
- 使用`vec3b`处理彩色图像,注意将BGR顺序转换为RGB。
- 将每像素颜色值转换为十六进制格式写入文件中。
- 保存后生成的PostScript文件可以通过支持PostScript格式的查看器打开。

这个代码很基础,如果遇到更复杂的图像或者需要转换成矢量图的话,需要根据图像内容和PostScript语言进行拓展。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-14 19:29:59

FishC 发表于 2024-11-14 19:10
生成有效的PostScript文件需要遵循相应的PostScript语言规范,您必须在文件中手动定义图像数据结构。以 ...

还是不对,如果可以输出PDF文件也是可以的,请问怎样输出PDF文件,并且设置图像dpi,主要是黑白二值图,需要输出1200dpi

FishC 发表于 2024-11-14 19:30:17

cxnet 发表于 2024-11-14 19:29
还是不对,如果可以输出PDF文件也是可以的,请问怎样 ...

要在C++中输出PDF文件并设置DPI,可以使用第三方库如 Cairo。Cairo 允许您创建矢量图形,并可以导出到PDF格式。以下是一个示例,展示如何使用 Cairo 库将黑白二值图像输出为1200 DPI的PDF文件:

1. **安装 Cairo**:首先,确保安装了 Cairo 库并且您的项目已正确配置以使用它。

2. **使用 Cairo 创建 PDF**:

   #include <cairo/cairo.h>
   #include <opencv2/opencv.hpp>

   void saveMatAsPDF(const cv::Mat& image, const std::string& filename, double dpi) {
       // 确保图像是单通道
       CV_Assert(image.type() == CV_8UC1);

       // 计算图像尺寸(以点为单位)
       double width_in_points = image.cols * 72.0 / dpi;
       double height_in_points = image.rows * 72.0 / dpi;

       // 创建一个 PDF surface
       cairo_surface_t* surface = cairo_pdf_surface_create(filename.c_str(), width_in_points, height_in_points);
       cairo_t* cr = cairo_create(surface);

       // 设置缩放比例以匹配 DPI
       cairo_scale(cr, width_in_points / image.cols, height_in_points / image.rows);

       // 绘制图像
       for (int y = 0; y < image.rows; ++y) {
         for (int x = 0; x < image.cols; ++x) {
               uchar pixel = image.at<uchar>(y, x);
               double gray = static_cast<double>(pixel) / 255.0;
               cairo_set_source_rgb(cr, gray, gray, gray);
               cairo_rectangle(cr, x, y, 1, 1);
               cairo_fill(cr);
         }
       }

       // 完成并释放资源
       cairo_destroy(cr);
       cairo_surface_destroy(surface);
   }

   int main() {
       // 加载二值图像
       cv::Mat image = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
       if (image.empty()) {
         std::cerr << "Failed to load image.\n";
         return -1;
       }

       // 保存为 PDF,设置 1200 DPI
       saveMatAsPDF(image, "output.pdf", 1200.0);

       return 0;
   }

在上述代码中,`cairo_pdf_surface_create` 创建了一个新的PDF surface。通过设置`cairo_scale`来确保PDF的尺寸和DPI相匹配。图像以灰度方式绘制,通过遍历每个像素为白色或黑色设置cairo上下文。

确保您的项目正确链接到Cairo库,并且Cairo已在您的开发环境中安装,这样代码才能正常工作。对于Windows环境,安装Cairo库可能需要使用Vcpkg或其他包管理工具。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-14 20:26:09

FishC 发表于 2024-11-14 19:30
要在C++中输出PDF文件并设置DPI,可以使用第三方库如 Cairo。Cairo 允许您创建矢量图形,并可以导出到P ...

还有别的库可以将二值图输出到PDF并设置图像压缩dpi吗?Cairo库找了很久没找到适合64位的,官网已经很长时间没有更新了

FishC 发表于 2024-11-14 20:26:21

cxnet 发表于 2024-11-14 20:26
还有别的库可以将二值图输出到PDF并设置图像压缩dpi吗 ...

除了 Cairo,还有其他库可以用来将图像输出为 PDF,并设置分辨率。常用的库之一是 **PoDoFo** 和 **Poppler**。不过,如果您需要相对简单的解决方案,我建议使用 **libharu**,它是一个轻量级且易于使用的 PDF 库。

下面是一个使用 libharu 库将二值图像输出为 PDF 的示例:

### 使用 libharu 库

1. **安装 libharu**:首先,下载并安装 libharu。可以在其 (https://github.com/libharu/libharu)上找到相关信息。

2. **使用 libharu 创建 PDF**:

   #include <hpdf.h>
   #include <opencv2/opencv.hpp>
   #include <iostream>

   void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void *user_data) {
       std::cerr << "Error: " << std::hex << error_no << ", detail: " << detail_no << std::endl;
   }

   void saveImageAsPDF(const cv::Mat& image, const std::string& filename, int dpi) {
       // 初始化 libharu
       HPDF_Doc pdf = HPDF_New(error_handler, nullptr);
       if (!pdf) {
         std::cerr << "Failed to create PDF object." << std::endl;
         return;
       }

       // 创建页面
       HPDF_Page page = HPDF_AddPage(pdf);
       HPDF_Page_SetWidth(page, image.cols * 72.0 / dpi);
       HPDF_Page_SetHeight(page, image.rows * 72.0 / dpi);

       // 遍历图像并绘制
       for (int y = 0; y < image.rows; ++y) {
         for (int x = 0; x < image.cols; ++x) {
               uchar pixel = image.at<uchar>(y, x);
               double gray = pixel / 255.0;

               HPDF_Page_SetRGBFill(page, gray, gray, gray);
               HPDF_Page_Rectangle(page, x * 72.0 / dpi, (image.rows - y - 1) * 72.0 / dpi, 1 * 72.0 / dpi, 1 * 72.0 / dpi);
               HPDF_Page_Fill(page);
         }
       }

       // 保存 PDF
       HPDF_SaveToFile(pdf, filename.c_str());
       HPDF_Free(pdf);
   }

   int main() {
       cv::Mat image = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
       if (image.empty()) {
         std::cerr << "Failed to load image." << std::endl;
         return -1;
       }

       saveImageAsPDF(image, "output.pdf", 1200);
       return 0;
   }

### 注意事项:
- 确保图像是二值的,这样可以在 PDF 中保持简单。
- `libharu`默认情况下不支持压缩。如果需要压缩功能,可能需要手动实现压缩或考虑其他库。
- 使用上述代码时,请确保正确配置OpenCV和libharu的开发环境。

这将帮助您将二值图像转换为具有特定DPI的PDF。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-15 10:34:00

FishC 发表于 2024-11-14 20:26
除了 Cairo,还有其他库可以用来将图像输出为 PDF,并设置分辨率。常用的库之一是 **PoDoFo** 和 **Pop ...

这不知道什么问题,一个200mm * 60mm长宽1200dpi的二值图输出成pdf文件1个多G,而且打开根本不显示,是哪里出了问题?我用photoshop或者其它排版软件输出的PDF才几MB

FishC 发表于 2024-11-15 10:34:19

cxnet 发表于 2024-11-15 10:34
这不知道什么问题,一个200mm * 60mm长宽1200dpi的二 ...

输出PDF文件大且无法显示的问题,可能是因为直接将每个像素都作为一个绘图操作进行处理。这样处理图像时会生成大量绘图命令,从而增加文件的大小。为了避免这种情况,应该尝试压缩图像数据或使用效率更高的方法。

libharu库不支持直接保存二值图图像为PDF。因此,为了将图像添加到PDF,可以先将该图像转换为一种支持压缩的格式,比如JPEG或者带有压缩的PNG,然后再嵌入PDF。

在此之前,我们假设您已经将图像转换为PNG或JPEG格式。您需要确保图像最终是二元化的,并且可以通过OpenCV进行格式转换。

### 具体步骤

1. **转换并保存为PNG或JPEG格式**:

   使用OpenCV将图像作为PNG或JPEG格式保存:

   cv::Mat image = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
   if (image.empty()) {
       std::cerr << "Error: Could not open or find the image" << std::endl;
       return;
   }

   // 为确保二值化,使用阈值处理
   cv::threshold(image, image, 128, 255, cv::THRESH_BINARY);

   // 保存为PNG(PNG包含无损压缩)
   cv::imwrite("binary_image_converted.png", image);

2. **使用libharu将图像嵌入PDF:**

   #include <hpdf.h>
   #include <iostream>

   void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void *user_data) {
       std::cerr << "ERROR: error_no=" << error_no << ", detail_no=" << detail_no << std::endl;
   }

   int main() {
       HPDF_Doc pdf = HPDF_New(error_handler, nullptr);
       if (!pdf) {
         std::cerr << "Error: Cannot create pdf object" << std::endl;
         return 1;
       }

       HPDF_SetCompressionMode(pdf, HPDF_COMP_ALL);
       HPDF_Page page = HPDF_AddPage(pdf);
       HPDF_Page_SetSize(page, HPDF_PAGE_SIZE_A4, HPDF_PAGE_PORTRAIT);

       // 加载图像
       HPDF_Image image = HPDF_LoadPngImageFromFile(pdf, "binary_image_converted.png");
       float page_width = HPDF_Page_GetWidth(page);
       float page_height = HPDF_Page_GetHeight(page);
      float aspect_ratio = float(HPDF_Image_GetWidth(image)) / float(HPDF_Image_GetHeight(image));
       float width = page_width;
       float height = page_width / aspect_ratio;

       HPDF_Page_DrawImage(page, image, 0, (page_height - height) / 2, width, height);

       HPDF_SaveToFile(pdf, "output.pdf");
       HPDF_Free(pdf);
   }

### 说明

- 通过这种方法,将二值图像转换为一种可以优化存储的格式。
- 使用 `HPDF_SetCompressionMode(pdf, HPDF_COMP_ALL);` 以压缩PDF中的图像数据。
- 注意页面大小和图像大小的匹配,以确保图像在PDF上适当显示。这里根据A4页面自动调整大小。

这种方式会显著减小文件大小并提速。尝试调整输出图像的格式和压缩级别以达到最佳效果。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-15 11:00:37

FishC 发表于 2024-11-15 10:34
输出PDF文件大且无法显示的问题,可能是因为直接将每个像素都作为一个绘图操作进行处理。这样处理图像 ...

这种方法倒是可以,但是只能读取png的二值图吗?我试了一下TIF二值图加载不行,另外怎样将页面尺寸设置为图像的宽度和高度?也就是自定义页面尺寸

FishC 发表于 2024-11-15 11:00:51

cxnet 发表于 2024-11-15 11:00
这种方法倒是可以,但是只能读取png的二值图吗?我试 ...

在处理TIFF格式的二值图像时,OpenCV默认读取可能存在一些问题,因为TIFF文件有多种变体和压缩方式,OpenCV对某些TIFF格式的支持有限。可以尝试使用 `libtiff` 这样的专用库来读取TIFF文件。

一旦图像加载成功,您可以使用libharu来自定义页面尺寸。

### 使用libharu将自定义尺寸的图像输出为PDF

下面是一个具体示例:

#include <hpdf.h>
#include <iostream>
#include <opencv2/opencv.hpp>

void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void* user_data) {
    std::cerr << "PDF creation error: " << error_no << ", detail: " << detail_no << std::endl;
}

int main() {
    // 使用libtiff或其它途径加载TIFF图像文件
    cv::Mat image = cv::imread("binary_image.tif", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "Error loading image!" << std::endl;
      return -1;
    }

    // 确保图像是二值的
    cv::threshold(image, image, 128, 255, cv::THRESH_BINARY);

    // 临时保存为png文件
    std::string tempImagePath = "temp_image.png";
    cv::imwrite(tempImagePath, image);

    // 初始化libharu
    HPDF_Doc pdf = HPDF_New(error_handler, NULL);
    if (!pdf) {
      std::cerr << "Error creating PDF object!" << std::endl;
      return -1;
    }

    try {
      // 自定义页面大小,尺寸单位为点(1/72英寸)
      double pageWidth = image.cols * 1200.0 / 72.0;// 两次dpi转换确保单位为点
      double pageHeight = image.rows * 1200.0 / 72.0;

      HPDF_Page page = HPDF_AddPage(pdf);
      HPDF_Page_SetWidth(page, pageWidth);
      HPDF_Page_SetHeight(page, pageHeight);

      // 从PNG图像加载
      HPDF_Image pdfImage = HPDF_LoadPngImageFromFile(pdf, tempImagePath.c_str());
      HPDF_Page_DrawImage(page, pdfImage, 0, 0, pageWidth, pageHeight);

      // 保存PDF
      HPDF_SaveToFile(pdf, "output.pdf");
    } catch (...) {
      HPDF_Free(pdf);
      throw;
    }

    // 清理
    HPDF_Free(pdf);

    return 0;
}

### 说明

1. **TIFF读取**:若直接使用`cv::imread`读取TIFF遇到问题,考虑使用其他库如`libtiff`。
2. **自定义尺寸**:页面大小设置是基于图像分辨率和DPI的,可以根据图像的宽度和高度按比例设置。
3. **PNG中介**:这里将图像先另存为PNG,然后通过libharu将其加载到PDF中,以此来形成压缩。

在将图像转换为PDF时,通过先将其保存为压缩格式再加载会更高效,这样不仅能解决体积问题,也能确保生成的PDF文件较小而且格式正确。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-15 11:45:22

FishC 发表于 2024-11-15 11:00
在处理TIFF格式的二值图像时,OpenCV默认读取可能存在一些问题,因为TIFF文件有多种变体和压缩方式,Op ...

我的意思是HPDF_Image pdfImage这里不能直接加载TIF格式二值图,如果能直接将Mat图转为HPDF_Image也是可以的

FishC 发表于 2024-11-15 11:45:34

cxnet 发表于 2024-11-15 11:45
我的意思是HPDF_Image pdfImage这里不能直接加载TIF格 ...

在使用 libharu 时,的确存在不能直接加载特定格式图像(如TIFF)的限制。不过,我们可以尝试通过其他办法来实现部分目的,如将 `cv::Mat` 直接导出为支持的格式,然后嵌入到 PDF 中。下面是一个将 OpenCV 的 `cv::Mat` 转换为 PNG 临时文件并加载到 libharu 中的示例:

### 使用 OpenCV 和 libharu 来导出 PDF

1. **将 `cv::Mat` 保存为 PNG 格式**:首先将 Mat 图像保存为 PNG 格式,因其支持透明度且较广泛兼容。

#include <opencv2/opencv.hpp>
#include <hpdf.h>
#include <iostream>

void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void* user_data) {
    std::cerr << "Error: " << error_no << ", detail: " << detail_no << std::endl;
}

int main() {
    // 假设你有一个使用 OpenCV 加载的图像
    cv::Mat image = cv::imread("binary_image.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      std::cerr << "Failed to load image." << std::endl;
      return 1;
    }

    // 将图像写入 PNG 格式,以便于加载到 PDF 中
    std::vector<uchar> buf;
    cv::imencode(".png", image, buf);
    std::ofstream png_file("temp_image.png", std::ios::binary);
    png_file.write(reinterpret_cast<const char*>(buf.data()), buf.size());
    png_file.close();

    // 初始化 libharu
    HPDF_Doc pdf = HPDF_New(error_handler, NULL);
    if (!pdf) {
      std::cerr << "Failed to create PDF object." << std::endl;
      return 1;
    }

    // 添加页面并设置自定义尺寸
    HPDF_Page page = HPDF_AddPage(pdf);
    HPDF_Page_SetWidth(page, image.cols);
    HPDF_Page_SetHeight(page, image.rows);

    // 添加图像
    HPDF_Image pdfImage = HPDF_LoadPngImageFromFile(pdf, "temp_image.png");
    HPDF_Page_DrawImage(page, pdfImage, 0, 0, image.cols, image.rows);

    // 保存 PDF
    HPDF_SaveToFile(pdf, "output.pdf");

    // 清理
    HPDF_Free(pdf);
    std::remove("temp_image.png");

    return 0;
}

### 注意事项

- **加载 TIFF 图像**:OpenCV 的 `imread` 函数可以加载某些 TIFF 格式,但如果遇到问题,考虑转换为 PNG 后再进行操作。
- **临时文件的使用**:在此示例中,图像被保存为临时 PNG 文件,然后被 libharu 加载。这是一种桥接方法,符合 libharu 的图像格式支持。
- **文件清理**:确保在处理完成后删除临时文件,以保持良好的文件系统状态。

通过这种方式,可以灵活处理不同的图像格式,并有效利用现有工具(如 OpenCV 和 libharu)输出高质量的 PDF。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-15 11:55:35

FishC 发表于 2024-11-15 11:45
在使用 libharu 时,的确存在不能直接加载特定格式图像(如TIFF)的限制。不过,我们可以尝试通过其他 ...

是否可以通过HPDF_LoadRawImageFromMem这个来将Mat的图像转成HPDF_Image

FishC 发表于 2024-11-15 11:55:52

cxnet 发表于 2024-11-15 11:55
是否可以通过HPDF_LoadRawImageFromMem这个来将Mat的 ...

是的,您可以通过 `HPDF_LoadRawImageFromMem` 函数来将 OpenCV 的 `cv::Mat` 数据直接转换为 `HPDF_Image`。通过这种方法,您可以在内存中处理图像数据而不需要生成中间文件。

下面是如何使用 OpenCV 和 libharu 的一个示例,演示如何将 Mat 图像转换为 HPDF_Image 并添加到 PDF 页面中:

#include <opencv2/opencv.hpp>
#include <hpdf.h>

void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void* user_data) {
    std::cerr << "ERROR: error_no=" << error_no << ", detail_no=" << detail_no << std::endl;
}

int main() {
    // 假设这是您的 Mat 图像,并且已经加载为灰度(单通道)
    cv::Mat image = cv::imread("binary_image.tif", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "Failed to load image." << std::endl;
      return 1;
    }

    // 创建新的 PDF 文档
    HPDF_Doc pdf = HPDF_New(error_handler, nullptr);
    if (!pdf) {
      std::cerr << "Failed to create PDF object." << std::endl;
      return 1;
    }

    // 将 Mat 图像数据转为 HPDF_Image 对象
    HPDF_Page page = HPDF_AddPage(pdf);
    HPDF_REAL pdf_width = HPDF_Page_GetWidth(page);
    HPDF_REAL pdf_height = HPDF_Page_GetHeight(page);

    // 设置页面为图像尺寸
    HPDF_Page_SetWidth(page, image.cols);
    HPDF_Page_SetHeight(page, image.rows);

    // 将图像数据作为原始数据加载到 PDF
    HPDF_Image pdf_image = HPDF_LoadRawImageFromMem(
      pdf,
      image.data,                   // 图像数据指针
      image.cols,                   // 宽度
      image.rows,                   // 高度
      HPDF_CS_DEVICE_GRAY,          // 颜色空间:灰度
      8                           // 每个分量的位数,8 位表示灰度
    );

    HPDF_Page_DrawImage(page, pdf_image, 0, 0, image.cols, image.rows);

    // 将 PDF 保存到磁盘
    HPDF_SaveToFile(pdf, "output.pdf");

    // 清理和释放资源
    HPDF_Free(pdf);

    return 0;
}

### 关键点:
1. **图像加载**:确保使用 `cv::imread` 加载图像为灰度格式,以便能直接转换为 `HPDF_Image`。

2. **原始数据加载**:使用 `HPDF_LoadRawImageFromMem`,传入图像数据指针、图像的宽高、色彩空间等参数。

3. **页面设置与绘制**:调整 PDF 页面大小以适应图像,使用 `HPDF_Page_DrawImage` 将图像绘制到 PDF 页面。

这种方法可以避免中间文件的创建,需要注意的是库的内存管理部分,确保在操作完成后正确释放资源。如有需要,也可以对图像适度处理,比如调整分辨率以减少文件大小。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-15 12:51:27

FishC 发表于 2024-11-15 11:55
是的,您可以通过 `HPDF_LoadRawImageFromMem` 函数来将 OpenCV 的 `cv::Mat` 数据直接转换为 `HPDF_Im ...

试了一下显示无法解析的外部符号HPDF_LoadRawImageFromMem是什么原因呢?
    HPDF_MMgr mmgr = pdf->mmgr;
    HPDF_Xref xref = pdf->xref;
    HPDF_Image pdf_image=HPDF_Image_LoadRawImageFromMem(mmgr, image.data, xref, image.cols, image.rows, HPDF_CS_DEVICE_GRAY, 8);

FishC 发表于 2024-11-15 12:51:45

cxnet 发表于 2024-11-15 12:51
试了一下显示无法解析的外部符号HPDF_LoadRawImageFro ...

`HPDF_LoadRawImageFromMem` 是一个加载原始图像数据到 PDF 的方式,但直接调用这个函数可能需要用到 libharu 库的内部结构体和实现细节,如果遇到无法解析的外部符号错误,可能是因为:

1. **链接问题**:确保您已经正确链接了 libharu 的库文件。在项目设置中,确保 libharu 的库路径和头文件路径完全包含。

2. **函数可见性**:如果您尝试调用的是 `HPDF_LoadRawImageFromMem` 的变种(或内部实现函数,如 `HPDF_Image_LoadRawImageFromMem`),请确认其在所使用的 libharu 版本中是否公开。libharu 并不总是公开所有内部函数。公开的 API 函数应在库的文档中有所描述。

3. **正确调用 API**:以确保您使用的是公共API,可以尝试如下代码方法来处理图像:

   a. 将 Mat 数据转化为一个支持的格式(如 RGB)。

   b. 使用 libharu 提供的标准接口来加载图像数据。

如果 `HPDF_LoadRawImageFromMem` 不可用,考虑将 `cv::Mat` 存为临时 PNG 文件,然后使用 `HPDF_LoadPngImageFromFile` 读取:
   
#include <opencv2/opencv.hpp>
#include <hpdf.h>

// ... error handling functions ...

void saveMatAsPDF(const cv::Mat& image, const std::string& pdfFileName) {
    HPDF_Doc pdf = HPDF_New(error_handler, nullptr);
    if (!pdf) {
      std::cerr << "Cannot create PDF object" << std::endl;
      return;
    }

    HPDF_SetCompressionMode(pdf, HPDF_COMP_ALL);

    // 将图像写入临时文件
    cv::imwrite("temp_image.png", image);

    HPDF_Page page = HPDF_AddPage(pdf);

    // 设置页面大小和方向
    HPDF_Page_SetWidth(page, image.cols);
    HPDF_Page_SetHeight(page, image.rows);

    // 加载 PNG 临时文件
    HPDF_Image pdfImage = HPDF_LoadPngImageFromFile(pdf, "temp_image.png");

    // 绘制图像到页面
    HPDF_Page_DrawImage(page, pdfImage, 0, 0, image.cols, image.rows);

    // 保存 PDF 文件
    HPDF_SaveToFile(pdf, pdfFileName.c_str());

    // 清理
    HPDF_Free(pdf);

    // 删除临时文件
    remove("temp_image.png");
}

int main() {
    cv::Mat image = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
      std::cerr << "Failed to load image" << std::endl;
      return -1;
    }

    saveMatAsPDF(image, "output.pdf");

    return 0;
}

该示例展示了如何保存临时 PNG 文件并将其嵌入到 PDF 中,以解决直接从 OpenCV `cv::Mat` 数据加载图像的限制。这样可以避开 `HPDF_LoadRawImageFromMem` 函数的潜在问题。请确认您使用的 libharu 版本支持 `HPDF_LoadPngImageFromFile`。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1] 2
查看完整版本: C++图像保存问题