预防颈椎DIY

前段时间脖子有点疼,久坐对着电脑实在是难免,有时候打游戏头向前伸着对颈椎就更伤了。于是我就为自己写了一个小程序来呵护颈椎。

下面这个图大家还记得吧!我最开始也是感觉这个不错。但是,不得不说我们对中文的适应能力太强了。比如下面这段话:“研表究明,汉顺字序并不定一影阅响读,比如当看你完这句话后,才发这现里的字全是都乱的。”。换成英文?换成英文也有一个问题,应该说是普遍的问题——时间长了一目十行。下面说说我的程序吧!

我最开始的目的只是打算自己制作这种图片,自然想到Opencv可能满足这个要求。另外,颈椎的一个问题主要是久坐不动,所以我就强迫自己一定时间动一下。好了,我的程序的大致思想就是这样:每隔一段时间就全屏显示自己制作的图片,当然图片是程序自动生成的,而且每次生成的图片都不一样。至于显示的内容,选择空间就比较大了。考虑到显示图片的时间不要太久,我最后选择了一些英文名言警句,当成数据库,当然,我们这个小程序用个文本文件存储就可以了。总结一下,可以分成三个模块:计时模块、选取内容模块、生成图片并显示模块。下面分别说。

计时模块。很简单,调用C++中的Sleep(time)函数,time单位是毫秒。这样主控程序也就出来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int _tmain(int argc, _TCHAR* argv[])
{
int interval=0, showtime=0;

while(interval==0)
{
std::cout << "Please specified the interval time (minute(s)): ";
std::cin >> interval;
}

while(showtime==0)
{
std::cout << "Please specified the image showing time (minute(s)): ";
std::cin >> showtime;
}

while(1){
Sleep(interval*1000*60);
ImageShow *show = new ImageShow(showtime);
show->ImageGenerateAndShow();
}

return 0;
}

选取内容模块。所有内容都存放在data/data.txt文件中。每一行一句英文。我们只要每次random一行,并取出来作为下一个模块的处理内容就可以了。有几点需要考虑的是:1.不能把一个单词拆开放到两行;2.要考虑屏幕的大小,可以显示多少的字符,对于过长的内容直接就Pass了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#ifndef __CONTENT_H__
#define __CONTENT_H__

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
#include <vector>
#include <direct.h>
#include <time.h>
#include <vector>

using std::vector;
using std::cout;
using std::string;

class Content{
public:
Content(int charNum, int lineNum);
~Content();
bool ContentGenerate();
int FileLineCount();
vector<string> *contentShow;
private:
//屏幕每行可以容纳的最多字符个数
int MaxCharNumPerLine;
//屏幕最多可以显示多少行
int MaxLineNumShow;
};

#endif

#include "stdafx.h"
#include "Content.h"

Content::Content(int charNum, int lineNum)
{
MaxCharNumPerLine = charNum;
MaxLineNumShow = lineNum;

contentShow = new std::vector<std::string>;
(*contentShow).reserve(MaxLineNumShow);
}

Content::~Content()
{
if(contentShow)
delete contentShow;
}

bool Content::ContentGenerate()
{
srand((int)time(NULL));
int line = rand()%FileLineCount();

_chdir("data/");
std::ifstream out;
out.open("data.txt");

if(!out)
{
cout << "Error: unable to open the data.txt\n";
exit(-1);
}

string showStr;
while(line>0)
{
getline(out, showStr);
line--;
}
out.close();
if(showStr.size() > MaxCharNumPerLine*MaxLineNumShow*4/5)
return false;

int aveLength = showStr.size()/MaxLineNumShow;
int pos=0;
int i=3*MaxCharNumPerLine/5;

while(i<showStr.size())
{
if(isalnum(showStr[i]) || showStr[i]=='\'')
{
i++;
}
else
{
for(int j=i; j<showStr.size()&amp;&amp;isalnum(showStr[j]); j++)
{
if(ispunct(showStr[j]))
i=j;
}
(*contentShow).push_back(showStr.substr(pos, i-pos+1));
while(i<showStr.size() &amp;&amp; !isalnum(showStr[i]))
{
i++;
}
pos=i;
i += 3*MaxCharNumPerLine/5;
}
}
(*contentShow).push_back(showStr.substr(pos, showStr.size()-pos+1));

for(int i=0; i<(*contentShow).size(); i++)
std::cout << (*contentShow)[i] << std::endl;
return true;
}

