cxnet 发表于 2024-11-17 11:42:50

关于TIF图片处理问题

下面的代码可以将bmp24位图片进行膨胀处理,但仅限于bmp格式和24位色彩模式,请问怎样修改它才能让它直接处理TIF格式的1位色彩模式,或者8位色彩模式的二值图,因为图片比较大的时侯 photoshop没办法保存为bmp格式,只能保存为TIF格式

#include "pch.h"
#include "framework.h"
#include "TTS.h"
#include "TTSDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#pragma warning(disable:4996);//出现fopen错误使用此代码

void CTTSDlg::OnBnClickedButton1()
{
        int        m_nWidth;                //图像实际宽度
        int        m_nHeight;                //图像实际高度
        int        m_nDrawWidth;        //图像显示宽度
        int        m_nDrawHeight;        //图像显示高度
        DWORD m_nImage;                //图像数据的字节数 只含位图
        DWORD m_nSize;      //图像文件大小
        int m_nLineByte;    //图像一行所占字节数
        int        m_nBitCount;    //图像每个像素所占位数
        int        m_nPalette;   //位图实际使用的颜色表中的颜色数
        CBitmap m_bitmaplin;   //创建临时位图对象进行处理
        CString BmpNameLin;    //保存图像副本文件

        BYTE* m_pImage;         //读入图片数据后的指针
        BITMAPFILEHEADER bfh;   //全局变量文件头
        BITMAPINFOHEADER bih;   //全局变量信息头

        FILE* fp = fopen("test1.bmp", "rb");
        if (fp == 0)
        {
                AfxMessageBox(_T("无法打开文件!"), MB_OK, 0);
                return;
        }
        //读取文件头 解决BMP格式倒置的方法
        fread(&bfh.bfType, sizeof(WORD), 1, fp);
        fread(&bfh.bfSize, sizeof(DWORD), 1, fp);
        fread(&bfh.bfReserved1, sizeof(WORD), 1, fp);
        fread(&bfh.bfReserved2, sizeof(WORD), 1, fp);
        fread(&bfh.bfOffBits, sizeof(DWORD), 1, fp);
        //图像文件的总字节数
        m_nSize = bfh.bfSize;
        //判断是否是bmp格式图片
        if (bfh.bfType != 0x4d42)   //'BM'
        {
                AfxMessageBox(_T("不是BMP格式图片!"), MB_OK, 0);
                return;
        }
        //读取信息头
        fread(&bih.biSize, sizeof(DWORD), 1, fp);
        fread(&bih.biWidth, sizeof(LONG), 1, fp);
        fread(&bih.biHeight, sizeof(LONG), 1, fp);
        fread(&bih.biPlanes, sizeof(WORD), 1, fp);
        fread(&bih.biBitCount, sizeof(WORD), 1, fp);
        fread(&bih.biCompression, sizeof(DWORD), 1, fp);
        fread(&bih.biSizeImage, sizeof(DWORD), 1, fp);
        fread(&bih.biXPelsPerMeter, sizeof(LONG), 1, fp);
        fread(&bih.biYPelsPerMeter, sizeof(LONG), 1, fp);
        fread(&bih.biClrUsed, sizeof(DWORD), 1, fp);
        fread(&bih.biClrImportant, sizeof(DWORD), 1, fp);
        if (bih.biSize != sizeof(bih))
        {
                AfxMessageBox(_T("本结构所占用字节数出现错误"));
                return;
        }
        //位图压缩类型,必须是 0(不压缩) 1(BI_RLE8压缩类型)或2(BI_RLE压缩类型)之一
        if (bih.biCompression == BI_RLE8 || bih.biCompression == BI_RLE4)
        {
                AfxMessageBox(_T("位图被压缩!"));
                return;
        }
        //获取图像高宽和每个像素所占位数
        m_nHeight = bih.biHeight;
        m_nWidth = bih.biWidth;
        m_nDrawHeight = bih.biHeight;
        m_nDrawWidth = bih.biWidth;
        m_nBitCount = bih.biBitCount;   //每个像素所占位数
        //计算图像每行像素所占的字节数(必须是32的倍数)
        m_nLineByte = (m_nWidth * m_nBitCount + 31) / 32 * 4;
        //图片大小 调用系统自带的文件头 BITMAPFILEHEADER bfh; BITMAPINFOHEADER bih;
        //否则用 BITMAPFILEHEADER_ bfh; BITMAPINFOHEADER_ bih;要 m_nImage = m_nLineByte * m_nHeight - 2;
        m_nImage = m_nLineByte * m_nHeight;
        //位图实际使用的颜色表中的颜色数 biClrUsed
        m_nPalette = 0;                     //初始化
        if (bih.biClrUsed)
                m_nPalette = bih.biClrUsed;
        //申请位图空间 大小为位图大小 m_nImage
        //malloc只能申请4字节的空间 (未知)
        m_pImage = (BYTE*)malloc(m_nImage);

        int num;//记录每一行需要填充的字节
        if (m_nWidth * 3 % 4 != 0)
        {
                num = 4 - m_nWidth * 3 % 4;
        }
        else
        {
                num = 0;
        }

        //打开临时的图片
        FILE* fpw = fopen("output.bmp", "wb+");
        fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
        fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
        fread(m_pImage, m_nImage, 1, fp);
        unsigned char* ImageSize;
        ImageSize = new unsigned char;
        int x, y, val, xx, yy, temp;
        for (y = 1; y < m_nHeight - 1; y++)
        {
                for (x = 1; x < m_nWidth - 1; x++)//由于使用3*3的结构元素,防止越界,不处理最左边和最右边的的像素
                {
                        val = 0;
                        for (int j = 0; j < 2; j++)
                        {
                                yy = y + j - 1;
                                for (int i = 0; i < 2; i++)
                                {
                                        xx = x + i - 1;
                                        if (m_pImage[(xx + yy * m_nWidth) * 3 + yy * num] > val)//因为结构元素都是0,所以原图像中各点与对应结构元素想加还是原图像本身,
                                                //找和的最大值作为点(x,y)的灰度
                                        {
                                                val = m_pImage[(xx + yy * m_nWidth) * 3 + yy * num];
                                        }
                                }
                        }
                        ImageSize[(x + y * m_nWidth) * 3 + y * num] = unsigned char(val);
                        ImageSize[(x + y * m_nWidth) * 3 + y * num + 1] = unsigned char(val);
                        ImageSize[(x + y * m_nWidth) * 3 + y * num + 2] = unsigned char(val);
                }
        }

        fwrite(ImageSize, m_nImage, 1, fpw);

        fclose(fp);
        fclose(fpw);
}

