用于补偿喷墨打印头中喷嘴随时间变化发生错位的方法和装置的制作方法

文档序号:2480248阅读:234来源:国知局
专利名称:用于补偿喷墨打印头中喷嘴随时间变化发生错位的方法和装置的制作方法
技术领域
本发明与喷墨打印领域有关,特别是关于补偿在具有重叠段的打印头中喷嘴随时间变化发生错位的方法和装置。同类专利申请与本发明相关的各种方法、系统和装置在申请人或受让人与本发明同时提出的下列同类专利申请中揭示PCT/AU00/00518,PCT/AU00/00519,PCT/AU00/00520,PCT/AU00/00521,PCT/AU00/00522,PCT/AU00/00523,PCT/AU00/00524,PCT/AU00/00525,PCT/AU00/00526,PCT/AU00/00527,PCT/AU00/00528,PCT/AU00/00529,PCT/AU00/00530,PCT/AU00/00531,PCT/AU00/00532,PCT/AU00/00533,PCT/AU00/00534,PCT/AU00/00535,PCT/AU00/00536,PCT/AU00/00537,PCT/AU00/00538,PCT/AU00/00539,PCT/AU00/00540,PCT/AU00/00541,PCT/AU00/00542,PCT/AU00/00543,PCT/AU00/00544,PCT/AU00/00545,PCT/AU00/00547,PCT/AU00/00546,PCT/AU00/00554,PCT/AU00/00556,PCT/AU00/00557,PCT/AU00/00558,PCT/AU00/00559,PCT/AU00/00560,PCT/AU00/00561,PCT/AU00/00562,PCT/AU00/00563,PCT/AU00/00564,PCT/AU00/00565,PCT/AU00/00566,PCT/AU00/00567,PCT/AU00/00568,PCT/AU00/00569,
PCT/AU00/00570,PCT/AU00/00571,PCT/AU00/00572,PCT/AU00/00573,PCT/AU00/00574,PCT/AU00/00575,PCT/AU00/00576,PCT/AU00/00577,PCT/AU00/00578,PCT/AU00/00579,PCT/AU00/00581,PCT/AU00/00580,PCT/AU00/00582,PCT/AU00/00587,PCT/AU00/00588,PCT/AU00/00589,PCT/AU00/00583,PCT/AU00/00593,PCT/AU00/00590,PCT/AU00/00591,PCT/AU00/00592,PCT/AU00/00584,PCT/AU00/00585,PCT/AU00/00586,PCT/AU00/00594,PCT/AU00/00595,PCT/AU00/00596,PCT/AU00/00597,PCT/AU00/00598,PCT/AU00/00516,PCT/AU00/00517,PCT/AU00/00511,PCT/AU00/00501,PCT/AU00/00502,PCT/AU00/00503,PCT/AU00/00504,PCT/AU00/00505,PCT/AU00/00506,PCT/AU00/00507,PCT/AU00/00508,PCT/AU00/00509,PCT/AU00/00510,PCT/AU00/00512,PCT/AU00/00513,PCT/AU00/00514,andPCT/AU00/00515对上述同类专利申请的揭示以交叉索引方式总结于此。
在上述方案中,需要使用较宽的墨水喷嘴阵列实现能够对墨水进行控制的页宽打印头。而且,墨滴的尺寸必须非常小。例如,较好的打印系统(例如平板打印)一般可以支持1600dpi的分辨率。因此,按上述分辨率计算,A4页宽的打印头(8英寸宽)需要为每种颜色提供大约12800个墨水喷嘴。对于标准的四色打印过程,则需要51000个墨水喷嘴。对于6色打印过程(标准的四色+定色剂+IR墨水),则需要76800个墨水喷嘴。不幸的是,很难在基片(例如硅晶片基片)的连续段上制造较大的整体打印头。这主要是因为随着整体打印头结构尺寸的增加,产品的生产率会迅速降低。生产率问题在半导体业中由来已久,而喷墨打印设备的制造通常要利用半导体处理技术或模拟半导体处理技术,特别是微电机系统(MEMS)技术。S Tom Picraux和Paul J McWhorter在1998年12月出版的IEEE Spectrum论文集中一篇称为“集成微系统概览”的文章中对MEMS领域进行了研究。
保持高生产率的一种方法是在一系列段中制造超长的打印头,然后把各个端拼接或重叠在一起。不幸的是,打印头设备要求墨水喷嘴之间的距离非常大,这就意为着,即使在实际运转条件下存在热循环,打印头相邻段之间的间距也必须保持高度精确。例如,为了实现1600dpi的打印分辨率,相邻喷嘴之间的间距应在60微米左右。
打印头的周围环境和运转环境可能会在打印头的重叠区产生热循环,使相邻打印头段的重叠部位分别产生膨胀和收缩,因此可能使输出图像中产生人为因素的影响。打印头组件还可能是使用与打印头段具有不同热特性的材料制成的,因此这些组件的热膨胀可能不同。硅基片可能采用合成橡胶封装的,硅的热膨胀系数是2.6×10- 6/℃,而合成橡胶的热膨胀系数为20×10-6/℃。
由于打印头表现二进制的连续色调图像的分辨率是有限的,而人眼可以察觉图像中相邻点之间0.5%的颜色差异,所以最终图像中可能会产生人为因素的影响。
为实现本发明的上述目的,本发明所述的喷墨打印头包含多个重叠的打印头段,相邻段之间的空间关系可以随时间变化。用于控制重叠段中的喷嘴喷出墨水的方法包括下列步骤(a)确定相邻打印头段之间的一个重叠量;(b)为在重叠段的重叠区域中的喷嘴中产生一个半色调图案;(c)把所述半色调图案调整为所述打印头段的重叠区域中的所述重叠尺度的一个函数,以减少上述打印头段的重叠可能产生的任何人为因素的影响。
优选的是,上述确定重叠尺度的步骤还包括测量打印头段的温度的方法。半色调图案优选的是通过抖动矩阵或抖动容量和可替换的措施产生,并且这种变化方法还可以包括在进行抖动矩阵或抖动容量处理之前向当前连续色调像素输出值增加一个重叠值。除了使用温度尺度,还可以利用每个段上的重叠部分产生一个距离尺度,并使用干涉技术来确定段之间的相对移动量。
为实现本发明的其它目的,本发明所述的喷墨打印头系统还包括多个在空间上相互间隔的打印头段、至少一种用于测量相邻打印头段之间的重叠量的装置、为连续色调图像提供半色调的一种装置,以及用于调整相邻打印头段之间的重叠区域中的半色调的一种装置,以便减少上述相邻段之间的人为因素。
调整半色调处理的方法可包括一个连续色调输入、一个空间重叠输入、以及一个二进制输入。半色调方法利用空间重叠输入来改变连续色调输入,从而产生一个经过变化的连续色调输入,经过变化的连续色调输入在用于抖动矩阵或抖动容量的查询表中使用,以便产生二进制输出值,从而调整打印头段的重叠区域。用于调整半色调或抖动矩阵的方法可以在硬件中实现或通过软件算法实现。
在一个A4页宽的打印头中可能有10个段,并有9个重复交叠的重叠部分。使用某种普通的单片集成电路制造技术,可以把段之间的初始重叠长度控制在10微米以内。假设每个段的喷嘴以纵向的锯齿形式排列,相邻喷嘴之间的间隔为16微米,那么6色墨水打印头的段宽为225微米左右。
在本实施例中,在每个打印头段上都有一个温度传感器,用来提供每个打印头段的当前温度特性的尺度。然后,可以利用当前温度尺度确定相邻打印头段的重叠量。
另外,如果打印头各段的物理特点和性能都相同,并且每个重叠段对环境条件也相同,那么也可以只使用一个温度传感器。
然后,重叠量被用于实现一种控制相邻打印头段之间的半色调的机制。在本发明中,我们假设图像的输出采用某种半色调处理技术。有很多种半色调技术都可以用于本发明。关于半色调技术的更多信息,请参考美国麻省理工学院出版社出版的由Ulichney撰写的“数字半色调技术”。


