Homework1: A Proposal for My Team Project
题目:证件照照相机
项目背景:
当今社会,用到证件照的场合数不胜数,包括各种考试报名,升学入学,工作投简历,办签证等,都会被要求提供近期证件照,有时还会有尺寸大小要求。前几天,因为研究生网上报名时需要一张电子版证件照,还有大小要求,无奈只好四处奔走找到一家照相馆,他们在拍好之后也只是用PS做了扣图换背景等简单处理,只要电子版竟然要15块大洋,洗出来才20。后来我觉得,只要电子版的话,我们完全可以自己做出来。
市场调研:
有了这个想法之后,我立刻做了一个调查,在apple store里搜索了相关的app,总共有6个,其中一个打开时闪退无法使用,还有一个在2013年已经停止更新,其余四个都是最近一年才发布上线的,体验较好的只有1个,功能考虑的很全面,但在抠图时边界处理的还不够好。想到我们研究院也有做关于人脸识别的研究,我觉得开发一个更加方便好用的app是很可行的。毕竟有需求,需求还未被满足,我们又有很好的资源和技术,综上,这应该是一个很好的项目。
功能描述:
1、拍照后自动识别人脸和背景,智能抠图换背景
2、可以进行适当的手工处理,包括背景的显示与擦除等
3、能够选择照片的尺寸(1寸、2寸等)和大小(比如30k--300k等)
4、可以进行轻微的补光、美白等
5、能保存或上传
6、基本功能如上,其他功能未完待续...
Homework2: the Nth Prime Program
实现:
switch (Nth)
{
case 1:
return 2;
case 2:
return 3;
case 3:
return 5;
case 4:
return 7;
case 5:
return 11;
default:
break;
}//因为求上界的公式要求 Nth>=6
unsigned int n = Nth*log(Nth) + Nth*log(log(Nth));//求第N个质数的上界
//cout << n << endl;
unsigned int count = 0;//统计当前是第几个质数
bool* isPrimes = new bool[n + 1];//true 表示对应的数是质数
bool flag = true;
isPrimes[2] = true;
for (unsigned int i = 3; i < n + 1; i++)
{
isPrimes[i] = flag;
flag = !flag;
}//首先去掉偶数
Prime_list[count++] = 2;//先把2写入result list
for (unsigned int i = 3; i < n + 1; i += 2)//只判断奇数
{
if (isPrimes[i] == true)
{
Prime_list[count++] = i;
if (count == Nth)
{
return i;
}
}
//unsigned int comp = n / i;
for (unsigned int j = 1; j < count&&i*Prime_list[j] <= n; j++)//j从1开始,即Prime_list[j]从3开始,因为偶数已经被筛出
{ //Prime_list[j] <= n/i等价于判断i*Prime_list[j] <= n,但可以防止溢出,Prime_list[j] <= comp
isPrimes[i*Prime_list[j]] = false;
if (i == Prime_list[j])
{
break;
}
}
}
找到第1,000,000个质数花了0.078s,结果正确
Homework3: Count the word frequency and Output the top N hot words
流程如下:
首先输入根目录和N,然后遍历根目录下的所有文件,保存每一个文件的全路径,然后循环遍历所有文件,进行适当处理,将文件中的每个单词作为一个map<string,int>的关键字储存,若map中有这个关键字则其值加1,否则新建该关键字,其值置1,由此实现count功能。最后将这个map按pair写入一个vector中,自定义一个排序规则,将其排序,最后输出值最大的前N个单词及其数量。
代码细节如下:
先得到所有文件的路径,函数原型如下:
void getTextPath(string& Root, vector<string>& textPathArr);
采用递归调用的方法,进行遍历,整个过程耗时约0.015s
if ((hFile = _findfirst(path.assign(Root).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
//如果是目录,就迭代,继续向下寻找
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
{
getTextPath(path.assign(Root).append("\\").append(fileinfo.name), textPathArr);
}
}
else
{//如果不是,就加入textPathArr
textPathArr.push_back(path.assign(Root).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
然后利用得到的路径进行文件操作,得到word map,函数原型如下:
void getWordMap(string& path, map<string, int>& result);
调试中发现,这个遍历过程是整个工作中耗时最多的,因为涉及大量的文件读取操作。所以优化也全在这一部分,为了节约读取文件的时间,我试了几个方法,
v1,最开始的方法,也最朴素,就是每次读入一个单词,然后做一些处理,包括去掉特殊字符和标点符号以及转换成小写等,最后处理完大概需要35s
void getWordMap(string& path, map<string, int>& result)//v1,34.946s
{
ifstream infile;
infile.open(path);
if (!infile)
{
std::cout << "File open failed!" << endl;
}
string str;
while (infile >> str)
{
//cout << str << endl; break;
str.erase(remove_if(str.begin(), str.end(), bind2nd(less_equal<char>(), -1)), str.end());//移除特殊字符
str.erase(remove_if(str.begin(), str.end(), ispunct), str.end());//移除标点符号
if (str.length() == 0)
{
continue;
}
transform(str.begin(), str.end(), str.begin(), tolower);//转换成小写,不区分大小写
++result[str];
}
infile.close();
}
因为减少读取文件的次数可以加快速度,所以我试着将整个文件一次读入到缓存区,之后再从缓存区内读出,
void getWordMap(string& path, map<string, int>& result)//v2,fread()先全部读取成string类型再处理,38.984s
{
std::ifstream t(path);
std::stringstream buf;
buf << t.rdbuf();
//istringstream is(buf.str());
string str;
while (buf >> str)
{
//cout << str << endl; break;
str.erase(remove_if(str.begin(), str.end(), bind2nd(less_equal<char>(), -1)), str.end());//移除特殊字符
str.erase(remove_if(str.begin(), str.end(), ispunct), str.end());//移除标点符号
if (str.length() == 0)
{
continue;
}
transform(str.begin(), str.end(), str.begin(), tolower);//转换成小写,不区分大小写
++result[str];
}
}
但结果并不理想,反而更慢了一点,究其原因应该是对string的操作太费时间,所以我开始转为操作char类型字符数组:
void getWordMap(string& path, map<string, int>& result)//v4,先全部读取成char类型再处理,28.97s->25.736s->25.204s->24.245
{
ifstream infile;
int len;
infile.open(path); // open input file
infile.seekg(0, ios::end); // go to the end
len = infile.tellg(); // report location (this is the length)
infile.seekg(0, ios::beg); // go back to the beginning
char* buf = new char[len]; // allocate memory for a buffer of appropriate dimension
infile.read(buf, len); // read the whole file into the buffer
infile.close(); // close file handle
string str;
for (char*p = buf; p - buf < len; p++)
{
if (*p == ' ' || *p == '\t' ||*p == '\n' || *p == '\r' )
{
//string str(word);
if (str.length() == 0)
{
continue;
}
++result[str];
str.clear();
}
else
{
if (ispunct(*p)||*p<0)//移除标点符号和特殊字符
{
continue;
}
str += tolower(*p);//转换成小写,不区分大小写
}
}
}
这次我将文件这个读入成char型数组,然后从这个数组中一个一个提取出word,同时去掉特殊字符和标点符号,最终确实变快了不少,大约25s
最后就是排序了,因为vector有sort()函数,可以指定比较规则,所以先将map复制到一个vector中:
map<string, int>::iterator iter = result.begin();
while (iter != result.end())
{
result_sorted.push_back(*iter);
++iter;
}
然后写了一个比较规则,使其按降序排列
bool operator() (const PAIR& P1, const PAIR& P2)
{
return P1.second > P2.second;//降序
}
最后就是调用vector的sort()进行排序,
sort(result_sorted.begin(), result_sorted.end(), comp());
这个排序还是很快的,测试发现大约0.1s即可完成。
最后是试验结果:
整个程序大约需要25s左右的时间
实验代码:
https://github.com/v-chixma/Advanced-software-engineering-course-Week2