FishC 发表于 2024-11-17 11:42:58

要修改代码以处理TIF格式的1位或8位色彩模式的二值图,可以使用OpenCV库来简化操作。主要思路是通过OpenCV读取TIF文件并进行膨胀处理,最终将结果保存回TIF文件。以下是修改后的代码:

首先,确保在项目中配置了OpenCV库。

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

void processTif() {
    // 使用OpenCV读取TIF图像
    cv::Mat image = cv::imread("input.tif", cv::IMREAD_UNCHANGED);

    if(image.empty()) {
      std::cout << "无法打开文件!" << std::endl;
      return;
    }

    // 确保图像是8位或1位二值图
    if(image.type() != CV_8UC1 && image.type() != CV_8UC3) {
      std::cout << "图像类型不兼容!" << std::endl;
      return;
    }

    // 创建一个膨胀操作的结构元素 (3x3)
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));

    cv::Mat result;
    // 对图像进行膨胀操作
    cv::dilate(image, result, element);

    // 保存处理后的图像
    if(!cv::imwrite("output.tif", result)) {
      std::cout << "保存图像失败!" << std::endl;
    }
}

int main() {
    processTif();
    return 0;
}

### 说明:

1. **OpenCV库**: 使用OpenCV可以方便地处理多种格式的图像,包括TIF,并支持对一位或八位图像进行处理。

2. **cv::imread和cv::imwrite**: 分别用于读取和保存图像文件。`cv::IMREAD_UNCHANGED`确保读取的图像不受默认颜色转换的影响。

3. **cv::dilate**: 使用膨胀操作对图像进行处理,具体操作由`cv::getStructuringElement`函数定义的结构元素决定。

通过这种方式,处理图像的复杂度大大降低,并可以避免手动处理文件头、内存分配等低级细节。

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

cxnet 发表于 2024-11-17 11:45:06

FishC 发表于 2024-11-17 11:42
要修改代码以处理TIF格式的1位或8位色彩模式的二值图,可以使用OpenCV库来简化操作。主要思路是通过OpenCV ...