图1所示,相邻打印头段2、3在各自区域12、13中发生重叠。对于1600dpi分辨率,重叠区域的宽度可以扩展到40微米到1毫米之间,覆盖64个间隔16微米的喷嘴。在段2的区域10中,喷嘴专用于喷射墨水。同样,段3的区域11中的喷嘴也专用于喷射墨水。在重叠区域12、13中,两个打印头2、3之间产生“混合”现象,因此,在区域12中,沿边14,打印头段2的喷嘴专用于打印,同样,沿边15,段3的喷嘴也专用于打印。在两个点14、15的中间区域中,应采用线性插值或其它插值方法来控制喷嘴的打印。因此,如图2所示,当在一张打印纸上打印整页宽的彩色图像时,区域17由打印头段10独立打出,区域18由打印头段11独立打出(见图上的黑色点),而区域19由上述两个打印头段10、11中的喷嘴联合打出。这种打印过程可以利用上述参考文献中描述的任何一种半色调矩阵技术实现。虽然在打印时使用半色调矩阵技术,但是实际利用的打印头段取决于重叠段之间的重叠尺度所提供的混合比例。
图3中显示了其中的一种方法。在图3中,对重叠区域中的喷嘴采用线性插值方法处理。在边14上与重叠部分12对应的区域中,打印头段2的喷嘴的使用率为100%,而在边7的等价区域中,打印头段3的喷嘴的使用率为0。当重叠区域的距离从段2的14行向段3的15行增加时,12部分中的喷嘴的利用率逐渐下降(线性下降),在边9上,利用率降为0;相反,13部分中的喷嘴的利用率逐渐增加,到边15时,利用率达到100%。在本发明的第一个实例中,喷嘴之间的重叠增加,所以在重叠区域中所利用的半色调的极限增加。这样会减少在混合区域中打印的点数。反之,如果打印头段的重叠量减少稍高与可接受值的程度,可以通过降低半色调的极限值增加点的频率。
图4中显示了完整的半色调处理方案,其中,抖动矩阵25向求和装置27输出一个当前抖动值,而求和装置27具有另一个输入28,该输入28是一个重叠信号,它根据相邻段间的重叠量变大或减小。求和装置(或加法器)27的输出值29在比较器30中被与输入的连续色调数据32比较,从而输出半色调数据31。另外一种方案是,在施加抖动处理前,可以从连续色调数据29中减去数据值28,这样也可以得到类似结果,如图5中显示的方案。
如图5所示,通过在加法器46中把抖动矩阵40的输出42与叠加信号44合并,然后在减法器中把连续色调数据48减去加法器46的输出54,可以产生半色调数据输出52。这种方案与图4中的方案是等效的。
通过利用上述的某个方案以及图3和图4,可以产生叠加混合的控制量,从而减少相邻打印头段之间的条纹瑕疵。
把每个叠加信号28乘以一个校准因子然后加上一个校准偏移因子,还可以显著降低对相邻打印头段的精确定位要求。因此,在制造打印头段过程中,可以比较粗略地定位相邻打印头段。然后,可以在特定温度下打印测试图案,以确定相邻打印头段之间的重合量。当为特定温度范围确定了重合量后,可以向可编程ROM存储设备写入一系列相应值,以便提供与打印头段的特定重叠量有关的所有偏移值。
本发明的另一个实例中采用了一个软件方案来减少打印头的重叠段之间的人为因素的影响。附录A中提供了一个抖动矩阵的完整的软件方案(包括为调整打印头段之间的可变重叠量提供一种算法)。该程序是用C语言编写的。业界人士也可以使用其它代码实现该算法。该算法的依据如下算法中采用了一种离散点随机抖动处理方法来重现使用双水平点的连续色调像素值。离散点随机抖动方法可以把较高的空间频率(即图像的详细信息)再现到点分辨率的极限程度,而且可以把较低的空间频率再现到人眼可观察的完全的色深水平。随机抖动矩阵用于消除页间的低频率图案。
可以使用点增益技术建立点叠加的模型。点增益是指点图案的理想强度与打印图案时产生的实际强度的差值。在喷墨打印中,点增益主要是由墨水扩散产生的。扩散本身是墨水和打印媒质的一个特性。颜料墨水会在媒质表面扩散,但是向媒质内部的扩散不强。染料墨水可以沿媒质中的纤维扩展。使用表面涂层可用于减少扩散。
由于点重叠效应对点分布的敏感程度与点增益效应对点分布的敏感程度一样,所以可以把理想点的模型建立为铺满整个页面而不存在重叠。虽然实际的墨滴是圆的,并且与临近点重叠,但是在建立模型时,可以把理想点视为方点。因此,理想点和实际点的形状差异可作为点增益参数。
点增益是一种边界效应,也就是说,点增益效应在已打印点和未打印点之间的边界上显现。点增益与点图案之间的边界连接和点图案的区域成正比。处理点增益的两种技术是离散点抖动技术和聚集点抖动技术。在离散点抖动技术中,点在某个区域中均匀分布,例如,50%强度的点可以表示为一种棋盘图案。在聚集点抖动技术中,点由一个单中心的“颜色”区域和一个“无颜色”的边表示,其中,“颜色”区域与“无颜色”区域的面积的比值等于要打印的点的强度。因此,离散点抖动比聚集点抖动对点增益更敏感。
两个相邻的打印头段具有若干个重叠的喷嘴。总体来说,相邻段中的对应喷嘴之间没有完美的对齐关系。在局部水平上,可能存在多半个喷嘴宽度或少半个喷嘴宽度的间隔差异,在1600dpi下,这种差异为±8微米。在较大范围的水平上,重叠喷嘴的数量可能存在一定的差异。
均匀地混合重叠区域之间和相邻段之间的输出的第一种途径是,从重叠区域的一边向另一边混合连续色调输入。当进行重叠区域的输出时,第二个段接收越来越多的连续色调值,而第一个段接收越来越少的连续色调值,如图3所示。此时,可以使用线性插值或更高阶的插值算法。这样,用于抖动通过上述两个段的输出的抖动矩阵在喷嘴级别上就完全对齐了。
上述途径有两个缺点,第一,如果特定点位置的抖动极限低于两个段的插值连续色调值,那么两个段都会为该位置产生一个点。由于这两个点会重叠,所以两个抖动矩阵所保证的强度只能部分重现,从而导致总体强度的损失。对于这种情况,可以限制相应的喷嘴,不允许它们同时产生一个点,这样可以纠正该问题。也可以为可选择段应用抖动矩阵的逆矩阵,或者通过一个抖动矩阵抖动连续色调值,然后根据当前插值因子给出的概率把输出值随机地用于其中某一个喷嘴。
第二个缺点是,由不同段打印的相邻点会再次重叠,导致总体强度的损失。
如图6所示,每个重叠段的值沿水平轴要求60,62要求绘出,分别表示为VA和VB,其值的范围在0.0到1.0之间。计算后的输出要求66要求在垂直轴要求64要求上绘出,为函数IA+B,值的范围在0.0到1.0之间。一个等值面显示了IA+B=0.5的结果值。
图6定性显示了把两个段的输入的连续色调值VA和VB与输出强度IA+B链接起来的一个三维函数图形。对于上述第一种途径,输入连续色调值V和插值因子f共同产生VA=(1-f)V和VB=fV。插值因子越接近0.5,输入连续色调值和所观察到的输出强制之间的差异就越大。对于V=1.0,其结果由图6中的垂直VA+VB=1.0平面的曲线200示出。该曲线在定义上位于功能平面上。图6中还显示了,当任何形式的混合出现时(即0.0<f<1.0),输出强度将被削弱。为了实现所需的输出强度,两个段的输入值的和必须大于所需的输出值,即VA+VB>V。这是附录A中的算法的依据。
当只有一个段提供输出时(即f=0.0或f=1.0),上述函数给出了一个线性结果。当然,这需要假定抖动矩阵包含点增益效应。
上述描述只限于阐述本发明的特定实例,不应限制本发明的适用范围。本领域的技术人员可以很容易地根据本发明进行变化和更改,同时实现本发明的部分或全部优点。例如,本发明可以在某种编程的数字数据处理系统中以某种习知的硬件或软件方式实现。但是,任何根据本发明进行的等价修改或修饰都在本发明的概念范围。
<pre listing-type="program-listing">staticvoidObtainMisregistrationTransferFunction(  int dotsPerPixel,  int subdotsPerDot,  BI_Image const&amp;amp; dotImage,  char const*pDotImageName,  char const*pRefDotImageName,  int const overlapSize,  int const overlapIndex, ∥0..overlapSize-1  int const misregFactor,  BI_Image const&amp;amp; ditherMatrix,  BI_LUT&amp;amp;lutv,  BI_LUT&amp;amp;lut0,  BI_LUT&amp;amp;lut1);class RLE_DotLine{public  RLE_DotLine()  m_whiteRun(0),m_blackRun(0){}  RLE_DotLine(int whiteRun,int blackRun)  m_whiteRun(whiteRun),m_blackRun(blackRun){}  int WhiteRun()const{return m_whiteRun;}  int BlackRun()const{return m_blackRun;}&lt;dp n="d11"/&gt;private  int m_whiteRun;  int m_blackRun;};typedef vector<RLE_DotLine,allocator<RLE_DotLine>>RLE_Dot;staticvoid Usage(){  fprintf(stderr,″usageSegmentDither\n″);  fprintf(stderr,″ inputImage\n″);  fprintf(stderr,″ dotsPerPixel\n″);  fprintf(stderr,″ subdotsPerDot\n″);  fprintf(stderr,″ dotImage\n″);  fprintf(stderr,″ refDotImage\n″);  fprintf(stderr,″ overlapCenter\n″);  fprintf(stderr,″ overlapSize\n″);  fprintf(stderr,″ misregFactor\n″);  fprintf(stderr,″ ditherMatrix\n″);  fprintf(stderr,″ outputImage\n″);  fprintf(stderr,″ outputResolution\n″);  exit(1);}staticvoidBadArgument(char const*pErrorMsg)&lt;dp n="d12"/&gt;{  fprintf(stderr,″SegmentDitherargument error%s\n″,pErrorMsg);  exit(1);}#define CHECK_ARGUMENT(cond)if(cond)BadArgument(#cond)staticdoubleMisregDots(int const misregFactor){  return(double)misregFactor/1000;}staticintMisregSubdots(int const misregFactor,int const subdotsPerDot){  return(int)BU_Round(MisregDots(misregFactor)*subdotsPerDot);}staticvoidPutDot(  int const subdotsPerDot,  RLE_Dot const&amp;amp;rleDot,  int const dotRow,  int const dotCol,&lt;dp n="d13"/&gt;  int const misregFactor,  BI_Image&amp;amp;outputImage){  int const misregSubdots=MisregSubdots(misregFactor,subdotsPerDot);  int const subdotRow=dotRow*subdotsPerDot;  int const subdotCol=dotCol*subdotsPerDot;  int const dotOverlap=rleDot.size()-subdotsPerDot;  int const dotMargin=dotOverlap/2;  RLE_Dot∷const_iterator ii=rleDot.begin();  for(int i=0;i<rleDot.size();i++,ii++)  {  int const row=subdotRow-dotMargin+i;  if(row<0‖row>=outputImage.Height())   continue;  int const whiteRun=(*ii).WhiteRun();  int blackRun=(*ii).BlackRun();  int col=subdotCol-dotMargin+whiteRun+misregSubdots;  if(col<0)  {   blackRun+=col;   col=0;  }  if(col+blackRun&gt;=outputImage.Width())&lt;dp n="d14"/&gt;   blackRun=outputImage.Width()-col;  if(blackRun<=0)   continue;  BU_ExpandBitRun  (   outputImage.Image(row),   col,   outputImage.Width(),   blackRun,   1  );  }}staticvoidMergeScale(  double const scale,  int&amp;amp;v,  double&amp;amp;f0,  double&amp;amp;f1){  double const vScaled=(double)v*scale;  if(vScaled<=255.0)  {  v=(int)BU_Round(vScaled);&lt;dp n="d15"/&gt;  }  else  {  v=255;  double const fScale=vScaled/255.0;  f0*=fScale;  f1*=fScale;  }}staticvoidDither(  BI_Image const&amp;amp;inputImage,  BI_LUT const&amp;amp;lutDotGain,  int const dotsPerPixel,  int const subdotsPerDot,  BI_Image const&amp;amp;dotImage,  char const*pDotImageName,  char const*pRefDotImageName,  int const overlapCenter,  int const overlapSize,  int const misregFactor,  BI_Image const&amp;amp;ditherMatrix,  BI_Image&amp;amp;outputImage,  int const outputResolution,  bool const bRetain,  bool const bSkipLHS,&lt;dp n="d16"/&gt;  bool const bSkipRHS,  bool const bFixedInterp=false,  double const fixedF0=0,  double const fixedF1=0){  ∥compute overlap interval  int const overlapStart=overlapCenter-(overlapSize/2);  int const overlapEnd=overlapStart+overlapSize-1;  ∥copy and invert dither matrix  BI_Image ditherMatrix2;  ditherMatrix2=ditherMatrix;  BI_Invert(ditherMatrix2);  ∥initialise and clear output image  int const subdotsPerPixel=dotsPerPixel*subdotsPerDot;  int const bilevelHeight=inputImage.Height()*subdotsPerPixel;  int const bilevelWidth=inputImage.Width()*subdotsPerPixel;  if(!bRetain)  {  ∥initialise  outputImage.Initialise  (  BI_ColorModel(BI_ColorGrayscale,1),  bilevelHeight,  bilevelWidth,  outputResolution,  outputResolution&lt;dp n="d17"/&gt;  );  ∥clear  BI_CC*pOutputRow=outputImage.Image();  for(intj=0;j<outputImage.Height();j++)  {  BU_ClearLine(pOutputRow,outputImage.Width());  pOutputRow+=outputImage.RowSize();  }}∥convert dot image to RLERLE_DotrleDot;for(int i=0;i<dotImage.Height();i++){  int const whiteRun=BU_GetBitRun  (  dotImage.Image(i),  0,  dotImage.Width(),  0∥white  );  int blackRun;  if(whiteRun=dotImage.Width())  {  blackRun=0;  }  else  {&lt;dp n="d18"/&gt;  blackRun=BU_GetBitRun  (  dotImage.Image(i),  whiteRun,  dotImage.Width(),  1∥black  );  }  rleDot.push_back(RLE_DotLine(whiteRun,blackRun));  }  ∥dither contone input image to bi-level output image  BI_CC const*pImage=inputImage.Image();  BI_CC const*pRow=pImage;  BI_CC const*pDither=ditherMatrix.Image();  BI_CC const*pDitherRow=pDither;  BI_CC const*pDither2=ditherMatrix2.Image();  BI_CC const*pDitherRow2=pDither2;  int ditherRow=0;  for(int row=0;row<inputImage.Height();row++)  {  for(int dotRow=0;dotRow<dotsPerPixel;dotRow++)  {  int const globalDotRow=(row*dotsPerPixel)+dotRow;  BI_CC const*pPixel=pRow;  BI_CC const*pDitherPixel=pDitherRow;  BI_CC const*pDitherPixel2=pDitherRow2;  int ditherCol=0;&lt;dp n="d19"/&gt;   for(int col=0;col<inputImage.Width();col++)   {   int const vRaw=*pPixel++;   int const vDotGain=lutDotGain[vRaw];   for(int dotCol=0;dotCol<dotsPerPixel;dotCol++)   {   int vRawDot=vRaw;   int const t0=*pDitherPixel;   int const tl=t0;∥*pDitherPixel2;   int const globalDotCol=(col*dotsPerPixel)+dotCol;   ∥interpolate intensities in overlap regionand dither   ∥one or the other or both   if(!bFixedInterp &amp;amp;&amp;amp; globalDotCol<overlapStart)   {  int const t=t0;  if((vDotGain=255)‖(vDotGain>=t&amp;amp;&amp;amp;vDotGain!=0))  {   if(!bSkipLHS)   {  PutDot  (   subdotsPerDot,&lt;dp n="d20"/&gt;   rleDot,    globalDotRow,   globalDotCol,   0,   outputImage   );   }   }   }   else   if(!bFixedInterp&amp;amp;&amp;amp;overlapEnd<globalDotCol)   {  int const t=(overlapSize=0)?t0t1;  if((vDotGain=255)‖(vDotGain>=t&amp;amp;&amp;amp;vDotGain!=0))  {   if(!bSkipRHS)   {   PutDot   (   subdotsPerDot,   rleDot,   globalDotRow,   globalDotCol,   misregFactor,   outputImage   );&lt;dp n="d21"/&gt;   }   }   }   else   {#if1   ∥account for stretch or shrink   if(!bFixedInterp)   {  double const misregDots=MisregDots(misregFactor);  double const newOverlapSize=overlapSize+misregDots;  double const overlapScale=newOverlapSize/overlapSize;  vRawDot=(int)BU_Round(vRawDot*overlapScale);  if(vRawDot>255)   vRawDot=255;  ∥MergeScale(overlapScale,vRawDot,f0,f1);   }#endif#if1   ∥compute interpolation factors   double f0,f1;   if(bFixedInterp)&lt;dp n="d22"/&gt;  {  f0=fixedF0;  f1=fixedF1;  }  else  {  ∥compute overlap index  int const overlapIndex=   globalDotCol-overlapStart;  ∥obtain misregistrationLUTs  BI_LUT lutv;  BI_LUT lut0;  BI_LUT lut1;  ObtainMisregistrationTransferFunction  (  dotsPerPixel,  subdotsPerDot,  dotImage,  pDotImageName,  pRefDotImageName,  overlapSize,  overlapIndex,  misregFactor,  ditherMatrix,  lutv,&lt;dp n="d23"/&gt;  lut0,  lut1  );  ∥retrieve interpolationfactors  f0=(double)lut0[vRawDot]/255;  f1=(double)lut1[vRawDot]/255;  if(globalDotCol>overlapCenter)   BU_Swap(f0,f1);  ∥adjust intensityf forattenuation  vRawDot=lutv[vRawDot];  }#endif   ∥diagnostics   ∥printf(″f0=%5.1lf1=%5.1lf(%5.11f)vRaw=%dv=%d\n″,   ∥ f0,f1,f0+f1,vRaw,vRawDot);  ∥interpolate dither with jitter    int vd=0;   int v0d=0;&lt;dp n="d24"/&gt;   int vld=0;   if((vRawDot=255)‖(vRawDot>=t0&amp;amp;&amp;amp;vRawDot!=0))   {   vd=1;   }   double const rr=(double)rand()/RAND_MAX;   if(vd&amp;amp;&amp;amp;rr<f0)   {   v0d=1;   if(!bSkipLHS)   {   PutDot   (   subdotsPerDot,   rleDot,   globalDotRow,   globalDotCol,   0,   outputImage   );   }   }   if(vd&amp;amp;&amp;amp;(1.0-rr)<=f1)   {   v1d=1;&lt;dp n="d25"/&gt;  if(!bSkipRHS)  {  PutDot  (  subdotsPerDot,  rleDot,  globalDotRow,  globalDotCol,  misregFactor,  outputImage  );  }  }#if0  if(globalDotRow=864)  {  printf(″%1d%1d%1d(%3d%3d%3d%3d)″,  vd,v0d,v1d,vRawDot,v0,v1,v0+v1);  if(v0d+v1d<vd)     printf(″?″);   if(v0d+v1d>vd)   printf(″#″);   printf\n″);   }#endif  }&lt;dp n="d26"/&gt;  pDitherPixel++;  pDitherPixel2++;  ditherCol++;  if(ditherCol>=ditherMatrix.Width())  {   pDitherPixel=pDitherRow;   pDitherPixel2=pDitherRow2;   ditherCol=0;  }  }  }  pDitherRow+=ditherMatrix.RowSize();  pDitherRow2+=ditherMatrix2.RowSize();  ditherRow++;  if(ditherRow>=ditherMatrix.Height())  {  pDitherRow=pDither;  pDitherRow2=pDither2;  ditherRow=0;  }  }  pRow+=inputImage.RowSize();  }}staticvoidChangeFileSuffx(&lt;dp n="d27"/&gt;  char const*pPath,  char const*pSuffix,  char const*pExt,  char path[_MAX_PATH]){  char drive[_MAX_DRIVE];  char dir[_MAX_DIR];  char fname[_MAX_FNAME];  char ext[_MAX_EXT];  _splitpath(pPath,drive,dir,fname,ext);  strcat(fname,pSuffix);  _makepath(path,drive,dir,fname,pExt);}staticvoidLogTransferFunction(char const*pType,double const intensity[],intconst v){  printf(″%s%03d%5.1lf(%5.11f)\n″,  pType,v,intensity[v],v-intensity[v]);}staticvoidComputeMisregistrationTransferFunction(  int dotsPerPixel,&lt;dp n="d28"/&gt;  int subdotsPerDot,  BI_Image const&amp;amp;dotImage,  char const*pDotImageName,  double const f0,  double const f1,  int const misregFactor,  BI_Image const&amp;amp;ditherMatrix,  BI_LUT&amp;amp;lutv,  BI_LUT&amp;amp;lut0,  BI_LUT&amp;amp;lut1){  ∥create test image  BI_Image testImage;  testImage.Initialise  (  BI_ColorModel(BI_ColorGrayscale),  ditherMatrix.Height(),  ditherMatrix.Width()  );  ∥build identity transfer function  BI_LUT identityLut;  for(int v=0;v<256;v++)  identityLut[v]=v;  ∥create output image  BI_Image outputImage;&lt;dp n="d29"/&gt;∥compute intensity for each gray leveldouble intensigy[512];int vLast;for(v=0;v<512;v++){  ∥compute extended interpolation factors  double f0x,f1x;  int vx;  if(v<=255)  {  vx=v;  f0x=f0;  f1x=f1;  }  else  {  vx=255;  double const fScale=(double)v/255.0;  f0x=f0*fScale;  f1x=f1*fScale;  }  ∥set test image to next intensity  testImage=BI_Color((BI_CC)vx);  ∥dither test image to bi-level output  Dither  (  testImage,&lt;dp n="d30"/&gt;  identityLut,    dotsPerPixel,   subdotsPerDot,   dotImage,   pDotImageName,    pDotImageName,     0,0, ∥no explicit overlap   misregFactor,   ditherMatrix,  outputImage,    72, ∥output resolution   false, ∥don′t retain output image   false, ∥don′t skip LHS   false, ∥don′t skip RHS   true, ∥fixed interpolation   f0x,   f1x  );  ∥determine intensityof dithered bi-level output  long nDots=0;  BI_CC const*pRow=outputImage.Image();  for(int row=0;row<outputImage.Height();row++)  {  nDots+=BU_CountBits(pRow,0,outputImage.Width());  pRow+=outputImage.RowSize();   }   intensity[v]=255*(double)nDots/&lt;dp n="d31"/&gt;outputImage.PixelCount(); ∥LogTransferFunction(″misreg″,intensity,v);   vLast=v;  if(intensity[v]>=255)   break;  }  LogTransferFunction(″misreg″,intensity,1);  LogTransferFunction(″misreg″,intensity,vLast);  ∥create LUTs  for(int x=0;x<256;x++)  {   double d=-1;   for(v=0;v<=vLast;v++)   {   double const d2=BU_Abs(intensity[v]-x);   if(d<0‖d2<d)   {  d=d2;  if(v<=255)  {  lutv[x]=v;  int const k0=(int)BU_Round(f0*255);  lut0[x]=(BI_CC)BU_Min(k0,255);  int const k1=(int)BU_Round(f1*255);  lut1[x]=(BI_CC)BU_Min(k1,255);   }   else&lt;dp n="d32"/&gt;  {  lutv[x]=255;  int const k0=(int)BU_Round(f0*v);  lut0[x]=(BI_CC)BU_Min(k0,255);  int const k1=(int)BU_Round(f1*v);  lut1[x]=(BI_CC)Bu_Min(k1,255);  if(k0>255‖k1>255)  {   fprintf(stderr,″k0=%d k1=%d(x=%d v=%d fO=%5.11f fl=%5.1lf\n″,   k0,k1,x,v,f0,f1);  }  }  }  }  }}staticvoidSimplifyFraction(int&amp;amp;n,int&amp;amp;d){  for(int i=n;i>1&amp;amp;&amp;amp;n>1;-i)  {  if((d%i)=0)  {  if((n%i)=0)  {   n/=i;&lt;dp n="d33"/&gt;  d/=i;  }  }  }}staticvoidObtainMisregistrationTransferFunction(  int dotsPerPixel,  int subdotsPerDot,  BI_Image const&amp;amp;dotImage,  char const*pDotImageName,  char const*pRefDotImageName,  int const overlapSize,  int const rawOverlapIndex,∥0..overlapSize-1  int const misregFactor,  BI_Image const&amp;amp;ditherMatrix,  BI_LUT&amp;amp;lutv,  BI_LUT&amp;amp;lut0,  BI_LUT&amp;amp;lut1){  ∥nonmalize overlap index  int overlapIndex=rawOverlapIndex;  if(overlapIndex>=((overlapSize+1)/2))  overlapIndex=(overlapSize-1)-overlapIndex;&lt;dp n="d34"/&gt;char lutvName[_MAX_PATH];char lut0Name[_MAX_PATH];char lut1Name[_MAX_PATH];char suffix[_MAX_FNAME];int interpNum=overlapIndex+1;int interpDenom=overlapSize+1;SimplifyFraction(interpNum,interpDenom);sprintf(suffix,″_%03d_%02d_%02d″,  BU_Abs(misregFactor),interpNum,interpDenom);ChangeFileSuffix(pRefDotImageName,suffix,″.amp″,lutvName);sprintf(suffix,″_%03d_%02d_%02d_0″,  BU_Abs(misregFactor),interpNum,interpDenom);ChangeFileSuffix(pRefDotImageName,suffix,″.amp″,lut0Name);sprintf(suffix,″_%03d_%02d_%02d_1″,  BU_Abs(misregFactor),interpNum,interpDenom);ChangeFileSuffix(pRefDotImageName,suffix,″.amp″,lut1Name);try{  BU_File lutvFile(lutvName,_O_BINARY|_O_RDONLY);  lutv.Load(lutvFile);  BU_File lut0File(lut0Name,_O_BINARY|_O_RDONLY);  lut0.Load(lut0File);  BU_File lut1File(lutlName,_O_BINARY|_O_RDONLY);  lut1.Load(lutlFile);&lt;dp n="d35"/&gt;}catch(...){  ∥if using a reference dot image,LUTs must already exist  if(strcmp(pDotmageName,pRefDotImgeName)!=0)  {  fprintf(stderr,″can′tload%s or%s or%s\n″,  lutvName,lut0Name,lut1Name);  exit(1);  }  ∥determine interpolation factors  double f1=(double)interpNum/interpDenom;  double f0=1.0-fl;  ComputeMisregistrationTransferFunction  (  dotsPerPixel,  subdotsPerDot,  dotImage,  pDotImageName,  f0,  f1,  BU_Abs(misregFactor),  ditherMatrix,  lutv,  lut0,  lut1  );&lt;dp n="d36"/&gt;  BU_File lutvFile(lutvName,_O_BINARY|_O_WRONLY|_O_CREAT); lutv.Save(lutvFile);   BU_File lut0File(lut0Name,_O_BINARY|_O_WRONLY|_O_CREAT);  lut0.Save(lut0File);  BU_File lut1 File(lutlName,_O_BINARY|_O_WRONLY|_O_CREAT);  lut1.Save(lut1File);  }}staticvoidComputeDotGainTransferFunction(  int dotsPerPixel,  int subdotsPerDot,  BI_Image const&amp;amp;dotImage,  char const*pDotImageName,  BI_Image const&amp;amp;ditherMatrix,  BI_LUT&amp;amp;lutDotGain){  ∥create test image  BI_Image testImage;  testImage.Initialise  (  BI_ColorModel(BI_ColorGrayscale),&lt;dp n="d37"/&gt;  ditherMatrix.Height(),  ditherMatrix.Width());∥build identity transfer funnctionBI_LUT identityTransferFunction;for(int v=0;v<256;v++)  identityTransferFunction[v]=v;∥create output imageBI_Image outputImage;∥compute intensity for each gray leveldouble intensigy[256];for(v=0;v<256;v++){  ∥set test image to next intensity  testImage=BI_Color((BI_CC)v);  ∥dither test image to bi-level output  Dither  (  testImage,  identityTransferFunction,  dotsPerPixel,  subdotsPerDot,  dotImage,  pDotImageName,  pDotImageName,&lt;dp n="d38"/&gt;   0,0, ∥no overlap   0, ∥nomisregistration   ditherMatrix,   outputImage,   72, ∥outputresolution   false,∥don′t retain outputimage   false,∥don′t skip LHS   false ∥don′t skip RHS  );  ∥determine intensity of dithered bi-level output  longnDots=0;  BI_CC const*pRow=outputImage.Image();  for(int row=0;row<outputImage.Height();row++)  {  nDots+=BU_CountBits(pRow,0,outputImage.Width());  pRow+=outputImage.RowSize();  }  intensity[v]=255*(double)nDots/outputImage.PixelCount(); ∥LogTransferFunction(″dot gain″,intensity,v);     }   LogTransferFunction(″dot gain″,intensity,1);   LogTransferFunction(″dot gain″,intensity,255);&lt;dp n="d39"/&gt;  ∥create LUT  for(int x=0;x<256;x++)  {  double d=-1;  for(v=0;v<256;v++)  {  double const d2=BU_Abs(intensity[v]-x);  if(d<0‖d2<d)  {   d=d2;   lutDotGain[x]=v;  }  }  }}staticvoidObtainDotGainTransferFunction(  int dotsPerPixel,  int subdotsPerDot,  BI_Image const&amp;amp;dotImage,  char const*pDotImageName,  char const*pRefDotImageName,  BI_Image const&amp;amp;ditherMatrix,  BI_LUT&amp;amp;lutDotGain){&lt;dp n="d40"/&gt;  char lutName[_MAX_PATH];  ChangeFileSuffix(pRefDotImageName,″″,″.amp″,lutName);  try   {  BU_File lutFile(lutName,_O_BINARY|_O_RDONLY);  lutDotGain.Load(lutFile);  }  catch(...)  {  ∥if using a reference dot image,LUT must already exist  if(strcmp(pDotImageName,pRefDotImageName)!=0)  {  fprint(stderr,″can′t load%s\n″,lutName);  exit(1);   }   ComputeDotGainTransferFunction   (  dotsPerPixel,  subdotsPerDot,  dotImage,  pDotImageName,  ditherMatrix,  lutDotGain   );   BU_File lutFile(lutName,_O_BINARY|_O_WRONLY|_O_CREAT);   lutDotGain.Save(lutFile);  }}&lt;dp n="d41"/&gt;staticvoidSegmentDither(int argc,char*argv[]){  ∥parse arguments  if(argc!=12)   Usage();  char const*pInputImageName=argv[1];  int const dotsPerPixel=atoi(argv[2]);  int const subdotsPerDot=atoi(argv[3]);  char const*pDotImageName=argv[4];  char const*pRefDotImageName=argv[5];  int const overlapCenter=atoi(argv[6]);  int const overlapSize=atoi(argv[7]);  int const misregFactor=atoi(argv[8]);  int const misregSubdots=MisregSubdots(misregFactor,subdotsPerDot);  char const*pDitherMatrixName=argv[9];  char const*pOutputImageName=argv[10];  int const outputResolution=atoi(argv[11]);  ∥open input image  BI_Image inputImage;  BI_LoadImage(inputImage,pInputImageName);  CHECK_ARGUMENT(inputImage.ColorModel()!=BI_ColorModel(BI_ColorGrayscale));  BI_Invert(inputImage);∥max is black  BI_TIFFSetMinIsBlack(false);∥max is black&lt;dp n="d42"/&gt;  ∥check arguments  CHECK_ARGUMENT(dotsPerPixel<1);  CHECK_ARGUMENT(dotsPerPixel>16);  CHECK_ARGUMENT(subdotsPerDot<1);  CHECK_ARGUMENT(subdotsPerDot>32);  CHECK_ARGUMENT(overlapCenter<1);  CHECK_ARGUMENT(overlapCenter>=inputImage.Width()*dotsPerPixel);  CHECK_ARGUMENT(overlapSize<0);  CHECK_ARGUMENT(misregSubdots<-subdotsPerDot/2);  CHECK_ARGUMENT(misregSubdots>subdotsPerDot/2);  CHECK_RGUMENT(outputResolution<=0);  ∥diagnostics  printf(″misregSubdots=%d\n″,misregSubdots);  ∥open dot image  BI_Image dotImage;  BI_LoadImage(dotImage,pDotImageName);  CHECK_ARGUMENT(dotImage.ColorModel()!=BI_ColorModel(BI_ColorGrayscale,1));  CHECK_ARGUMENT(dotImage.Height()<subdotsPerDot);  CHECK_ARGUMENT(dotImage.Width()<subdotsPerDot);  CHECK_ARGUMENT(dotImage.Height()!=dotImage.Width());  int const dotOverlap=dotImage.Width()-subdotsPerDot;  CHECK_ARUMENT((dotOverlap%2)!=0); ∥open dither matrix&lt;dp n="d43"/&gt;  BI_Image ditherMatrix;  BI_LoadImage(ditherMatrix,pDitherMatrixName);  CHEC_ARGUMENT(ditherMatrix.ColorModel()!=BI_ColorModel(BI_ColorGrayscale,8));  CHECK_ARGUMENT(ditherMatrix.Height()<16);    CHECK_ARGUMENT(ditherMatrix.Width()<16);  ∥create output image  BI_Image outputImage;  ∥obtain dot gain transfer function for particular dot shape  BI_IUT lutDotGain;  ObtainDotGainTransferFunction  (   dotsPerPixel,   subdotsPerDot,   dotImage,   pDotImageName,   pRefDotImageName,   ditherMatrix,   lutDotGain  );  ∥dither input to bi-level output  Dither  (   inputImage,   lutDotGain,   dotsPerPixel,&lt;dp n="d44"/&gt;  subdotsPerDot,  dotImage,  pDotImageName,  pRefDotImageName,  overlapCenter,  overlapSize,  misregFactor,  ditherMatrix,  outputImage,  outputResolution,  false,∥don′t retain output image  false,∥don′t skip LHS  false ∥don′t skip RHS);BI_SaveImge(outputImage,pOutputImageName);∥dither input to bi-level output(LHS only)BI_Image outputImageLHS;Dither(  inputImage,  lutDotGain,  dotsPerPixel,  subdotsPerDot,  dotImage,  pDotImageName,  pRefDotImageName,  overlapCenter,  overlapSize,&lt;dp n="d45"/&gt;  misregFactor,  ditherMatrix,  outputImageLHS,  outputResolution,  false, ∥don′t retain output image  false, ∥don′t skip LHS  true ∥skip RHS);BI_SaveImage(outputImageLHS,"OutLHS.tif");∥dither input to bi-level output(RHS only)BI_Image outputImageRHS;Dither(  inputImage,  lutDotGain,  dotsPerPixel,  subdotsPerDot,  dotImage,  pDotImageName,  pRefDotImageName,  overlapCenter,  overlapSize,  misregFactor,  ditherMatrix,  outputImageRHS,  outputResolution,  false,∥don′t retain output image  true, ∥skip LHS&lt;dp n="d46"/&gt;  false ∥don′t skip RHS);BI_SaveImage(outputImageRHS,″OutRHS.tif″);∥dither input to bi-level output(no interp)BI_Image outputImageNoInterp;Dither(  inputImage,  lutDotGain,  dotsPerPixel,  subdotsPerDot,  dotImage,  pDotImageName,  pRefDotImageName,  overlapCenter,  overlapSize,  misregFactor,  ditherMatrix,  outputImageNoInterp,  outputResolution,  false,∥don′t retain output image  false,∥skip LHS  false,∥don′t skip RHS  true, ∥fixed interp  0, ∥f0  0∥f1);BI_SaveImage(outputImageNoInterp,"OutNoInterp.tif");&lt;dp n="d47"/&gt;}voidmain(int argc,char*argv[]){  try  {  SegmentDither(argc,argv);  }  catch(BU_Error error)  {  error.Print();  }  exit(0);}</pre>
权利要求
1.一种喷墨打印头,包括多个重叠的打印头段,其中相邻段的空间关系可以随时间变化;用于控制喷墨打印头的重叠段中的喷嘴的方法包括下述步骤(a)确定相邻打印头段之间的一个重叠尺度;(b)为在重叠段的重叠区域中的喷嘴中产生一个半色调图案;(c)把所述半色调图案调整为所述打印头段的重叠区域中的所述重叠尺度的一个函数,以减少上述打印头段的重叠可能产生的任何人为因素的影响。
2.如权利要求1所述的控制喷嘴动作的方法,其中所述确定相邻打印头段之间的重叠尺度的步骤包括测量打印头段的温度。
3.如权利要求1所述的控制喷嘴动作的方法,其中所述确定相邻打印头段之间的重叠尺度的步骤包括测量所述重叠段的相对位移。
4.如权利要求2所述的控制喷嘴动作的方法,其中所述在重叠段的重叠区域中的喷嘴中产生一个半色调图案的步骤包括利用一个带插值功能的抖动矩阵。
5.如权利要求4所述的控制喷嘴动作的方法,其中所述调整上述半色调图案的步骤是指使VA+VB>V,其中VA和VB分别是两个相邻段的抖动矩阵值,而V是要表现的连续色调值。
6.如权利要求5所述的控制喷嘴动作的方法,其中所述方法还包括调整相邻段中的喷嘴动作的步骤,以使各自段中的相应喷嘴不会同时喷墨。
7.如权利要求5所述的控制喷嘴动作的方法,其中所述的步骤包括为可选择段使用抖动矩阵的逆矩阵的方法步骤。
8.如权利要求5所述的控制喷嘴动作的方法,还包括根据所述插值功能的当前插值因子给出的概率把输出点随机提供给某个喷嘴的步骤。
9.如权利要求3所述的控制喷嘴动作的方法,还包括保证相邻重叠段中的对应喷嘴不会同时喷墨的步骤。
10.一种喷墨打印头系统,包括多个在空间上相互间隔的重叠打印头段;至少一种测量打印头段之间的重叠量的装置;为连续色调图像提供半色调的一种装置,以及用于调整相邻打印头段之间的重叠区域中的半色调的一种装置,以便减少上述相邻段之间的人为因素,该方法用于减少所述相邻段之间的人为因素的影响。
11.如权利要求10所述的喷墨打印头系统,其中所述至少一种测量打印头段之间的重叠量的装置包括一种测量所述相邻打印头段的温度的装置。
12.如权利要求10所述的喷墨打印头系统,其中所述至少一种测量打印头段之间的重叠量的装置包括一种测量所述重叠段的相对位移的装置。
13.如权利要求11所述的喷墨打印头系统,其中所述提供连续色调图像的半色调处理的方法包括一个抖动矩阵;所述用于调整所述半色调措施的方法包括一种带有两个输入的求和方法,一个输入是所述抖动矩阵的输出,所述另一个输入从所述至少一种测量相邻打印头段之间的重叠量的方法中得出。
14.如权利要求13所述的喷墨打印头系统,还包括一个用于把所述求和方法的输出与连续色调数据输入比较的比较器装置,所述比较器装置的输出是用于相邻打印头段的对应喷嘴的半色调数据。
15.如权利要求11所述的喷墨打印头系统,其中所述用于调整相邻打印头段之间的重叠区域的所述半色调处理方法的装置包括为可选择段对抖动矩阵求逆的装置。
16.如权利要求13所述的喷墨打印头系统,还包括一种从连续色调数据中减去所述求和装置的输出来产生驱动相邻打印段的喷嘴的半色调数据的装置。
17.一种喷墨打印头系统,包括多个在空间上相互间隔的重叠打印头段;至少一种测量打印头段之间的重叠量的装置;为连续色调图像提供半色调的一种装置,以及用于调整相邻打印头段之间的重叠区域中的半色调的一种装置,以便减少上述相邻段之间的人为因素,该方法用于减少所述相邻段之间的人为因素的影响;其中所述为连续色调图像提供半色调的一种装置以及用于调整相邻打印头段之间的重叠区域中的半色调以便减少所述相邻段之间的人为因素的一种装置,包括一个编有某种算法的可编程数字计算机,所述算法产生的功能实现对连续色调像素值的离散点随机抖动再现,使相邻段的对应喷嘴从不会同时打印一个点,而所需的输出值小于相邻段的两个输入抖动矩阵值的和。
18.如权利要求17所述的喷墨打印头系统,其中所述至少一种测量相邻打印头之间的重叠量的方法包括一种测量上述打印头段的温度的方法。
19.如权利要求17所述的喷墨打印头系统,其中所述至少一种测量相邻打印头之间的重叠量的方法包括一种测量所述打印头段相对位移的方法。
全文摘要
本发明提供了一种用于补偿页宽打印头系统中的段之间的可变重叠的方法和装置,以减少由于相邻重叠段的错位所导致的打印页面中可见人为因素的影响。本发明所述的一种方法利用一种求和装置,该求和装置把从抖动矩阵获得的当前抖动值与重叠信号求和,以提供一个输出值,该输出值在一个比较器中与输入的连续色调数据值进行比较,然后输出经过补偿的抖动数据,以控制相邻打印头段的重叠区域中的喷嘴。本发明中所述的另一种方法使用软件程序提供补偿的抖动矩阵。一种感知装置提供段的重叠量的数值,以产生重叠信号。该感知装置可能感知打印头段的温度或打印头段的相对位移。可以为各种温度确定相邻段的重叠量,然后把得到的重叠量数据存储到ROM中。
文档编号B41J2/205GK1452557SQ00819578
公开日2003年10月29日 申请日期2000年5月24日 优先权日2000年5月24日
发明者卡·西尔弗布鲁克, 保罗·拉普斯顿 申请人:西尔弗布鲁克研究有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1