OpenCV聚合图片和文本(2021.3.4)

1、目标需求

        原始图片origin.jpg,大小为 531 x 461 像素
在这里插入图片描述

        需要添加以下文本到图片的正下方后,共同组成一张图片。

名称:1号水质传感器
经度:81.624
纬度:44.227

        最终希望得到如下Word中这样的整张图片。
在这里插入图片描述

2、环境搭建 (Windows 10 64位操作系统下)

        这里使用的环境配置主要包括:VS 2010OpenCV 2.4.9C++

2.1 安装Visual Studio 2010(VS 2015更好)

在这里插入图片描述
在这里插入图片描述

2.2 安装OpenCV(使用预编译库)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
        OpenCV官网下点击上方Library下的Releases就可以查看发行版的各版本OpenCV,其中包括文档、源码、Windows系统下的安装文件、iOS系统下的安装文件以及Android系统下的安装文件和Java开发文档,比如选择了OpenCV 2.4.9,选择其中的Windows后就可以下载所需的OpenCV-2.4.9.exe,同时可以点击下方的Tutorials后可以根据官方文档指导来配置OpenCV环境。
在这里插入图片描述
在这里插入图片描述
        下载好opencv-2.4.9.exe后就可以双击进行安装,安装会弹出一个窗口,选择自己提取到的目录D:\Program Files (x86)\OpenCV 2.4.9\ 后确定即可,安装完成后文件夹opencv下包含build文件夹和sources文件夹,里面的文件内容如下图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
                vc10        对应        Visual Studio 2010
                vc11        对应        Visual Studio 2012
                vc12        对应        Visual Studio 2013
                vc14        对应        Visual Studio 2015

        vcVisual Studio版本的对应关系如上图所示,然后设置系统的环境变量,由于这里使用的是Visual Studio 2010,所以应该选择vc10,因此需要将D:\Program Files (x86)\OpenCV 2.4.9\opencv\build\x64\vc10\binD:\Program Files (x86)\OpenCV 2.4.9\opencv\build\x86\vc10\bin添加到Path中,因为bin文件夹就是编译生成的二进制binary文件夹,里面包含了dll文件,其中封装了opencv生成的核心函数,放到Path系统环境变量中便于其他使用OpenCV函数的项目来通过路径寻找到对应的dll文件并在运行时进行调用。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
        非必须:当然如果C盘空间较多,也可以尝试将所需要的D:\Program Files (x86)\OpenCV 2.4.9\opencv\build\x86\vc10\bin下所有的dll文件复制到C:\Windows\System32目录下,这个操作本质上和设置Path环境变量的作用效果相同,都是为了在项目运行时找到所依赖的dll。(如果项目运行提示找不到dll的话,可以尝试这样做,32位C++项目将OpenCV下对应VS版本的x86下bin中全部dll复制到C:\Windows\System32目录下;若为64位项目则应将OpenCV下对应VS版本的x64下bin中全部dll复制到C:\Windows\SysWOW64目录下)
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3、在Visual Studio中利用OpenCV来构建C++应用程序实现需求