排除opencv方法,因为opencv方式打开特别大的图片时很费时间,所以想使用现在这种方式

FishC 发表于 2024-11-17 11:45:17

cxnet 发表于 2024-11-17 11:45
排除opencv方法,因为opencv方式打开特别大的图片时很 ...

如果你想通过现有的方式读取和处理TIF格式的图像,而不依赖OpenCV,可以考虑使用libtiff库。libtiff是一个专门用于处理TIFF文件格式的库,可以高效读取和写入TIFF图像。

下面是一个如何使用libtiff来读取和处理TIF文件的示例代码,假设你已经安装并配置了libtiff:

#include <tiffio.h>
#include <stdlib.h>
#include <stdio.h>

void processTIFF(const char* inputFileName, const char* outputFileName) {
    TIFF* tif = TIFFOpen(inputFileName, "r");
    if (tif) {
      uint32 width, height;
      size_t npixels;
      uint32* raster;

      TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
      TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
      npixels = width * height;
      raster = (uint32*)_TIFFmalloc(npixels * sizeof(uint32));

      if (raster != NULL) {
            if (TIFFReadRGBAImage(tif, width, height, raster, 0)) {
                // 膨胀操作
                for (uint32 y = 1; y < height - 1; y++) {
                  for (uint32 x = 1; x < width - 1; x++) {
                        // 获取像素值,进行膨胀
                        uint32 maxVal = 0;
                        for (int j = -1; j <= 1; j++) {
                            for (int i = -1; i <= 1; i++) {
                              uint32 currentVal = raster[(y + j) * width + (x + i)];
                              if (currentVal > maxVal) {
                                    maxVal = currentVal;
                              }
                            }
                        }
                        raster = maxVal;
                  }
                }

                // 将处理后的图像写入新文件
                TIFF* out = TIFFOpen(outputFileName, "w");
                if (out) {
                  TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
                  TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
                  TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 4);
                  TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 8);
                  TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
                  TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
                  TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);

                  TIFFWriteEncodedStrip(out, 0, raster, npixels * sizeof(uint32));
                  TIFFClose(out);
                }
            }
            _TIFFfree(raster);
      }
      TIFFClose(tif);
    } else {
      fprintf(stderr, "Could not open the specified TIFF file\n");
    }
}

int main() {
    processTIFF("input.tif", "output.tif");
    return 0;
}

请注意,这是一个基础示例,处理的是32位RGBA TIFF图像。如果你要处理1位或8位的图像,你需要根据具体的TIFF图像格式更改读取和膨胀操作的方式。此外,为了成功编译和运行,你需要在编译时链接libtiff库。

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

cxnet 发表于 2024-11-17 11:48:58

FishC 发表于 2024-11-17 11:45
如果你想通过现有的方式读取和处理TIF格式的图像,而不依赖OpenCV,可以考虑使用libtiff库。libtiff是 ...

这里面怎样将它修改成2*2的结构元素进行膨胀

FishC 发表于 2024-11-17 11:49:18

cxnet 发表于 2024-11-17 11:48
这里面怎样将它修改成2*2的结构元素进行膨胀

要使用2x2的结构元素进行膨胀,你需要遍历每个像素,并检查该像素及其右、下、和右下区域的像素值(因为2x2结构元素在平面上意味着要检查这些位置)。如果其中任一像素有非零值,那么中央像素就设置为最大值。在二值图像的情况下,这意味着任何“1”就使中心值成为“1”。

以下是使用libtiff库的示例代码,假设你已经安装并正确配置了libtiff库:

#include <tiffio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>