int Content::FileLineCount()
{
_chdir("data/");
std::ifstream out;
out.open("data.txt");

string line;
int count = 0;
if(!out)
{
std::cout << "Error: unable to open the data.txt" << std::endl;
exit(-1);
}

while(out)
{
getline(out, line);
count ++;
}
out.close();

return count;
}

生成图片并显示模块。这部分主要就是opencv的工作,但是还是很繁琐,只能非常细心的处理,并且逻辑不能乱。大家可以先参考opencv的reference自己想想怎么做。下面几点作为参考:1.如何将字体倒着放?opencv是做不到的,所以我们只有把图片旋转之后再加字。2.如何全屏显示?也就是要获得显示器的宽和高的参数。3.显示时间长短。其它问题大家自己体会吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
void ImageShow::ImageGenerateAndShow()
{
//调节字体等属性
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
std::string text("HelloWorld");

double fontScale = 3;
int thickness = 2 ;

int baseline=0;
cv::Size textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &amp;baseline);

int fontHeight = textSize.height;

//获得屏幕分辨率
int nFullWidth = GetSystemMetrics(SM_CXSCREEN);//横向
int nFullHeight = GetSystemMetrics(SM_CYSCREEN);//纵向

Content *tent = new Content((nFullHeight*10/textSize.width), (nFullWidth*4/5)/(3*textSize.height/2));
while(!tent->ContentGenerate());
//获得需要显示的内容,存在ptext里
std::cout << "contentShow size: " << (*tent->contentShow).size() << std::endl;
std::vector<std::string> ptext = *tent->contentShow;
std::cout << "ptext size: " << ptext.size() << std::endl;
for(int i=0; i<ptext.size(); i++)
std::cout << ptext[i] << std::endl;

cv::Mat img(nFullWidth, nFullHeight, CV_8U, cv::Scalar::all(0));
cv::Mat imgdst(nFullHeight, nFullWidth, CV_8U, cv::Scalar::all(0));

//一行字的左下角坐标
cv::Point textOrg;

int i, pos=3*fontHeight;
for(i=0; i<ptext.size()&amp;&amp;i*fontHeight+nFullHeight/5 + i*fontHeight/3 < img.rows*4/5; i=i+2)
{
textSize = cv::getTextSize(ptext[i], fontFace, fontScale, thickness, &amp;baseline);
textOrg.x = (img.cols-textSize.width)/2;
textOrg.y = pos;
cv::putText(img, ptext[i], textOrg, fontFace, fontScale, cv::Scalar::all(255), thickness, 8);
pos += 3*fontHeight;
}
cv::flip(img, img, 0);
cv::flip(img, img, 1);

if(ptext.size()%2)
{
i=i-3;
pos = nFullWidth - textOrg.y + 5*fontHeight/2;
}
else{
--i;
pos = nFullWidth - textOrg.y - fontHeight/2;
}
std::cout << "i= " << i << std::endl;
for(; i>0; i=i-2)
{
std::cout << ptext[i] << std::endl;
textSize = cv::getTextSize(ptext[i], fontFace, fontScale, thickness, &amp;baseline);
textOrg.x = (img.cols-textSize.width)/2;
textOrg.y = pos;
cv::putText(img, ptext[i], textOrg, fontFace, fontScale, cv::Scalar::all(255), thickness, 8);
pos += 3*fontHeight;
}

//transpose the image 90 degree anticlockwise
cv::flip(img, img, 0);
cv::flip(img, img, 1);
cv::transpose(img, imgdst);
cv::flip(imgdst, imgdst, 0);

cv::imwrite("test5.jpg", imgdst);
initShowWindow();
ImageShowing(imgdst);
destroyShowWindow();
}

void ImageShow::initShowWindow()
{
cvNamedWindow("Projector Window", CV_WINDOW_NORMAL);
cvSetWindowProperty("Projector Window", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
}

void ImageShow::ImageShowing(cv::Mat img)
{
//cv::Mat img = cv::imread("E:\\lena.jpg",0);
IplImage ipl_img = img;
cvShowImage("Projector Window", &amp;ipl_img);
cvWaitKey(showTime);
}

下面就是程序最后的成果。

这么看下来太别扭了,放大旋转之后如下图所示。

至于全部代码,之后我再部署到github上去。