3.1 VS新建C++Win32控制台应用程序并配置OpenCV目录

        可以参考官方教程,打开Visual Studio 2010后,选择文件->新建后会打开新建项目窗口,在新建项目窗口中选择Visual C++后点击中间的Win 32控制台应用程序,在下方输入项目名称CVProject,并选择位置后点击确定(值得注意的是:x86对应32位应用程序,x64对应64位应用程序,然后相应地设置lib库目录和include包含目录即附加依赖项.lib并添加对应值bin目录到环境变量Path
在这里插入图片描述

        弹出Win 32应用程序向导窗口,点击下一步
在这里插入图片描述
        应用程序类型选择控制台应用程序,附加选项选择空项目后点击确定即可。
在这里插入图片描述
        然后在解决方案资源管理器中找到项目下的源文件,右键点击添加后选择新建项,会弹出添加新项窗口
在这里插入图片描述
        在添加新项窗口中选择C++文件(.cpp)后,在下方名称后的文本框中输入main.cpp后再点击添加
在这里插入图片描述
        现在在项目CVProject上右键选择属性后弹出CVProject属性也窗口,找到VC++目录下的包含目录库目录,包含目录即需要添加OpenCV的include目录(D:\Program Files (x86)\OpenCV 2.4.9\opencv\build\include
D:\Program Files (x86)\OpenCV 2.4.9\opencv\build\include\opencv
D:\Program Files (x86)\OpenCV 2.4.9\opencv\build\include\opencv2),而库目录需要添加OpenCV的lib目录(D:\Program Files (x86)\OpenCV 2.4.9\opencv\build\x86\vc10\lib
在这里插入图片描述
        同时在链接器的输入下找到附加依赖项,在附加依赖项中输入如下lib后点击应用并确定即可(如果项目配置为Debug,选择opencv_ts249d.lib;如果为Release,选择opencv_ts249.lib,带d的为Debug配置项目所需要的依赖库)
opencv_objdetect249.lib opencv_ts249.lib opencv_video249.lib opencv_nonfree249.lib opencv_ocl249.lib opencv_photo249.lib opencv_stitching249.lib opencv_superres249.lib opencv_videostab249.lib opencv_calib3d249.lib opencv_contrib249.lib opencv_core249.lib opencv_features2d249.lib opencv_flann249.lib opencv_gpu249.lib opencv_highgui249.lib opencv_imgproc249.lib opencv_legacy249.lib opencv_ml249.lib
在这里插入图片描述

3.2 实现代码并运行

        最后将下面的C++代码复制到main.cpp中,在项目上右键点击生成后没有报错就启动本地Windows调试器运行项目。

#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void GetStringSize(HDC hDC, const char* str, int* w, int* h)
{
    SIZE size;
    GetTextExtentPoint32A(hDC, str, strlen(str), &size);
    if (w != 0) *w = size.cx;
    if (h != 0) *h = size.cy;
}
 
void MyPutText(Mat &dst, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline)
{
    CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));
 
    int x, y, r, b;
    if (org.x > dst.cols || org.y > dst.rows) return;
    x = org.x < 0 ? -org.x : 0;
    y = org.y < 0 ? -org.y : 0;
 
    LOGFONTA lf;
    lf.lfHeight = -fontSize;
    lf.lfWidth = 0;
    lf.lfEscapement = 0;
    lf.lfOrientation = 0;
    lf.lfWeight = 5;
    lf.lfItalic = italic;   //斜体
    lf.lfUnderline = underline; //下划线
    lf.lfStrikeOut = 0;
    lf.lfCharSet = DEFAULT_CHARSET;
    lf.lfOutPrecision = 0;
    lf.lfClipPrecision = 0;
    lf.lfQuality = PROOF_QUALITY;
    lf.lfPitchAndFamily = 0;
    strcpy_s(lf.lfFaceName, fn);
 
    HFONT hf = CreateFontIndirectA(&lf);
    HDC hDC = CreateCompatibleDC(0);
    HFONT hOldFont = (HFONT)SelectObject(hDC, hf);
 
    int strBaseW = 0, strBaseH = 0;
    int singleRow = 0;
    char buf[1 << 12];
    strcpy_s(buf, str);
    char *bufT[1 << 12];  // 这个用于分隔字符串后剩余的字符,可能会超出。
    //处理多行
    {
        int nnh = 0;
        int cw, ch;
 
        const char* ln = strtok_s(buf, "\n",bufT);
        while (ln != 0)
        {
            GetStringSize(hDC, ln, &cw, &ch);
            strBaseW = max(strBaseW, cw);
            strBaseH = max(strBaseH, ch);
 
            ln = strtok_s(0, "\n",bufT);
            nnh++;
        }
        singleRow = strBaseH;
        strBaseH *= nnh;
    }
 
    if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
    {
        SelectObject(hDC, hOldFont);
        DeleteObject(hf);
        DeleteObject(hDC);
        return;
    }
 
    r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
    b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
    org.x = org.x < 0 ? 0 : org.x;
    org.y = org.y < 0 ? 0 : org.y;
 
    BITMAPINFO bmp = { 0 };
    BITMAPINFOHEADER& bih = bmp.bmiHeader;
    int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));
 
    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = strBaseW;
    bih.biHeight = strBaseH;
    bih.biPlanes = 1;
    bih.biBitCount = 24;
    bih.biCompression = BI_RGB;
    bih.biSizeImage = strBaseH * strDrawLineStep;
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;
 
    void* pDibData = 0;
    HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);
 
    CV_Assert(pDibData != 0);
    HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);
 
    //color.val[2], color.val[1], color.val[0]
    SetTextColor(hDC, RGB(255, 255, 255));
    SetBkColor(hDC, 0);
    //SetStretchBltMode(hDC, COLORONCOLOR);
 
    strcpy_s(buf, str);
    const char* ln = strtok_s(buf, "\n",bufT);
    int outTextY = 0;
    while (ln != 0)
    {
        TextOutA(hDC, 0, outTextY, ln, strlen(ln));
        outTextY += singleRow;
        ln = strtok_s(0, "\n",bufT);
    }
    uchar* dstData = (uchar*)dst.data;
    int dstStep = dst.step / sizeof(dstData[0]);
    unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
    unsigned char* pStr = (unsigned char*)pDibData + x * 3;
    for (int tty = y; tty <= b; ++tty)
    {
        unsigned char* subImg = pImg + (tty - y) * dstStep;
        unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
        for (int ttx = x; ttx <= r; ++ttx)
        {
            for (int n = 0; n < dst.channels(); ++n){
                double vtxt = subStr[n] / 255.0;
                int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
                subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
            }
 
            subStr += 3;
            subImg += dst.channels();
        }
    }
 
    SelectObject(hDC, hOldBmp);
    SelectObject(hDC, hOldFont);
    DeleteObject(hf);
    DeleteObject(hBmp);
    DeleteDC(hDC);
}
void ProduceNewPicturewithText(std::string filename,const char* rowtext[])
{
	cv::Mat img1 = imread(filename);
	std::cout << img1.rows << "," << img1.cols << std::endl;//输出第一张图片的高度和宽度
	cv::Mat img2(100, img1.cols, CV_8UC3, Scalar(255,255,255));//初始化图像
	int flag = 0;//1表示顺时针旋转90度,0表示逆时针旋转90度
	MyPutText(img2, rowtext[0], Point(img2.cols / 3 + 10,img2.rows / 5), CV_RGB(0, 0, 0), 15, "黑体", false, false);
	MyPutText(img2, rowtext[1], Point(img2.cols / 3 + 10,img2.rows / 5 + 30), CV_RGB(0, 0, 0), 15, "黑体", false, false);
	MyPutText(img2, rowtext[2], Point(img2.cols / 3 + 10,img2.rows / 5 + 60), CV_RGB(0, 0, 0), 15, "黑体", false, false);//给图片添加三行文字

	cv::Mat temp1,temp2,img_left,img_right;
	cv::transpose(img1, temp1); cv::flip(temp1, img_left,flag);//将第一张图片逆时针旋转90度
	cv::transpose(img2, temp2); cv::flip(temp2, img_right,flag);//将第二章图片逆时针旋转90度
	cv::imshow("第一张图片逆时针旋转90度", img_left);
	cv::imshow("第二张图片逆时针旋转90度", img_right);

	cv::Mat img_merge,outImg_left, outImg_right;
    Size size(img_left.cols + img_right.cols, MAX(img_left.rows, img_right.rows));
	img_merge.create(size, CV_MAKETYPE(img_left.depth(), 3));
	img_merge = Scalar::all(0);
	outImg_left = img_merge(Rect(0, 0, img_left.cols, img_left.rows));
	outImg_right = img_merge(Rect(img_left.cols, 0, img_right.cols, img_right.rows));
 
	if(img_left.type() == CV_8U)
	{
		cvtColor(img_left, outImg_left, CV_GRAY2BGR);
	}
	else
	{
		img_left.copyTo(outImg_left);
	}
 
	if(img_right.type() == CV_8U)
	{
		cvtColor(img_right, outImg_right, CV_GRAY2BGR);
	}
	else
	{
		img_right.copyTo(outImg_right);
	}
	cv::Mat middleImg,resultImg;
	cv::transpose(img_merge, middleImg); cv::flip(middleImg,resultImg,flag+1);//最后将图片顺时针旋转90度
    cv::imshow("Merge Result", resultImg);
	cv::imwrite("E:\\Merge Result.jpg",resultImg);
}
void main()
{
	std::string filename = "E:\\origin.png";
	const char* rowtext[3];
	//char* rowtext1 = "名称:1号水质传感器";
	std::string rowtext1 = "名称:";
	std::string rowtext2 = "经度:";
	std::string rowtext3 = "纬度:";
	rowtext1+="2号水质传感器";
	rowtext[0] = rowtext1.c_str();//字符串赋值给Const char *
	rowtext2+="81.624";
	rowtext[1] = rowtext2.c_str();
	rowtext3+="44.227";
	rowtext[2] = rowtext3.c_str();
	ProduceNewPicturewithText(filename,rowtext);
	std::cout << "Success!" << std::endl;
	cv::waitKey(0);
	//txt文件里如果有文本,需要保存成ANSI格式,才能读取txt中的文本
	system("pause");
}

在这里插入图片描述
在这里插入图片描述

3.3 运行结果

在这里插入图片描述
在这里插入图片描述
        程序运行后会在E盘保存生成的图片文本聚合文件,打开E盘进行查看并用照片编辑器打开,如下图所示,目标达成,还算满足需求。
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值