void processTIFF(const char* inputFileName, const char* outputFileName) {
    TIFF* tif = TIFFOpen(inputFileName, "r");
    if (!tif) {
      printf("Could not open input file %s\n", inputFileName);
      return;
    }

    uint32 width, height;
    size_t nPixels;
    uint32* raster;

    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
    nPixels = width * height;
    raster = (uint32*)_TIFFmalloc(nPixels * sizeof(uint32));

    if (!raster) {
      printf("Memory allocation error\n");
      TIFFClose(tif);
      return;
    }

    // Read image data
    if (!TIFFReadRGBAImage(tif, width, height, raster, 0)) {
      printf("Could not read image\n");
      _TIFFfree(raster);
      TIFFClose(tif);
      return;
    }

    // Creating output raster with the same size
    uint32* outputRaster = (uint32*)_TIFFmalloc(nPixels * sizeof(uint32));
    if (!outputRaster) {
      printf("Memory allocation error\n");
      _TIFFfree(raster);
      TIFFClose(tif);
      return;
    }

    // Copy the original image before processing
    memcpy(outputRaster, raster, nPixels * sizeof(uint32));

    // Dilation with a 2x2 structure
    for (uint32 y = 0; y < height - 1; y++) {
      for (uint32 x = 0; x < width - 1; x++) {
            int isSet = 0;
            uint32 idx = y * width + x;
            
            // Examine 2x2 area
            isSet |= (raster & 0xFF); // pixel at (x, y)
            isSet |= (raster & 0xFF); // pixel at (x+1, y)
            isSet |= (raster & 0xFF); // pixel at (x, y+1)
            isSet |= (raster & 0xFF); // pixel at (x+1, y+1)

            if (isSet) {
                // Set gray-scale to max (assuming 8-bit per channel grayscale)
                outputRaster = 0xFFFFFFFF; // white: RGBA all channels max
                outputRaster = 0xFFFFFFFF;
                outputRaster = 0xFFFFFFFF;
                outputRaster = 0xFFFFFFFF;
            }
      }
    }

    TIFF* out = TIFFOpen(outputFileName, "w");
    if (!out) {
      printf("Could not open output file %s\n", outputFileName);
      _TIFFfree(raster);
      _TIFFfree(outputRaster);
      TIFFClose(tif);
      return;
    }

    TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
    TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
    TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 4);
    TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 8);
    TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
    TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);

    tsize_t lineBytes = 4 * width; // RGBA image
    unsigned char* buf = NULL;   // buffer used to store the horizontal line

    // Allocate buffer for 1 line of span
    buf = (unsigned char*)_TIFFmalloc(lineBytes);
    if (!buf) {
      printf("Memory allocation error\n");
      _TIFFfree(raster);
      _TIFFfree(outputRaster);
      TIFFClose(tif);
      TIFFClose(out);
      return;
    }

    // Write the image
    for (uint32 row = 0; row < height; row++) {
      memcpy(buf, &outputRaster[(height-row-1)*width], lineBytes);   
      if (TIFFWriteScanline(out, buf, row, 0) < 0) {
            break;
      }
    }

    _TIFFfree(buf);
    _TIFFfree(raster);
    _TIFFfree(outputRaster);
    TIFFClose(tif);
    TIFFClose(out);
}

int main() {
    processTIFF("input.tif", "output.tif");
    return 0;
}

在这个示例中,遍历图像时,检查当前位置及其上、左、右上方像素的值。若任一位置为1,表示需要设置结果位置为255(即白色)。注意,这段代码是基于假设图像为8位灰度(每像素一个字节),需要根据实际的TIFF格式做调整,比如样本字节大小和像素格式。

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

cxnet 发表于 2024-11-17 11:55:31

