稿子文字左右对称翻转_Matlab/OpenCV (2021-09-06)
Matlab/OpenCV 实现稿子文字左右对称翻转 2021.9.6
- 1、为什么要对文字进行左右对称翻转?(需求)
- 2、针对稿子的三种思路
- 3、具体实践(方式1、文本类;方式2、图片类)
- 3.1 文本类方式(使用Word)
- 3.2 图片类方式(Matlab/OpenCV)
- 3.2.1 图片类代码实现
- 3.2.1.1 Matlab(图片类)
- 3.2.1.2 OpenCV(图片类)
- 3.2.2 图片类结果C#打开滚动查看
- 3.2.2.1 C# 长图片滚动查看器代码
- 3.2.2.1.1 Form1.Designer.cs
- 3.2.2.1.2 Form1.cs
- 3.2.2.1.3 Program.cs
- 3.2.2.2 C#运行界面查看文本反转图片结果(Matlab和OpenCV)
1、为什么要对文字进行左右对称翻转?(需求)
在日常生活中,文字就是从左到右、从上到下的方式呈现在大家的视野当中,当然我们也都习以为然。然而,由于人们普遍认清了小孔成像、镜面反射、倒影等自然现象的机理并乐意接收,某些具体的生活应用场景则需要让文字以反序、颠倒的方式来展现,比如课程的现场录制。
中国大学慕课平台 | 现场录课 |
举例来说,相信很多大学生都使用过中国大学慕课平台,里面的课程种类丰富,大多以提前录制的视频来讲解PPT为主,然而由于受现场人员、环境气氛、心理因素等影响,有工作人员在摄像、有工作人员在帮忙等等,总之有的老师在现场录制视频的过程当中,不可避免地需要查看提前准备的稿子以便忘记相关的流程及备课讲解顺序,从而按部就班地讲好所有该讲的知识点和方法,同时也能提高课程视频录制的效率。
以下是自己亲身经历课程录制过程中的四点细节(略微总结,可能并不通用):
- 稿子(提前准备好);
- 场地、服装和灯光;
- 工作人员积极配合摄像和提示;
- 为了让主讲人念稿子更加自然流畅,旁边工作人员负责将文字投影动态播放,本质上将稿子对应的文字左右翻转;
2、针对稿子的三种思路
为了实现文字左右翻转的效果,首先需要知道汉字本身也是一种编码,每个汉字在计算机系统都有唯一的字符编码(主要是Unicode统一码),具体了解可参考我的一篇博客:常用编码学习,当然这些编码早已形成了一种体系结构。
针对如下稿子内容:
课改驱走旧陈俗,教育迎来艳阳春。遥望今后的课改之路,或许我们仍像刚走路的孩子一样步履蹒跚;或许我们
仍要付出比别人更多的意想不到的困难,但亲爱的老师们,请别忘记课改给我们带来的一份份惊喜与感动吧!他正和我
们一起奏响动听的乐章;它正和我们一道描绘多彩的画卷;它正和我一同谱写壮丽的诗篇。让我们再一次感谢课改,祝
福课改!我们满怀信心、满怀激情、满怀憧憬,将课改进行到底!
思路一(不可行):如果我们想要将汉字翻转(左右、上下)的结果保存到编码库中,那么每次使用直接将其插入到相应的位置不就可以了吗?但反过来讲,由于每个汉字经过上下或左右翻转后会出现好多个结果,每个结果都保存到编码库中有些不太现实,毕竟编码库的基本原则要求精简,就像线性代数里线性空间讲到的基,需要相互独立正交且无相关性最好;同时以我现在的能力还无法立刻做到,思虑再三,为了现实性,果断放弃这种想法;
思路二(可行):针对现有稿子中的文本,通过对文本进行旋转、对称、翻转等操作实现,可借助常用办公系列软件Office来实现;
思路三(可行):将稿子中的文本逐行添加到二维图像(矩阵)即图片中,然后文本和图片就是一个整体,这时可以采用计算机图像处理的方法来得到想要翻转后的文本效果。
3、具体实践(方式1、文本类;方式2、图片类)
3.1 文本类方式(使用Word)
打开word,输入稿子内容,然后选择上方菜单栏下布局->文字方向,如上图所示,可以看到文字方向中的效果并没有我们想要的;因此考虑使用文本框的方式来实现:首先插入一个文本框,将稿子内容粘贴到文本框中,之后选中文本框后点击鼠标右键设置形状格式,找到三维旋转,设置X旋转180度即可,效果如下。
Word左右翻转前 | Word左右翻转后 |
3.2 图片类方式(Matlab/OpenCV)
3.2.1 图片类代码实现
3.2.1.1 Matlab(图片类)
稿子.txt (ANSI格式)
课改驱走旧陈俗,教育迎来艳阳春。遥望今后的课改之路,或许
我们仍像刚走路的孩子一样步履蹒跚;或许我们仍要付出比别人更多
的意想不到的困难,但亲爱的老师们,请别忘记课改给我们带来的一
份份惊喜与感动吧!他正和我们一起奏响动听的乐章;它正和我们一
道描绘多彩的画卷;它正和我一同谱写壮丽的诗篇。让我们再一次感
谢课改,祝福课改!我们满怀信心、满怀激情、满怀憧憬,将课改进
行到底!
首先新建一个记事本文件,将稿子内容粘贴其中,并控制每行文字的数量与word一致,然后记得一定要保存为ANSI格式。(注意:电脑可能默认保存为UTF-8格式,然后导致Matlab读取中文文本乱码,不信自己可以试试哦,然后最终另存为ANSI格式的记事本文件即可正常读取中文文本,亲测不乱码哦)。
稿子.txt(UTF-8编码) | Matlab读取结果(UTF-8编码) |
稿子.txt(ANSI编码) | Matlab读取结果(ANSI编码) |
完整的Matlab代码如下,直接运行即可实现稿子文本翻转效果。
%第一步,获取稿子.txt中文本的行数
fid = fopen('稿子.txt'); %打开文本文件
INDEX = 0;
while ~feof(fid)str = fgetl(fid); % 读取一行, str是字符串INDEX = INDEX+1;
end
fontsize = 18;
rowdistance = 50;
%第二步,生成黑色背景图
m=zeros(100*INDEX,900);
x = 100;
y = 50;
figure;
imshow(m);
%第三步,读取稿子.txt中文本写到背景图上
fid = fopen('稿子.txt'); %打开文本文件
INDEX = 0;
while ~feof(fid)str = fgetl(fid); % 读取一行, str是字符串disp(str);text(x,y,str,'horiz','left','color','w','FontSize',fontsize);INDEX = INDEX+1;y = y + rowdistance;
end
fclose(fid);
frame = getframe(gcf);%获取figure界面
im = frame2im(frame);% 转为图像
imshow(im);
imwrite(im,'Normalgaozi.jpg');% 保存
%第四步,对图像做水平垂直镜像即可
InputImage = imread('Normalgaozi.jpg');
J2=flipdim(InputImage,2);%原图像的垂直镜像
figure;
imshow(J2);
imwrite(J2,'Mirrorgaozi.jpg');% 保存
从Matlab运行结果可以看出,生成稿子文本图片并且水平垂直翻转成功。
3.2.1.2 OpenCV(图片类)
完整的OpenCV代码如下,直接运行解析稿子.txt中内容生成文本图片,之后上下反转,然后逆时针旋转180度即可实现文本翻转效果图片的生成。
#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
#include <core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.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, std::string 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];const char *Mystr = str.c_str();strcpy_s(buf, Mystr);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);Mystr = str.c_str();strcpy_s(buf, Mystr);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 main()
{int rowdistance = 0;cv::Mat img2(800, 660, CV_8UC3, Scalar(0,0,0));//初始化图像ifstream in("E:\\稿子.txt");string filename; string line;int rowcount=0;if(in) // 有该文件{ while (getline (in, line)) // line中不包括每行的换行符 { cout << line << endl;MyPutText(img2, line, Point(30,rowdistance), CV_RGB(255,255,255), 20, "楷体", false, false);rowcount++;rowdistance +=40;}}else // 没有该文件 { cout <<"no such file" << endl; }int flag = 0;//1表示顺时针旋转90度,0表示逆时针旋转90度cv::imshow("原图", img2);cv::imwrite("E:\\Origin Result.jpg",img2);cv::Mat temp1,temp2,img_right;cv::flip(img2, temp1,flag);//先上下垂直翻转cv::transpose(temp1, temp2); cv::flip(temp2, img_right,flag);//逆时针旋转90度cv::transpose(img_right, temp2); cv::flip(temp2, img_right,flag);//逆时针旋转90度cv::imshow("背景图加文字的制作", img_right);cv::imwrite("E:\\Merge Result.jpg",img_right);cv::waitKey(0);system("pause");
}
从OpenCV运行结果可以看出,生成稿子文本图片并且水平垂直翻转成功。
3.2.2 图片类结果C#打开滚动查看
C#设置picturebox滚动条来实现查看大图片,打开Visual Studio 2015,新建C# Windows窗体应用项目,向Form1中添加一个Label控件、一个TextBox控件、两个Button控件、一个Panel控件和PictureBox控件。按照如下步骤设置控件属性:
(1)选中 picturebox 控件,将 picturebox 放在 panel 上;
(2)选中 panel 控件,在属性窗口中,将 panel 的 AutoScroll 属性设置为 ture;
(3)选中picturebox控件,将 picturebox 的 SizeMode 属性设置为 AutoSize;
(4)将 picturebox 的 Dock 属性设置为None(必须为None,设置为Fill不会出现滚动条)。
最终设置好的效果如下图所示。
3.2.2.1 C# 长图片滚动查看器代码
3.2.2.1.1 Form1.Designer.cs
namespace DisplayLongPicture
{partial class Form1{/// <summary>/// 必需的设计器变量。/// </summary>private System.ComponentModel.IContainer components = null;/// <summary>/// 清理所有正在使用的资源。/// </summary>/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows 窗体设计器生成的代码/// <summary>/// 设计器支持所需的方法 - 不要修改/// 使用代码编辑器修改此方法的内容。/// </summary>private void InitializeComponent(){this.pictureBox1 = new System.Windows.Forms.PictureBox();this.panel1 = new System.Windows.Forms.Panel();this.button1 = new System.Windows.Forms.Button();this.button2 = new System.Windows.Forms.Button();this.textBox1 = new System.Windows.Forms.TextBox();this.label1 = new System.Windows.Forms.Label();((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();this.panel1.SuspendLayout();this.SuspendLayout();// // pictureBox1// this.pictureBox1.BackColor = System.Drawing.SystemColors.AppWorkspace;this.pictureBox1.Location = new System.Drawing.Point(3, 3);this.pictureBox1.Name = "pictureBox1";this.pictureBox1.Size = new System.Drawing.Size(660, 800);this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;this.pictureBox1.TabIndex = 0;this.pictureBox1.TabStop = false;// // panel1// this.panel1.AutoScroll = true;this.panel1.Controls.Add(this.pictureBox1);this.panel1.Location = new System.Drawing.Point(8, 74);this.panel1.Name = "panel1";this.panel1.Size = new System.Drawing.Size(681, 339);this.panel1.TabIndex = 1;// // button1// this.button1.Location = new System.Drawing.Point(450, 426);this.button1.Name = "button1";this.button1.Size = new System.Drawing.Size(75, 23);this.button1.TabIndex = 2;this.button1.Text = "退出";this.button1.UseVisualStyleBackColor = true;this.button1.Click += new System.EventHandler(this.button1_Click);// // button2// this.button2.Location = new System.Drawing.Point(466, 30);this.button2.Name = "button2";this.button2.Size = new System.Drawing.Size(75, 23);this.button2.TabIndex = 3;this.button2.Text = "浏览";this.button2.UseVisualStyleBackColor = true;this.button2.Click += new System.EventHandler(this.button2_Click);// // textBox1// this.textBox1.Location = new System.Drawing.Point(142, 32);this.textBox1.Name = "textBox1";this.textBox1.Size = new System.Drawing.Size(318, 21);this.textBox1.TabIndex = 4;// // label1// this.label1.AutoSize = true;this.label1.Location = new System.Drawing.Point(23, 35);this.label1.Name = "label1";this.label1.Size = new System.Drawing.Size(113, 12);this.label1.TabIndex = 5;this.label1.Text = "请选择要查看的图片";// // Form1// this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(691, 461);this.Controls.Add(this.label1);this.Controls.Add(this.textBox1);this.Controls.Add(this.button2);this.Controls.Add(this.button1);this.Controls.Add(this.panel1);this.Name = "Form1";this.Text = "长图片查看器";((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();this.panel1.ResumeLayout(false);this.panel1.PerformLayout();this.ResumeLayout(false);this.PerformLayout();}#endregionprivate System.Windows.Forms.PictureBox pictureBox1;private System.Windows.Forms.Panel panel1;private System.Windows.Forms.Button button1;private System.Windows.Forms.Button button2;private System.Windows.Forms.TextBox textBox1;private System.Windows.Forms.Label label1;}
}
3.2.2.1.2 Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace DisplayLongPicture
{public partial class Form1 : Form{public Form1(){InitializeComponent();this.StartPosition = FormStartPosition.CenterScreen;}private void button2_Click(object sender, EventArgs e){OpenFileDialog dialog = new OpenFileDialog();dialog.InitialDirectory = Application.StartupPath;dialog.RestoreDirectory = true;dialog.Multiselect = false;//该值确定是否可以选择多个文件dialog.Title = "请选择图片文件夹";dialog.Filter = "JPG文件|*.jpg|PNG文件|*.png|所有文件|*.*";if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK){string file = dialog.FileName;this.textBox1.Text = file;pictureBox1.ImageLocation = file;}}private void button1_Click(object sender, EventArgs e){this.Close();Application.Exit();}}
}
3.2.2.1.3 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;namespace DisplayLongPicture
{static class Program{/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new Form1());}}
}
3.2.2.2 C#运行界面查看文本反转图片结果(Matlab和OpenCV)
运行C#查看长图片查看器Windows窗体应用程序,分别打开Matlab结果图片和OpenCV结果图片进行浏览文本翻转效果,感觉很有趣,兴趣是最好的老师,希望学以致用,哈哈哈!
稿子文字左右对称翻转_Matlab/OpenCV (2021-09-06)相关推荐
- 2021/09/06 Terraform 从入门到精通(二)
P38 Route53 demo 在这个演示中,我将向你展示Route 53如何使用Terraform.这里我们有一个文件"route53.tf".有一个资源"aws_r ...
- 实习日志 (2021.09.13)
2021.09.13星期一 今天把之前的算法题终于给弄明白了,并能够按照自己的思路去把他给完成,总结这个题目并不是很难,最重要的是要把链表给弄懂,一开始由于我对链表不是很熟悉,导致我在写该题目的时候花 ...
- 橘子CPS联盟操作手册2021.09
橘子CPS联盟操作手册2021.09 目录 橘子CPS联盟操作手册2021.09 橘子CPS联盟是干嘛的 橘子CPS基本操作流程 PC端操作 1.注册 2.登陆 3.渠道管理 4.分享网站 5.分享网 ...
- 2021.09.27 MySQL笔记
2021.09.27 MySQL笔记 文章目录 2021.09.27 MySQL笔记 一.展示当前存在的所有数据库 二.使用(选中)一个数据库 三.创建一个数据表 四.查询并展示该数据库内的所有数据表 ...
- 2021.09青少年软件编程(Python)等级考试试卷(三级)
2021.09青少年软件编程(Python)等级考试试卷(三级) 一.单选题(共25题,每题2分,共50分) 1.使用map函数可以实现列表数据元素类型的转换,而无需通过循环.则将列表L=['1',' ...
- 2021.09.24—皮皮与帅帅的第二篇情话
2021.09.24我们小情书的第二天 每天晚上,小兔子都会一个人来到溪水边,坐在地上数着星星.而且他个人也非常喜欢一闪一闪的东西.对于小兔子来说,每颗星星都是特别的,于是她就给每颗星星都起了一个可爱 ...
- 《惢客创业日记》2021.09.15(周三)套路的最高境界
今天,凉粉儿给我发了一段她想的关于惢客的宣传词:"你被套路过吗?想不再被套路吗?上惢客."总结一句话是"不被套路上惢客".我觉得能把惢客跟套路联系在一起,很贴切 ...
- 梦想Android版CAD控件2021.12.06更新
梦想Android版CAD控件2021.12.06更新 下载地址:https://www.mxdraw.com/ndetail_30181.html 修改中文扩展数据名,保存不成功问题 增加WORLD ...
- 梦想CAD控件 2021.12.06更新
梦想CAD控件 2021.12.06更新 下载地址:https://www.mxdraw.com/ndetail_30180.html 修改得到cad.dwg预览图片,如果图片是png格式,读取失败的 ...
最新文章
- 【译】Engineering Security Through Coordination Problems
- Linux中高斯分布的参数设置,华为openGauss 配置操作系统参数
- 让Nginx支持pathinfo
- dataTables本地刷新数据解决只能初始化一次问题
- c语言变长参数 第一个参数必须吗,一种使用变长参数为C程序构造灵活回调函数的方法...
- ruby宝石区块链最新消息_Ruby宝石| Ruby工具
- Linux系统编程:fifo有名管道的使用
- 计算机二级数据模型三要素,2017年计算机二级公共基础知识重点讲解:数据模型...
- 依赖注入例子php,依赖注入小例子
- 【C++快速入门】面向对象篇
- 关于switchhosts的一些问题
- 企业信息系统战略规划
- QTTabBar我装好啦!
- 回归分析的五个基本假设
- java文件转换pdf模糊_java 库将 pdf 文件转换成高清图片方法
- 物联网:GPRS和NB-IOT
- Arction图表控件LightningChart振动分析可以检测什么?
- apos mysql,The /usr/local/mysql/data directory is not owned by the apos;mysqlapos; to apos;_mysql...
- 小程序滑动点击切换中间大图两边小图
- ¥符号在安卓上显示只有一个横杠的问题
热门文章
- word格式刷刷完格式后公式上凸了怎么办?
- Daphne以10%的单利投资了100美元。也就是说,每一年的利润都是投资额的10%,即每年10美元:利息 = 0.10 * 原始存款而Cleo以5%的复利投资了100美元。也就是说,
- php画爱心,在WEB里绘制爱心
- autojs教程:贝壳赚app脚本代码
- 生活小知识,生活要认真,驱蚊驱虫驱苍蝇大法
- 腾讯投资“差评”遭舆论讨伐,或被迫退股!!
- 微信上收到的文件怎么打印出来
- java学习方法和经验
- 时尚唯美婚礼视频制作AE标题模板 Wedding Responsive Titles
- 三行代码,可以让你电脑崩溃!