• 原文作者:aircraft ... MFC图像处理CImage类常用操作 CImage类头文件为#include<atlimage.h> CImage类读取图片CImage.Load("src.bmp"); CImage类保存图片CImage.Save("dst.jpg")...

    原文作者:aircraft

    原文地址:https://www.cnblogs.com/DOMLX/p/9598974.html

    MFC图像处理CImage类常用操作

     

     

    CImage类头文件为#include<atlimage.h>

    CImage类读取图片CImage.Load("src.bmp");

    CImage类保存图片CImage.Save("dst.jpg");

     

    一.CImage类实例拷贝数据到另外一个CImage类实例

    复制代码

    bool ImageCopy(const CImage &srcImage, CImage &destImage)
    {
        int i, j;//循环变量
        if (srcImage.IsNull())
            return FALSE;
        //源图像参数
        BYTE* srcPtr = (BYTE*)srcImage.GetBits();
        int srcBitsCount = srcImage.GetBPP();
        int srcWidth = srcImage.GetWidth();
        int srcHeight = srcImage.GetHeight();
        int srcPitch = srcImage.GetPitch();
        //销毁原有图像
        if (!destImage.IsNull())
        {
            destImage.Destroy();
        }
        //创建CImage类新图像并分配内存
        if (srcBitsCount == 32)   //支持alpha通道
        {
            destImage.Create(srcWidth, srcHeight, srcBitsCount, 1);
        }
        else
        {
            destImage.Create(srcWidth, srcHeight, srcBitsCount, 0);
        }
        //加载调色板
        if (srcBitsCount <= 8 && srcImage.IsIndexed())//需要调色板
        {
            RGBQUAD pal[256];
            int nColors = srcImage.GetMaxColorTableEntries();
            if (nColors>0)
            {
                srcImage.GetColorTable(0, nColors, pal);
                destImage.SetColorTable(0, nColors, pal);//复制调色板程序
            }
        }
        //目标图像参数
        BYTE *destPtr = (BYTE*)destImage.GetBits();
        int destPitch = destImage.GetPitch();
        //复制图像数据
        for (i = 0; i<srcHeight; i++)
        {
            memcpy(destPtr + i*destPitch, srcPtr + i*srcPitch, abs(srcPitch));
        }
    
        return TRUE;
    }

    复制代码

     

     

    二.CImage类实例处理图像间的腐蚀运算

    复制代码

    //腐蚀运算
    
    //width:图像宽;height:图像高;矩形掩膜的边长(2*r+1)void erosion(CImage image, int width, int height, int r)
    {
        int i, j, m, n;
        int flag;
        //unsigned char * pBuff = tempBuff;
        CImage Buff;
        ImageCopy(image, Buff);
        //dataCopy(image, pBuff, width, height);
        byte *pImg = (byte *)image.GetBits();
        byte *pBuff = (byte *)Buff.GetBits();
        int step = image.GetPitch();
        //int height = image.GetHeight();
        //int width = image.GetWidth();
        for (i = r; i<height - r; i++)
        {
            for (j = r; j<width - r; j++)
            {
                flag = 1;
                for (m = i - r; m <= i + r; m++)
                {
                    for (n = j - r; n <= j + r; n++)
                    {
                        //if (!pBuff[i*width + j] || !pBuff[m*width + n])
                        if (!*(pBuff + i*step + j) || !*(pBuff + m*step + n))
                        {
                            flag = 0;
                            break;
                        }
                    }
                }
                if (flag == 0)
                {
                    *(pImg + i*step + j) = 0;
                }
                else
                {
                    *(pImg + i*step + j) = 255;
                }
            }
        }
    }

    复制代码

     

    三.CImage类实例处理图像间的膨胀运算

     

    复制代码

    //膨胀运算
    //width:图像宽;height:图像高;矩形掩膜的边长(2*r+1)
    void diate(CImage image, int width, int height, int r)
    {
        int i, j, m, n;
        int flag;
        //unsigned char * pBuff = tempBuff;
    
        CImage Buff;
        ImageCopy(image, Buff);
        //dataCopy(image, pBuff, width, height);
        byte *pImg = (byte *)image.GetBits();
        byte *pBuff = (byte *)Buff.GetBits();
        int step = image.GetPitch();
        //int height = image.GetHeight();
        //int width = image.GetWidth();
        //dataCopy(image, pBuff, width, height);
        for (i = r; i<height - r; i++)
        {
            for (j = r; j<width - r; j++)
            {
                flag = 1;
                for (m = i - r; m <= i + r; m++)
                {
                    for (n = j - r; n <= j + r; n++)
                    {
                        if (255 == *(pBuff + i*step + j) || 255 == *(pBuff + m*step + n))
                        {
                            flag = 0;
                            break;
                        }
                    }
                }
                if (flag == 0)
                {
                    *(pImg + i*step + j) = 255;
                }
                else
                {
                    *(pImg + i*step + j) = 0;
                }
            }
        }
    }

    复制代码

     

     

    四.CImage类实例处理图片遍历赋值操作

    按 Ctrl+C 复制代码

     

    按 Ctrl+C 复制代码

     

    五.用CImage类实例遍历生成手指静脉边缘图

    复制代码

    #define  mlen  9 //模板长度
    //加长扩展的水平边缘检测模板
    int upperEdgeOperator[mlen * 3] =
    {
        -1, -1, -1, -1, -1, -1, -1, -1, -1,
        0, 0, 0, 0, 0, 0, 0, 0, 0,
        1, 1, 1, 1, 1, 1, 1, 1, 1
    };
    int lowerEdgeOperator[mlen * 3] =
    {
        1, 1, 1, 1, 1, 1, 1, 1, 1,
        0, 0, 0, 0, 0, 0, 0, 0, 0,
        -1, -1, -1, -1, -1, -1, -1, -1, -1
    };
    
    int findEdgesHorizontal(CImage& imgSrc, CImage& imgDst)
    {
        int maxY = imgSrc.GetHeight();
        int maxX = imgSrc.GetWidth();
    
        if (!imgDst.IsNull())
        {
            imgDst.Destroy();
        }
        imgDst.Create(maxX, maxY, 8, 0);//图像大小与imgSrc相同,每个像素占1字节
    
        if (imgDst.IsNull())
            return FALSE;
    
        byte *pImg = (byte *)imgSrc.GetBits();
        byte *pDstImg = (byte *)imgDst.GetBits();
        int step = imgSrc.GetPitch();
        int height = imgSrc.GetHeight();
        int width = imgSrc.GetWidth();
        int sum = 0;
        unsigned char val = 0;
        //初始化
        for (int i = 0; i<maxY; i++)
            for (int j = 0; j<maxX; j++)
                *(pDstImg + i*step + j) = 0;
    
        //找上边缘
        for (int i = 1; i <maxY / 2 - 1; i++)
        {
            for (int j = 4; j < maxX - 4; j++)
            {
                sum = 0;
                for (int m = -1; m <= 1; m++)
                {
                    for (int n = -mlen / 2; n <= mlen / 2; n++)
                    {
                        sum += *(pImg + (i + m)*step + (j + n))*upperEdgeOperator[(m + 1)*mlen + (n + mlen / 2)];
                    }
                }
                sum = sum < 0 ? 0 : sum;
                sum = sum > 255 ? 255 : sum;
                val = unsigned char(sum);
                *(pDstImg + i*step + j) = val;
            }
        }
        //找下边缘
        for (int i = maxY / 2 + 1; i <maxY - 1; i++)
        {
            for (int j = 4; j < maxX - 4; j++)
            {
                sum = 0;
                for (int m = -1; m <= 1; m++)
                {
                    for (int n = -mlen / 2; n <= mlen / 2; n++)
                    {
                        sum += *(pImg + (i + m)*step + (j + n))*upperEdgeOperator[(m + 1)*mlen + (n + mlen / 2)];;
                    }
                }
                sum = sum < 0 ? 0 : sum;
                sum = sum > 255 ? 255 : sum;
                val = unsigned char(sum);
                *(pDstImg + i*step + j) = val;
            }
        }
    
        return TRUE;
    }

    复制代码

     

    六.CImage图像类实例将RGB图转为灰度(gary)图

    复制代码

    BOOL ImageToGray(CImage& imgSrc, CImage& imgDst)
    {
        int maxY = imgSrc.GetHeight();
        int maxX = imgSrc.GetWidth();
    
        if (!imgDst.IsNull())
        {
            imgDst.Destroy();
        }
        imgDst.Create(maxX, maxY, 8, 0);//图像大小与imgSrc相同,每个像素占1字节
    
        if (imgDst.IsNull())
            return FALSE;
    
        //为imgDst构造256阶灰度调色表
        RGBQUAD ColorTab[256];
        for (int i = 0; i<256; i++)
        {
            ColorTab[i].rgbBlue = ColorTab[i].rgbGreen = ColorTab[i].rgbRed = i;
        }
        imgDst.SetColorTable(0, 256, ColorTab);
    
        byte* pDataSrc = (byte*)imgSrc.GetBits(); //获取指向图像数据的指针
        byte* pDataDst = (byte*)imgDst.GetBits();
        int pitchSrc = imgSrc.GetPitch(); //获取每行图像占用的字节数 +:top-down;-:bottom-up DIB
        int pitchDst = imgDst.GetPitch();
        int bitCountSrc = imgSrc.GetBPP() / 8;  // 获取每个像素占用的字节数
        int bitCountDst = imgDst.GetBPP() / 8;
        if ((bitCountSrc != 3) || (bitCountDst != 1))
            return FALSE;
        int tmpR, tmpG, tmpB, avg;
        for (int i = 0; i<maxX; i++)
        {
            for (int j = 0; j<maxY; j++)
            {
                tmpR = *(pDataSrc + pitchSrc*j + i*bitCountSrc);
                tmpG = *(pDataSrc + pitchSrc*j + i*bitCountSrc + 1);
                tmpB = *(pDataSrc + pitchSrc*j + i*bitCountSrc + 2);
                avg = (int)(tmpR + tmpG + tmpB) / 3;
                *(pDataDst + pitchDst*j + i*bitCountDst) = avg;
            }
        }
        return TRUE;
    }

    复制代码

     

    七.CImage类转opencv Mat类  以及Mat类转CImage类

    复制代码

    #include "stdafx.h"
    #include <opencv2/opencv.hpp>
    #include "CimgMat.h"
    
    
    void CimgMat::MatToCImage(Mat& mat, CImage& cimage)
    {
        if (0 == mat.total())
        {
            return;
        }
    
    
        int nChannels = mat.channels();
        if ((1 != nChannels) && (3 != nChannels))
        {
            return;
        }
        int nWidth = mat.cols;
        int nHeight = mat.rows;
    
    
        //重建cimage
        cimage.Destroy();
        cimage.Create(nWidth, nHeight, 8 * nChannels);
    
    
        //拷贝数据
    
    
        uchar* pucRow;                                    //指向数据区的行指针
        uchar* pucImage = (uchar*)cimage.GetBits();        //指向数据区的指针
        int nStep = cimage.GetPitch();                    //每行的字节数,注意这个返回值有正有负
    
    
        if (1 == nChannels)                                //对于单通道的图像需要初始化调色板
        {
            RGBQUAD* rgbquadColorTable;
            int nMaxColors = 256;
            rgbquadColorTable = new RGBQUAD[nMaxColors];
            cimage.GetColorTable(0, nMaxColors, rgbquadColorTable);
            for (int nColor = 0; nColor < nMaxColors; nColor++)
            {
                rgbquadColorTable[nColor].rgbBlue = (uchar)nColor;
                rgbquadColorTable[nColor].rgbGreen = (uchar)nColor;
                rgbquadColorTable[nColor].rgbRed = (uchar)nColor;
            }
            cimage.SetColorTable(0, nMaxColors, rgbquadColorTable);
            delete[]rgbquadColorTable;
        }
    
    
        for (int nRow = 0; nRow < nHeight; nRow++)
        {
            pucRow = (mat.ptr<uchar>(nRow));
            for (int nCol = 0; nCol < nWidth; nCol++)
            {
                if (1 == nChannels)
                {
                    *(pucImage + nRow * nStep + nCol) = pucRow[nCol];
                }
                else if (3 == nChannels)
                {
                    for (int nCha = 0; nCha < 3; nCha++)
                    {
                        *(pucImage + nRow * nStep + nCol * 3 + nCha) = pucRow[nCol * 3 + nCha];
                    }
                }
            }
        }
    }
    
    void CimgMat::CImageToMat(CImage& cimage, Mat& mat)
    {
        if (true == cimage.IsNull())
        {
            return;
        }
    
    
        int nChannels = cimage.GetBPP() / 8;
        if ((1 != nChannels) && (3 != nChannels))
        {
            return;
        }
        int nWidth = cimage.GetWidth();
        int nHeight = cimage.GetHeight();
    
    
        //重建mat
        if (1 == nChannels)
        {
            mat.create(nHeight, nWidth, CV_8UC1);
        }
        else if (3 == nChannels)
        {
            mat.create(nHeight, nWidth, CV_8UC3);
        }
    
    
        //拷贝数据
    
    
        uchar* pucRow;                                    //指向数据区的行指针
        uchar* pucImage = (uchar*)cimage.GetBits();        //指向数据区的指针
        int nStep = cimage.GetPitch();                    //每行的字节数,注意这个返回值有正有负
    
    
        for (int nRow = 0; nRow < nHeight; nRow++)
        {
            pucRow = (mat.ptr<uchar>(nRow));
            for (int nCol = 0; nCol < nWidth; nCol++)
            {
                if (1 == nChannels)
                {
                    pucRow[nCol] = *(pucImage + nRow * nStep + nCol);
                }
                else if (3 == nChannels)
                {
                    for (int nCha = 0; nCha < 3; nCha++)
                    {
                        pucRow[nCol * 3 + nCha] = *(pucImage + nRow * nStep + nCol * 3 + nCha);
                    }
                }
            }
        }
    }

    复制代码

     

    八.纯图像数据赋值给CImage后的初始化,并且写入调色板

    复制代码

    bool InitalImage(CImage &image, int width, int height)
    {
        if (image.IsNull())
            image.Create(width, height, 8);
        else
        {
            if (width <= 0 || height <= 0)
                return false;
            else if (image.GetHeight() == width && image.GetWidth() == height)
                return true;
            else
            {
                image.Destroy();
                image.Create(width, height, 8);
            }
        }
        //写入调色板
        RGBQUAD ColorTable[256];
        image.GetColorTable(0, 256, ColorTable);
        for (int i = 0; i < 256; i++)
        {
            ColorTable[i].rgbBlue = (BYTE)i;
            ColorTable[i].rgbGreen = (BYTE)i;
            ColorTable[i].rgbRed = (BYTE)i;
        }
        image.SetColorTable(0, 256, ColorTable);
        return true;
    }

    复制代码

     

    九.根据MFC控件大小CImage类实例图片显示

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    if(m_image2.IsNull())    //判断有无图像 

            return

       

    // 取得客户区尺寸 

    CRect zcRect; 

    GetDlgItem(IDC_STATIC_PIC2)->GetClientRect(&zcRect); 

       

    // 将图像显示在界面之上 

    m_image2.Draw(GetDlgItem(IDC_STATIC_PIC2)->GetDC()->m_hDC, 

                                zcRect.left, 

                                zcRect.top, 

                                zcRect.Width(), 

                                zcRect.Height());

     

    十.根据CImage类实例图片调整控件大小显示图片

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    if(m_image1.IsNull()) 

        return

       

    // 将整控件调整为与图像同一尺寸  

    GetDlgItem(IDC_STATIC_PIC)->SetWindowPos(NULL, 

                        0,0,m_image1.GetWidth(), m_image1.GetHeight(),  

                        SWP_NOMOVE);    

       

    CRect zcRect; 

    GetDlgItem(IDC_STATIC_PIC)->GetClientRect(&zcRect); 

       

    m_image1.Draw(GetDlgItem(IDC_STATIC_PIC)->GetDC()->m_hDC, 

                                zcRect.left, 

                                zcRect.top, 

                                zcRect.Width(), 

                                zcRect.Height()); 

     

    十一.CImage类与CBitmap转换

     

    复制代码

        CImage nImage;
        nImage.Load(imgFilePath);
     
        HBITMAP hBitmap=nImage.Detach(); // 获得位图句柄 用以转换
     
     
        // 转换方式一:
        CBitmap bmp;
        bmp.DeleteObject();
        bmp.Attach(hBitmap); //  转换为CBitmap对象
     
     
        // 转换方式二:
         
        CBitmap *pBitmap=CBitmap::FromHandle(nImage.m_hBitmap);

    复制代码

     

    十二.CImage类实例实现图像二值化

     

    复制代码

    void imgBinary(CImage image, int imgW, int imgH, int threshold)
    {
        int i;
        int index = 0;
        byte *pImg = (byte *)image.GetBits();
        int step = image.GetPitch();
        int height = image.GetHeight();
        int width = image.GetWidth();
        for (i = 0; i<height*width; i++)
        {
            *(pImg + index) = *(pImg + index)>threshold ? 255 : 0;
            index++;
        }
    
    }

    复制代码

     

     

    十三.CImage实现自己的argmax函数----求图像一定高度区域中某一列遇到的第一个最大像素值得坐标并返回

     

    复制代码

    int  argmax(CImage &image,int Top,int Bottom,int x)
    {
        int max = 0;
        int tem;
        int pos = 0;
        byte *pImg = (byte *)image.GetBits();
        int step = image.GetPitch();
        int height = image.GetHeight();
        int width = image.GetWidth();
        if (Top > 0 && Top < height && Bottom > 0 && Bottom < height && x > 0 && x < width)
        {
            for (int i = Top; i < Bottom; ++i)
            {
                tem = *(pImg + i*step + x);
                if (tem > max)
                {
                    max = tem;
                    pos = i;
                }
    
            }
            return pos;
        }
        else
        {
            return FALSE;
        }
            
    }

    复制代码

     

     

     

     

     十四.CImage类创建指定长宽图像并初始化调色板

     

    复制代码

    bool InitalImage(CImage &image, int width, int height)
    {
        if (image.IsNull())
            image.Create(width, height, 8);
        else
        {
            if (width <= 0 || height <= 0)
                return false;
            else if (image.GetHeight() == width && image.GetWidth() == height)
                return true;
            else
            {
                image.Destroy();
                image.Create(width, height, 8);
            }
        }
        //写入调色板
        RGBQUAD ColorTable[256];
        image.GetColorTable(0, 256, ColorTable);
        for (int i = 0; i < 256; i++)
        {
            ColorTable[i].rgbBlue = (BYTE)i;
            ColorTable[i].rgbGreen = (BYTE)i;
            ColorTable[i].rgbRed = (BYTE)i;
        }
        image.SetColorTable(0, 256, ColorTable);
        return true;
    }

    复制代码

     

     

     

     十五.将存放在一维指针数组里的图像数据赋值给CImage类实例

    复制代码

    void LoadImageData(CImage &image, unsigned char * data)
    {
        if (data == nullptr)
            return;
        byte *pS;
        byte *pImg = (byte *)image.GetBits();
        int step = image.GetPitch();
        int height = image.GetHeight();
        int width = image.GetWidth();
        for (int i = 0; i < image.GetHeight(); ++i)
        {
            pS = data + i * width;
            for (int j = 0; j < image.GetWidth(); ++j)
            {
                *(pImg + i*step + j) = pS[j];
            }
        }
    }

    复制代码

     

    十六.CImage类自己实现图片的裁剪

     

    复制代码

    //裁剪roi区域
    void RoiCut(CImage &image, CImage &roiImg, int heightTop,int heightDown,int widthBegin,int widthEnd)
    {
        InitalImage(roiImg, heightDown - heightTop + 1, widthBegin - widthEnd + 1);
        byte *pImg = (byte *)image.GetBits();
        byte *pRoi = (byte *)roiImg.GetBits();
        int step = image.GetPitch();
        int height = image.GetHeight();
        int width = image.GetWidth();
        int index = 0;
        for (int i = heightTop; i < heightDown; i++)
        {
            for (int j = widthBegin; j < widthEnd; j++)
            {
                *(pRoi + index) = *(pImg + i*step + j);
                index++;
            }
        }
    
    }

    复制代码

     

    展开全文
  • 最近老师让做一个处理图像的小软件,用了大概一个星期做出来了成品,MFC上我算是新手,一路摸索过来也算是收获不少吧,现在软件也做完了,给自己总结一下在学习过程中的收获和不足。 首先放出一下软件的运行截图,...
  • 本程序是基于MFC的单文档(SDI)程序,每次可以打开耽搁图片文件,可以保存,另存为图片文件。支持打开jpg,png,ico,bmp,jpeg等格式的图片。图片打开后的界面如下: 2.图片缩放: 通过滚动鼠标滚轮,可以调节图像的...
  • 新建一个MFC工程,添加两个picture控件和一个按钮控件。 二、添加变量 1、为两个picture控件添加control变量,类型为CStatic,变量名分别为m_picture1和m_picture2. 2、添加保存图片路径和名称变量,CString类型,...
  • 主要详细介绍了BMP图片格式,同时使用C++和MFC显示BMP格式,主要结合自己的《数字图像处理》课程和以前的项目叙述讲解.一.BMP图片格式定义BMP文件格式是Windows操作系统推荐和支持的标准图像文件格式,是一种将内存或显...
  • MFC平台的图像处理 2020-07-06 16:27:38
    本次项目采用Opencv+MFC框架,通过对源代码的学习和借鉴,完成了对图像进行点处理、邻域处理、二值化、二值图像处理、形态学处理、彩色图像处理、综合处理、视频采集播放这些常用功能。
  • 数字图像处理作业用opencv + MFC图像处理界面设计与实现,点击按钮(Button)在Picture Control上显示处理前后的对比图像 // ImageprocessingDlg.cpp : 实现文件 // #include &quot;stdafx.h&quot; #...
  • MFC图像处理 2020-06-03 23:32:19
    适合MFC 图像处理初学者,自己也是菜鸟级的。有基本的点运算
  • 本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行讲解,主要通过MFC单文档视图实现显示BMP图像增强处理,包括图像普通平滑、高斯平滑、不同算子的图像锐化知识...
  • 该资源主要参考我的博客【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放详解,博客地址http://blog.csdn.net/eastmount/article/details/46345299 主要讲述基于VC++6.0 MFC图像处理的应用知识,要...
  • opencv+MFC图像处理程序 2020-06-03 23:32:55
    使用opncv2.49和VS2010,利用MFC框架做的图像处理软件
  • 七.MFC图像增强之图像普通平滑、高斯平滑、Laplacian、Sobel、Prewitt锐化详解,博客地址http://blog.csdn.net/eastmount/article/details/46378783 主要讲述基于VC++6.0 MFC图像处理的应用知识,要通过MFC单文档...
  • 该资源主要参考我的博客【数字图像处理】四.MFC对话框绘制灰度直方图,博客地址http://blog.csdn.net/eastmount/article/details/46237463 讲述VC++ 6.0关于数字图像处理的灰度直方图(中值灰度、平均灰度)、灰度、...
  • 五.MFC图像点运算之灰度线性变化、灰度非线性变化、阈值化和均衡化处理详解,博客地址http://blog.csdn.net/eastmount/article/details/46312145 主要讲述基于VC++6.0 MFC图像处理的应用知识,通过MFC单文档视图实现...
  • MFC图像处理 反色和红色 2020-06-03 23:32:42
    。。。。。。。MFC图像处理 反色和红色
  • 本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行回忆讲解,主要通过MFC单文档视图实现点击弹出对话框绘制BMP图片的灰度直方图,再获取平均灰度、中指灰度和...
  • OpenCV MFC 实现图像处理,在文档中直接进行图像处理,综合了边缘检测,Hough变换等等一些图像处理功能
  • 使用Windows编程进行各种图像图形的处理 内含全部源程序代码
  • 在我之前的一篇文章中MFC图像处理-图像扫描显示之基本窗口已经介绍了我怎么创建基本的窗口,可以模仿着创建响应图像处理的基本操作按钮。   图像移动显示的方式有很多种,可以根据自己的需要设计自己的显示算法,...
  • 本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行讲解,主要通过MFC单文档视图实现显示BMP格式图片,并通过Bitmap进行灰度处理、图片采样和量化功能。...
1 2 3 4 5 ... 20
收藏数 14,692
精华内容 5,876