FishC 发表于 2024-11-17 11:49
要使用2x2的结构元素进行膨胀,你需要遍历每个像素,并检查该像素及其右、下、和右下区域的像素值(因 ...

为什么这段代码里的2*2的结构元素膨胀后比opencv的2*2的结构元素所膨胀得要多差不多1像素呢?相当于opencv的3*3的结构元素膨胀效果,我要把它减为1*1结构元素应该修改哪个参数

FishC 发表于 2024-11-17 11:55:41

cxnet 发表于 2024-11-17 11:55
为什么这段代码里的2*2的结构元素膨胀后比opencv的2*2 ...

这段代码的膨胀操作实际上是基于2x2邻域(正方形顶部左角开始),它影响的区域是比2x2邻域要多的。这是因为代码遍历了邻近像素并以相对简单的方式扩展了图像。

如果要缩小为1x1结构元素,即简单的自扩展(实际上不扩展任何像素),那么实际上不会改变图像,因为1x1结构元素等同于对像素自身进行检查,没有邻域的影响。所以“1x1膨胀”不会对图像产生变化。

对于只考虑2x2实际的膨胀:

1. 检查每个单独像素而不是树立看整个2x2局部。
2. 确保只有自身参与"扩张",这意味着只修改特定点。

但是,如果你的意思是将不同形状的结果生成,查看代码时注意:

for (int j = 0; j < 2; j++) {
    yy = y + j - 1;
    for (int i = 0; i < 2; i++) {
      xx = x + i - 1;
      if (m_pImage > val) {
            val = m_pImage;
      }
    }
}

在上面这个片段中,如果你想控制邻域的大小和形状,调整这两个for循环即可。`1x1`不改变任何邻域,你可以直接输出原始图像而无需额外处理,或者移除所有的`val`赋值步骤,保留`outputRaster`生成部分即可。

如果你遇到了图形扩张结果不是预期问题,可能是邻域处理中访问过多外背景,因此要更谨慎的设置起始和边界条件,如果您需要精细化,使用专业工具(如OpenCV的形态学操作)可能会更合适。但在`libtiff`仅用于读取和原生操作部分,它不提供OpenCV复杂操作。

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

cxnet 发表于 2024-11-17 12:11:24

FishC 发表于 2024-11-17 11:55
这段代码的膨胀操作实际上是基于2x2邻域(正方形顶部左角开始),它影响的区域是比2x2邻域要多的。这是 ...

用这种方式试了一下大尺寸的TIF图片会出错,看来这种方式也只能处理比较小的图片。

用CImage方式加载TIF二值8位或者1位二值图进行2*2结构元素进行膨胀应该怎样写代码。想试试这种方式是否可行

FishC 发表于 2024-11-17 12:11:38

cxnet 发表于 2024-11-17 12:11
用这种方式试了一下大尺寸的TIF图片会出错,看来这种 ...

使用 `CImage` 类来处理 TIFF 图像可以利用 GDI+ 来进行图像加载和处理,从而支持较大的图片。下面是一个使用 `CImage` 和 GDI+ 实现简单膨胀的示例代码:

#include <afxwin.h>   // MFC core and standard components
#include <gdiplus.h>    // GDI+

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

using namespace Gdiplus;

void ProcessTIFwithCImage(const wchar_t* inputFileName, const wchar_t* outputFileName) {
    // Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    CImage image;
    HRESULT hr = image.Load(inputFileName);
    if (FAILED(hr)) {
      printf("Failed to load image\n");
      return;
    }

    int width = image.GetWidth();
    int height = image.GetHeight();

    // Create a new image for the output
    CImage outputImage;
    outputImage.Create(width, height, 8);

    // Iterate through each pixel, excluding the boundary pixels
    for (int y = 0; y < height - 1; y++) {
      for (int x = 0; x < width - 1; x++) {
            // Check the 2x2 neighborhood for dilation
            BYTE pixelValue = image.GetPixel(x, y) == 0xFFFFFF ? 1 : 0;   // Assuming binary white is the forepixel

            // Check each pixel in 2x2 area
            if (pixelValue == 0) {
                if ((x + 1 < width && image.GetPixel(x + 1, y) == 0xFFFFFF) ||
                  (y + 1 < height && image.GetPixel(x, y + 1) == 0xFFFFFF) ||
                  (x + 1 < width && y + 1 < height && image.GetPixel(x + 1, y + 1) == 0xFFFFFF)) {
                  pixelValue = 1;
                }
            }

            // Set the result pixel value
            COLORREF color = pixelValue ? RGB(255, 255, 255) : RGB(0, 0, 0);
            outputImage.SetPixel(x, y, color);
      }
    }

    // Save the processed image to a new file
    outputImage.Save(outputFileName);

    // Cleanup GDI+
    GdiplusShutdown(gdiplusToken);
}

int main() {
    ProcessTIFwithCImage(L"input.tif", L"output.tif");
    return 0;
}

### 代码说明:
1. **初始化GDI+**:在使用任何 GDI+ 功能之前,必须进行初始化。

2. **加载图像**:使用 `CImage` 来加载 TIFF 文件。

3. **创建输出图像**:创建一个新的 `CImage` 对象来存储输出结果。

4. **处理图像**:通过遍历图像,为每个非边界像素检查其自身及相邻像素的值,执行膨胀操作。

5. **保存结果**:最后将处理后的图像保存为一个新的 TIFF 文件。

请注意,确保使用 `CImage` 和 GDI+ 方法时,处理二值图像时要保证图像是用正确的格式(比如前景为白色 `0xFFFFFF` 和背景为黑色 `0x000000`)。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 关于TIF图片处理问题