//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
char colorA(int c){//颜色判断
int r,g,b;
r=c&255; g=(c>>8)&255; b=(c>>16)&255;
int f=r; if(f<g) f=g; if(f<b) f=b;
if (f*3-r-g-b<100){ if(r+g+b<150*3) return 1; return 0; } //黑白判断
if(r>190&&g<30&&b<30) return 2; //红
if(r<30&&g>190&&b<30) return 3; //绿
if(r<30&&g<30&&b>190) return 4; //蓝
return 0;
}
void imgDxDy(char *b,char *r,int dx,int dy){ //文字图像微移
int i,j,k,x2,y2;
for(i=0;i<18;i++)
for(j=0;j<18;j++){
k=i*18+j; x2=j+dx; y2=i+dy;
if(y2<0||y2>=18||x2<0||x2>=18) {r[k]=0; continue;}
r[k]=b[y2*18+x2];
}
}
int imgIs1(char *p,int k){ //图片p中的k位附近是否为黑
int m, x=k%18,y=k/18, x2,y2, dx,dy;
for(m=0;m<9;m++){
dx=m/3; if(dx>1) dx-=3;
dy=m%3; if(dy>1) dy-=3;
x2=x+dx; y2=y+dy;
if(y2<0||y2>=18||x2<0||x2>=18) continue;
if(p[y2*18+x2]==1) return 1;
}
return 0;
}
int imgX(char *b,char *z,int sz=0){ //相似度计算,数字或字母应置sz=1;
int i,m,c,dx,dy;
int v1,v2,v3,v1n,v2n,v3n;
int M=0,C;
char h[324];
for(m=0;m<25;m++){
dx=m/5; if(dx>2) dx-=4;
dy=m%5; if(dy>2) dy-=4;
imgDxDy(z,h,dx,dy);
v1=v2=v3=v1n=v2n=v3n=0;
for(i=0;i<324;i++){
if(sz&&i%18>11) continue;
if(h[i]==1){ v1n++; if(b[i]==1) v1++; } //黑色部分的相似计算
if(h[i]==2){ v2n++; if(b[i]==0) v2++; } //白色部分的相似计算
if(h[i]==3){ if(b[i]==0) v1-=4; } //必须为黑的部分处理
if(h[i]==4){ if(b[i]==1) v2-=4; } //必须为白的部分处理
if(b[i]==1){ v3n++; if(imgIs1(h,i)==1) v3++; } //字形相似计算
}
if(!v1n) v1n=1; if(!v2n) v2n=1; if(!v3n) v3n=1;
C=100.0*v1/v1n+100.0*v2/v2n+100.0*v3/v3n+0.5;
if(C>M) M=C;
if(C>290&&!sz) break;
}
return M;
}
char Z[30][324],Z_isload=0;
AnsiString strZ0="01234567891";
AnsiString strZ1="甲乙丙丁戊己庚辛壬癸正";
void loadZ(){ //截入标准文字库
if(Z_isload) return; Z_isload=1;
TImage *im=new TImage(NULL);
im->Picture->LoadFromFile("moB.bmp");
int w=im->Picture->Width;
w=w/19.0+0.5; if(w>30) w=30;
int i,j,k,c;
for(k=0;k<w;k++)
for(i=0;i<324;i++){
c=im->Canvas->Pixels[i%18+k*19][i/18];
Z[k][i]=colorA(c);
}
delete im;
}
AnsiString imgToA(char *buf,int retOne=0){ //图片转为文字
loadZ();
int i,v,vr,Max=0,k=-1,sz=1;
//判断是否为数字
for(i=0,v=0,vr=0;i<324;i++){
if(!buf[i]) continue;
if(i%18>11) sz=0;
if(i%18>8) vr++;
v++;
}
if(vr>15) sz=0;
if(v<6) return "空";
if(retOne) return "有";
//图像比对
if(sz){
for(i=0;i<11;i++){
v=imgX(buf,Z[i],1);
if(v>Max) Max=v,k=i;
}
}else{
for(i=11;i<22;i++){
v=imgX(buf,Z[i],0);
if(v>Max) Max=v,k=i;
if(v>290) break;
}
}
//转为字串
AnsiString c="";
if(k<=10) c=strZ0.SubString(k+1,1);
else c=strZ1.SubString((k-11)*2+1,2);
if(Max<270||sz&&Max<290) c="?"+c;
return c;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
class lunImg{
public:
int x0,y0; //文字起始坐标
double sx,sy; //图片的歪斜角
double Dx,Dy; //字宽,字高
TImage *im;
lunImg(){ im=new TImage(NULL); }
~lunImg() { delete im;}
void getRedXY(int x1,int y1,int x2,int y2,int *rx,int *ry){ //取红点的位置
int i,j,c;
for(i=x1;i<x2;i++)
for(j=y1;j<y2;j++){
c=im->Canvas->Pixels[i][j];
if( colorA(c)==2 ) {*rx=i,*ry=j;return;}
}
*rx=*ry=0;
}
void load(AnsiString fname){//载入图片
im->Picture->LoadFromFile(fname);
const w0=1068,h0=1593; //右下点的理想坐标
int i,c,w2,h2;
int x1,y1,x2,y2;
getRedXY(0,0,50,50,&x0,&y0); if(!x0){ShowMessage("未找到左上标"); return;}
getRedXY(x0+1066,y0-15,x0+1086,y0+15,&x1,&y1); if(!x1){ShowMessage("未找到右上标"); return;}
getRedXY(x0+1060,y0+1420,x0+1100,y0+1570,&x2,&y2); if(!x2){ShowMessage("未找到右下标"); return;}
sx = 1.0*(x2-x1-3)/(y2-y1); //水平线的斜率
sy = 1.0*(y1-y0)/(x1-x0); //竖直线的斜率
Dx=(x1-x0-3)/53.0; //字宽
c=(y2-y1-58)/28.4+0.5;
Dy=1.0*(y2-y1-58)/int((y2-y1-58)/28.4+0.5); //行高
}
void TZxy(int &x,int &y){//调整x,y
int i,j,d;
for(i=0;i<5;i++){
d=i; if(i>2) d=2-i;
for(j=0;j<18;j++) if( colorA(im->Canvas->Pixels[x+j][y+d])==1 ) break;
if(j==18){ y+=d; break; }
}
for(i=0;i<5;i++){
d=i; if(i>2) d=2-i;
for(j=0;j<18;j++) if( colorA(im->Canvas->Pixels[x+d][y+j])==1 ) break;
if(j==18){ x+=d; break; }
}
}
AnsiString getA(double row,double col,int wn=-1,int retOne=0){ //取图片中的文字,wn为字宽(最大为60)
//取指定的文字
int hz=0; //hz=1读汉字
if(wn==-1) wn=20,hz=1;
//确定x,y
char buf[28*60]={0},b[324];
double w,h;
int i,j,x,y,c;
w = col*Dx + 0.5 -3;
h = row*Dy + 60.5 -3;
x = x0+w + sx*h;
y = y0+h + sy*w;
if(y>im->Picture->Height-28) return "err";
TZxy(x,y);//调整x,y
//到图片
for(i=0;i<28;i++)
for(j=0;j<wn;j++){
c=im->Canvas->Pixels[x+j][y+i];
buf[i*wn+j]=colorA(c);
}
//去除上方及左边的空白区
int bx,by;
for(i=0;i<14*wn;i++) if(buf[i]) break;
by=i/wn;
for(i=0;i<28*wn;i++) if(buf[i%28*wn+i/28]) break;
bx=i/28;
for(i=0;i<28*wn;i++){
int y=i/wn,x=i%wn;
int x2=x+bx,y2=y+by;
if(x2>=wn||y2>=28) buf[i]=0;
else buf[i]=buf[y2*wn+x2];
}
//转换中文
if(hz){
for(i=0;i<324;i++) b[i]=buf[i/18*wn+i%18];
return imgToA(b,retOne);
}
int g[8]={0},gn=1; //g为各字的开始与结束所在列
for(i=1;i<wn;i++){
for(j=0;j<18;j++) if(buf[j*wn+i]) break;
if(gn%2==1&&(j==18||i-g[gn-1]==11)) g[gn++]=i;
if(gn%2==0&&j!=18) g[gn++]=i;
if(gn>=8) break;
}
AnsiString r;
if(gn%2==1) g[gn++]=wn;
for(i=0;i<gn-1;i++){
int offs=g[i],w=g[i+1]-offs,k;
if(i%2==1){ if(w>5&&gn<=6) r+="_"; continue; } //数据间距较大时视为空格
for(j=0;j<324;j++) b[j]=0;
for(j=0;j<18;j++)
for(k=0;k<w;k++) b[j*18+k]=buf[j*wn+k+offs];
r+=imgToA(b);
}
return r;
}
/*void show(char *b){
int i,c;
for(i=0;i<324;i++){
if(b[i]) c=1; else c=0xFFFFFF;
Form1->im2->Canvas->Pixels[i%18][i/18]=c;
}
}*/
}R;
AnsiString bmpToTxt(AnsiString fname){
if(!FileExists(fname+".bmp")){
ShowMessage(fname+".bmp 找不到");
return "";
}
R.load(fname+".bmp");
int i,k,rrn,rn;
AnsiString s,c,c2,c3,c4,c5;
for(k=0;k<19*3;k+=19)
for(i=0,rrn=0,rn=0;i<56;i++){
c2=c3="";
c=R.getA(i,0+k,-1,1); if(c=="err") break;
if(c=="空"){
c2=R.getA(i,12+k);
if(c2=="空") { //新的一年开始或到了页底
if(rrn) break;
else { rrn++,rn=0,i++; }
}
else s+=" "+c2;
continue;
}
if(!rn) s+="\r\n年份"+R.getA(i-1,12.5+k,50);
//节气
c=" "+R.getA(i,12+k);
c2=R.getA(++i,12+k);
if(c2!="空") c+=" "+c2;
s+="\r\n"+c;
rn++;
}
int fp = FileCreate("B"+fname+".txt");
FileWrite(fp,s.c_str(),s.Length());
FileClose(fp);
return s;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int n=StrToInt(Edit2->Text);
if(n<1||n>800) { ShowMessage("个数不要超过800或小于1"); return; }
AnsiString c;
for(int i=0;i<n;i++){
c=StrToInt(Edit1->Text)+1000+i;
c=c.SubString(2,3);
StatusBar1->SimpleText="正在转换"+c+".bmp";
Memo1->Text=bmpToTxt(c);
}
StatusBar1->SimpleText="完成";
}
//---------------------------------------------------------------------------