IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python——读取FLIR热像仪记录的PTW格式文件并转换为RAW格式或Numpy格式 -> 正文阅读

[Python知识库]Python——读取FLIR热像仪记录的PTW格式文件并转换为RAW格式或Numpy格式

Python——读取FLIR热像仪记录的PTW格式文件并转换为RAW格式或Numpy格式

前言

提示:在此记录一下解决这个问题的过程以供他人参考或自己未来复盘:

最近需要处理某款FLIR热像仪产生的数据,这些数据被记录为了PTW格式。然而PTW格式只有厂家自带的软件才能读取,尽管能读取但又无法转存为RAW格式或其他格式用来自己处理(能转存为ascii码保存,也许也能从这方面下手),而且该软件的处理功能又十分鸡肋,所以迫切需要自己写一个。


提示:以下是本篇文章正文内容,下面案例可供参考

一、现在有什么方法可以解决上述问题?

1. 实验室现有的ptw2raw.exe代码

个人未采纳原因:

1??数据量大时候无法处理(可能是因为内存)
2??代码不开源难以更改
3??仅仅支持Windows系统,不同设备需要用U盘把数据拷来拷去很麻烦
4??无法批量化处理
5??需要输入参数而无法自动识别

2. pyradi开源代码(我主要参考的代码)

简要介绍:

将常用的红外数据处理方式封装为工具包,以方便反复调用。其中包含有读取PTW格式文件的子函数。
??开源地址:https://github.com/NelisW/pyradi
??文档地址:http://nelisw.github.io/pyradi-docs/_build/html/index.html

个人未采纳原因:

1??环境需要手动配置,且配置起来很麻烦
2??原生的代码存在bug,读取PTW文件的代码无法直接运行,感觉像是测试中未完成的代码直接上线了,下面给出修改办法,把报错的子函数全部改为下面的形式

原代码存在问题的部分:

def mybyte(x):
    # will return an error if x is more than a byte length
    # TODO need to write error catch routine
        ans = ord(x)
    return ans

修改办法:

def mybyte(x):
    # will return an error if x is more than a byte length
    # TODO need to write error catch routine
    try:
        ans = ord(x)
    except:
        ans = 0
    return ans

3??可能考虑到工具箱中的函数彼此要来回调用,所以对我要实现的功能来说是一个子函数的东西被切成了许多小的子函数,逻辑看起来十分混乱,使用不便
4??在实际使用过程中256*320大小的图像1800帧需要读取16分钟,18000帧的图像五个小时读取不完,随着数据量增加所需时间指数级上升
5??进一步读源码发现数据拼接使用了动态内存分配(时间随数据指数级上升的主要原因);大量使用循环,且循环里有很多不需要循环的功能浪费了很多时间

提示:本人参考上述工具包中的读取PTW文件部分进行了大量整合更改优化,最终实现了4s读取18000帧数据;无需配置上述工具包,仅需python自带和常用的包并参考下一章给出的三段代码即可实现

3. Matlab GUI

简要介绍:

具有下面这些功能且具有图形界面,应该是需要自己下载的

  • 对比度方法(绝对、运行、标准、归一化对比度)
  • 微分绝对对比度(DAC)
  • 插值微分绝对对比度 (IDAC)
  • Skewness & kurtosis
  • 匹配的过滤器
  • 傅立叶变换
  • 小波变换 (without the need of the wavelet toolbox)
  • 主要成分热成像 (PCT)
  • 热信号重建 (TSR) with derivatives
  • 多尺度retinex增强
  • Markov error contrast
  • Thermal harmonic distortion
  • Dynamic Thermal Topography
  • 其他基本图像处理方法

??参考地址:https://ww2.mathworks.cn/matlabcentral/fileexchange/54032-prophetanity-thermographic-image-processing-gui?s_tid=srchtitle_tsr_40

未采纳原因

1??没开源
2??似乎也不能读PTW格式文件
3??看评论也说数据量大会崩溃

二、到底什么是PTW格式文件?

提示:我也不知道

pyradi的源码注释中写到header代表的意义参考了‘DL002U-D Altair Reference Guide’,但全网都没有搜到,只能根据源码中读取ptw_header的部分进行一个简略的倒推。

如图所示
如图所示,红色部分的数据代表了整个这次拍摄的各种实验参数,比如图像长宽或总帧数各种header长度等等详细信息;而一蓝两橘代表的是某一帧的数据,其中蓝色部分代表的是该帧帧头,里面存储了当前帧的一些信息,而黄色部分代表的是图像数据,每两位代表一个像素的数值(pyradi在这里就是每次读取两位做一次转化存在大矩阵里也浪费了一些时间)

三、重新整合重构的代码

提示:改进后的代码分为两个子函数和一个主函数
my read header’用来读取各种帧头的信息(几乎直接使用了pyradi的代码)
my read frame‘用来读取所有帧数据(所有改动整合在了这里)
my read ptw‘是主函数,如有需求可在后续加入其他功能

my read header文件(里面有些函数也没用上,但因为不参与运算所以也没删掉)

import struct
import sys
import string

def myint(x):
    # two bytes length
    # print(type(x), type(x[0]), type(ord(x[0])))

    # ans = ord(x[0])+(ord(x[1])<<8)
    try:
        ans = struct.unpack('h',x)[0]
    except:
        ans = 0
    return ans

def mylong(x):
    # four bytes length
    if sys.version_info[0] > 2:
        ans = x[0] + (x[1]<<8) + (x[2]<<16) + (x[3]<<32)
    else:
        ans = ord(x[0]) + (ord(x[1])<<8) + (ord(x[2])<<16) + (ord(x[3])<<32)
    return ans

def myfloat(x):
    ans = struct.unpack('f',x)[0]
    # ans = ans[0]
    return ans

def mydouble(x):
    ans = struct.unpack('d',x)[0]
    # ans = ans[0]
    return ans

def mybyte(x):
    # will return an error if x is more than a byte length
    # TODO need to write error catch routine
    try:
        ans = ord(x)
    except:
        ans = 0
    return ans

def myRGB(x):
    # three bytes with RGB values
    # TODO need to write error catch routine
    R = x[0] if sys.version_info[0] > 2 else ord(x[0])
    G = x[1] if sys.version_info[0] > 2 else ord(x[1])
    B = x[2] if sys.version_info[0] > 2 else ord(x[2])

    ans = '#{:02x}{:02x}{:02x}'.format(R,G,B)
    return ans

def terminateStrOnZero (str):
    """Iterate through string and terminate on first zero
    """
    idx=0
    while idx < len(str) and str[idx] != '\00':
        idx += 1
    return str[:idx]


def showHeader(Header):
    """Utility function to print the PTW header information to stdout

    Args:
        | header (class object) ptw file header structure

    Returns:
        | None

    Raises:
        | No exception is raised.
    """

    print('{} version {}'.format(Header.h_Signature, Header.h_Version))
    print('Main Header Size {}'.format(Header.h_MainHeaderSize))
    print('Frame Header Size {}'.format(Header.h_FrameHeaderSize))
    print('Frame + Frame Header Size {}'.format(Header.h_SizeOfOneFrameAndHeader))
    print('Frame Size {}'.format(Header.h_SizeOfOneFrame))
    print('Number of Frames {}'.format(Header.h_NumberOfFieldInFile))
    # print Header.h_CurrentFieldNumber

    print('Year {} Month {} Day {}'.format(Header.h_FileSaveYear, Header.h_FileSaveMonth,
                                           Header.h_FileSaveDay))
    print('( {} / {} / {} )'.format(str(Header.h_FileSaveYear).zfill(2),
                                    str(Header.h_FileSaveMonth).zfill(2), str(Header.h_FileSaveDay).zfill(2)))
    print('Hour {} Minute {} Second {}'.format(Header.h_FileSaveHour, Header.h_FileSaveMinute,
                                               Header.h_FileSaveSecond))
    print('( {} : {} : {} )'.format(str(Header.h_FileSaveHour).zfill(2),
                                    str(Header.h_FileSaveMinute).zfill(2), str(Header.h_FileSaveSecond).zfill(2)))

    # print Header.h_Millieme
    print('Camera Name {}'.format(Header.h_CameraName))
    print('Lens {}'.format(Header.h_LensName))
    print('Filter {}'.format(Header.h_FilterName))
    print('Aperture Name {}'.format(Header.h_ApertureName))
    if Header.h_Signature == 'IRUS':
        print('{}'.format(Header.h_IRUSBilletSpeed))
        print('{}'.format(Header.h_IRUSBilletDiameter))
        print('{}'.format(Header.h_IRUSBilletShape))
    print('Emissivity {:.6f}'.format(Header.h_Emissivity))
    print('Ambient Temperature {:.6f} (K)'.format(Header.h_Ambiant))
    print('Ambient Temperature {:.6f} (degC)'.format(Header.h_Ambiant - 273.15))
    print('Distance to target {}'.format(Header.h_Distance))
    if Header.h_Signature == 'IRUS':
        print('{}'.format(Header.h_IRUSInductorCoil))
        print('{}'.format(Header.h_IRUSInductorPower))
        print('{}'.format(Header.h_IRUSInductorVoltage))
        print('{}'.format(Header.h_IRUSInductorFrequency))
        print('{}'.format(Header.h_IRUSSynchronization))
    print('Atm Transmission {}'.format(Header.h_AtmTransmission))
    print('Ext Coef {}'.format(Header.h_ExtinctionCoeficient))
    print('Target {}'.format(Header.h_Object))
    print('Optic {}'.format(Header.h_Optic))
    print('Atmo {}'.format(Header.h_Atmo))
    print('Atm Temp {:.6f}'.format(Header.h_AtmosphereTemp))
    print('Cut on Wavelength {:.6f}'.format(Header.h_CutOnWavelength))
    print('Cut off Wavelength {:.6f}'.format(Header.h_CutOffWavelength))
    print('PixelSize {}'.format(Header.h_PixelSize))
    print('PixelPitch {}'.format(Header.h_PixelPitch))
    print('Detector Apperture {}'.format(Header.h_DetectorApperture))
    print('Optic Focal Length {}'.format(Header.h_OpticsFocalLength))
    print('Housing Temp1 {:.6f} (K)'.format(Header.h_HousingTemperature1))
    print('Housing Temp2 {:.6f} (K)'.format(Header.h_HousingTemperature2))

    print('Sensor Temp4 {:.6f} (K)'.format(Header.h_sensorTemp4))
    print('Detector/FPA Temp {:.6f} (K)'.format(Header.h_detectorTemp))

    print('Camera Serial Number {}'.format(Header.h_CameraSerialNumber))
    print('Min Threshold {}'.format(Header.h_MinimumLevelThreshold))
    print('Max Threshold {}'.format(Header.h_MaximumLevelThreshold))
    # print Header.h_EchelleSpecial
    # print Header.h_EchelleUnit
    # print Header.h_EchelleValue
    print('Gain {}'.format(Header.h_LockinGain))
    print('Offset {}'.format(Header.h_LockinOffset))
    print('HZoom {}'.format(Header.h_HorizontalZoom))
    print('VZoom {}'.format(Header.h_VerticalZoom))
    print('Field {}'.format(Header.h_PixelsPerLine, 'X', Header.h_LinesPerField))
    print('AD converter {} bit'.format(Header.h_ADDynamic))

    if Header.h_Signature == 'SATIR':
        print('{}'.format(Header.h_SATIRTemporalFrameDepth))
        print('{}'.format(Header.h_SATIRLocationLongitude))
        print('{}'.format(Header.h_SATIRLocationLatitude))
        print('{}'.format(Header.h_SATIRLocationAltitude))
    if Header.h_ExternalSynch:
        print('Ext Sync ON')
    else:
        print('Ext Sync OFF')
    print('Header.h_Signature = {}'.format(Header.h_Signature))
    if Header.h_Signature == 'CED':
        print('CEDIP Period {:.6f} Hz'.format(1. / Header.h_CEDIPAquisitionPeriod))
        print('CEDIP Integration {:.6f} msec'.format(Header.h_CEDIPIntegrationTime * 1000))
    if Header.h_Signature == 'WOLF':
        print('{}'.format(Header.h_WOLFSubwindowCapability))
    if Header.h_Signature == 'ORI':
        print('{}'.format(Header.h_ORIONIntegrationTime))
        print('{}'.format(Header.h_ORIONFilterNames))
    print('NUC {}'.format(Header.h_NucTable))
    # print Header.h_Reserve6
    print('Comment {}'.format(Header.h_Comment))

    print('Calibration File Name {}'.format(Header.h_CalibrationFileName))

    print('Tools File Name {}'.format(Header.h_ToolsFileName))

    print('Palette Index {}'.format(Header.h_PaletteIndexValid))
    print('Palette Current {}'.format(Header.h_PaletteIndexCurrent))
    print('Palette Toggle {}'.format(Header.h_PaletteToggle))
    print('Palette AGC {}'.format(Header.h_PaletteAGC))
    print('Unit Index {}'.format(Header.h_UnitIndexValid))
    print('Current Unit Index {}'.format(Header.h_CurrentUnitIndex))
    print('Zoom Pos {}'.format(Header.h_ZoomPosition))
    print('Key Framenum {}'.format(Header.h_KeyFrameNumber))
    print('Num Keyframes {}'.format(Header.h_KeyFramesInFilm))
    print('Player lock {}'.format(Header.h_PlayerLocked))
    print('Frame Select {}'.format(Header.h_FrameSelectionValid))
    print('ROI Start {}'.format(Header.h_FrameofROIStart))
    print('ROI Stop {}'.format(Header.h_FrameofROIEnd))
    print('Player inf loop {}'.format(Header.h_PlayerInfinitLoop))
    print('Player Init Frame {}'.format(Header.h_PlayerInitFrame))

    print('Isoterm0 {}'.format(Header.h_Isoterm0Active))
    print('Isoterm0 DL Min {}'.format(Header.h_Isoterm0DLMin))
    print('Isoterm0 DL Max {}'.format(Header.h_Isoterm0DLMax))
    print('Isoterm0 Color RGB {}'.format(Header.h_Isoterm0Color))

    print('Isoterm1 {}'.format(Header.h_Isoterm1Active))
    print('Isoterm1 DL Min {}'.format(Header.h_Isoterm1DLMin))
    print('Isoterm1 DL Max {}'.format(Header.h_Isoterm1DLMax))
    print('Isoterm1 Color RGB {}'.format(Header.h_Isoterm1Color))

    print('Isoterm2 {}'.format(Header.h_Isoterm2Active))
    print('Isoterm2 DL Min {}'.format(Header.h_Isoterm2DLMin))
    print('Isoterm2 DL Max {}'.format(Header.h_Isoterm2DLMax))
    print('Isoterm2 Color RGB {}'.format(Header.h_Isoterm2Color))

    print('Zero {}'.format(Header.h_ZeroActive))
    print('Zero DL {}'.format(Header.h_ZeroDL))
    print('Palette Width {}'.format(Header.h_PaletteWidth))
    print('PaletteF Full {}'.format(Header.h_PaletteFull))
    print('PTR Frame Buffer type {}'.format(Header.h_PTRFrameBufferType))
    print('Thermoelasticity {}'.format(Header.h_ThermoElasticity))
    print('Demodulation {}'.format(Header.h_DemodulationFrequency))
    print('Coordinate Type {}'.format(Header.h_CoordinatesType))
    print('X Origin {}'.format(Header.h_CoordinatesXorigin))
    print('Y Origin {}'.format(Header.h_CoordinatesYorigin))
    print('Coord Show Orig {}'.format(Header.h_CoordinatesShowOrigin))
    print('Axe Colour RGB {}'.format(Header.h_AxeColor))
    print('Axe Size {}'.format(Header.h_AxeSize))
    print('Axe Valid {}'.format(Header.h_AxeValid))
    print('Distance offset {}'.format(Header.h_DistanceOffset))
    print('Histogram {}'.format(Header.h_HistoEqualizationEnabled))
    print('Histogram % {}'.format(Header.h_HistoEqualizationPercent))

    print('Calibration File Name {}'.format(Header.h_CalibrationFileName))

    print('PTRFrame Valid {}'.format(Header.h_PTRTopFrameValid))
    print('Subsampling {}'.format(Header.h_SubSampling))
    print('Camera flip H {}'.format(Header.h_CameraHFlip))
    print('Camera flip V {}'.format(Header.h_CameraHVFlip))
    print('BB Temp {}'.format(Header.h_BBTemp))
    print('Capture Wheel Index {}'.format(Header.h_CaptureWheelIndex))
    print('Capture Wheel Focal Index {}'.format(Header.h_CaptureFocalIndex))
    # print Header.h_Reserved7
    # print Header.h_Reserved8
    # print Header.h_Framatone


################################################################
class PTWFrameInfo:
    """Class to store the ptw file header information.
    """

    def __init__(self):
        self.FileName = ''
        self.h_Signature = ''  # [0:5]
        self.h_format = 'unknown'
        self.h_unit = ''
        self.h_Version = ''  # [5:10]
        self.h_EofAsciiCode = 0  # [10]
        self.h_MainHeaderSize = 0  # [11:15]
        self.h_FrameHeaderSize = 0  # [15:19]
        self.h_SizeOfOneFrameAndHeader = 0  # [19:23]
        self.h_SizeOfOneFrame = 0  # [23:27]
        self.h_NumberOfFieldInFile = 0  # [27:31]
        self.h_CurrentFieldNumber = 0  # [31:35]

        # self.h_FileSaveDate = '' #[35:39]
        self.h_FileSaveYear = 0
        self.h_FileSaveMonth = 0
        self.h_FileSaveDay = 0

        # self.h_FileSaveTime = '' #[39:43]
        self.h_FileSaveHour = 0
        self.h_FileSaveMinute = 0
        self.h_FileSaveSecond = 0
        self.h_FileSaveCent = 0

        self.h_Millieme = 0  # [43:44]
        self.h_CameraName = ''  # [44:64]
        self.h_LensName = ''  # [64:84]
        self.h_FilterName = ''  # [84:104]
        self.h_ApertureName = ''  # [104:124]
        self.h_IRUSBilletSpeed = 0  # [124:128] # IRUS
        self.h_IRUSBilletDiameter = 0  # myfloat(headerinfo[128:132]) # IRUS
        self.h_IRUSBilletShape = 0  # myint(headerinfo[132:134]) #IRUS
        self.h_Emissivity = 0  # myfloat(headerinfo[141:145])
        self.h_Ambiant = 0  # myfloat(headerinfo[145:149])
        self.h_Distance = 0  # myfloat(headerinfo[149:153])
        self.h_IRUSInductorCoil = 0  # ord(everthing[153:154]) # IRUS
        self.h_IRUSInductorPower = 0  # mylong(headerinfo[154:158]) # IRUS
        self.h_IRUSInductorVoltage = 0  # myint(headerinfo[158:160]) # IRUS
        self.h_IRUSInductorFrequency = 0  # mylong(headerinfo[160:164]) # IRUS
        self.h_IRUSSynchronization = 0  # ord(headerinfo[169:170]) # IRUS
        self.h_AtmTransmission = 0  # myfloat(headerinfo[170:174])
        self.h_ExtinctionCoeficient = 0  # myfloat(headerinfo[174:178])
        self.h_Object = 0  # myint(headerinfo[178:180])
        self.h_Optic = 0  # myint(headerinfo[180:182])
        self.h_Atmo = 0  # myint(headerinfo[182:184])
        self.h_AtmosphereTemp = 0  # myfloat(headerinfo[184:188])
        self.h_CutOnWavelength = 0  # myfloat(headerinfo[188:192])
        self.h_CutOffWavelength = 0  # myfloat(headerinfo[192:196])
        self.h_PixelSize = 0  # myfloat(headerinfo[196:200])
        self.h_PixelPitch = 0  # myfloat(headerinfo[200:204])
        self.h_DetectorApperture = 0  # myfloat(headerinfo[204:208])
        self.h_OpticsFocalLength = 0  # myfloat(headerinfo[208:212])
        self.h_HousingTemperature1 = 0  # myfloat(headerinfo[212:216])
        self.h_HousingTemperature2 = 0  # myfloat(headerinfo[216:220])
        self.h_CameraSerialNumber = ''  # headerinfo[220:231])
        self.h_MinimumLevelThreshold = 0  # myint(headerinfo[245:247])
        self.h_MaximumLevelThreshold = 0  # myint(headerinfo[247:249])
        self.h_EchelleSpecial = 0  # myint(headerinfo[277:279])
        self.h_EchelleUnit = 0  # headerinfo[279:289]
        self.h_EchelleValue = 0  # (headerinfo[289:357]) # 16 float values
        self.h_Units = ''
        self.h_Lockin = 0
        self.h_LockinGain = 0  # myfloat(headerinfo[357:361])
        self.h_LockinOffset = 0  # myfloat(headerinfo[361:365])
        self.h_HorizontalZoom = 0  # myfloat(headerinfo[365:369])
        self.h_VerticalZoom = 0  # myfloat(headerinfo[369:373])
        self.h_PixelsPerLine = 0  # myint(headerinfo[377:379])
        self.h_LinesPerField = 0  # myint(headerinfo[379:381])
        self.h_Rows = 0
        self.h_Cols = 0

        self.h_framepointer = 1
        self.h_firstframe = 1
        self.h_cliprect = [0, 0, 1, 1]
        self.h_lastframe = 0
        self.h_FrameSize = 0

        self.h_ADDynamic = 0  # myint(headerinfo[381:383])
        self.h_SATIRTemporalFrameDepth = 0  # myint(headerinfo[383:385]) # SATIR
        self.h_SATIRLocationLongitude = 0  # myfloat(headerinfo[385:389]) # SATIR
        self.h_SATIRLocationLatitude = 0  # myfloat(headerinfo[389:393]) # SATIR South is negative
        self.h_SATIRLocationAltitude = 0  # myfloat(headerinfo[393:397]) # SATIR
        self.h_ExternalSynch = 0  # ord(headerinfo[397]) # 1=External 0 = Internal
        self.h_CEDIPAquisitionPeriod = 0  # myfloat(headerinfo[403:407]) # CEDIP seconds
        self.h_CEDIPIntegrationTime = 0  # myfloat(headerinfo[407:411]) # CEDIP seconds
        self.h_WOLFSubwindowCapability = 0  # myint(headerinfo[411:413]) # WOLF
        self.h_ORIONIntegrationTime = 0  # myfloat(headerinfo[413:437]) # ORION (6 values)
        self.h_ORIONFilterNames = ''  # headerinfo[437:557]) # ORION 6 fields of 20 chars each
        self.h_NucTable = 0  # myint(headerinfo[557:559])
        self.h_Reserve6 = ''  # headerinfo[559:563]
        self.h_Comment = ''  # headerinfo[563:1563]
        self.h_CalibrationFileName = ''  # headerinfo[1563:1663]
        self.h_ToolsFileName = ''  # headerinfo[1663:1919]
        self.h_PaletteIndexValid = 0  # ord(headerinfo[1919:1920])
        self.h_PaletteIndexCurrent = 0  # myint(1920:1922])
        self.h_PaletteToggle = 0  # ord(headerinfo[1922:1923])
        self.h_PaletteAGC = 0  # ord(headerinfo[1923:1924])
        self.h_UnitIndexValid = 0  # ord(headerinfo[1924:1925])
        self.h_CurrentUnitIndex = 0  # myint(headerinfo[1925:1927])
        self.h_ZoomPosition = 0  # (headerinfo[1927:1935]) # unknown format POINT
        self.h_KeyFrameNumber = 0  # ord(headerinfo[1935:1936])
        self.h_KeyFramesInFilm = 0  # headerinfo[1936:2056] # set of 30 frames
        self.h_PlayerLocked = 0  # ord(headerinfo[2057:2057])
        self.h_FrameSelectionValid = 0  # ord(headerinfo[2057:2058])
        self.h_FrameofROIStart = 0  # mylong(headerinfo[2058:2062])
        self.h_FrameofROIEnd = 0  # mylong(headerinfo[2062:2066])
        self.h_PlayerLockedROI = 0  # ord(headerinfo[2066:2067])
        self.h_PlayerInfinitLoop = 0  # ord(headerinfo[2067:2068])
        self.h_PlayerInitFrame = 0  # mylong(headerinfo[2068:2072])

        self.h_Isoterm0Active = 0  # ord(headerinfo[2072:2073])
        self.h_Isoterm0DLMin = 0  # myint(headerinfo[2073:2075])
        self.h_Isoterm0DLMax = 0  # myint(headerinfo[2075:2077])
        self.h_Isoterm0Color = 0  # headerinfo[2077:2081]

        self.h_Isoterm1Active = 0  # ord(headerinfo[2081:2082])
        self.h_Isoterm1DLMin = 0  # myint(headerinfo[2082:2084])
        self.h_Isoterm1DLMax = 0  # myint(headerinfo[2084:2086])
        self.h_Isoterm1Color = 0  # headerinfo[2086:2090]

        self.h_Isoterm2Active = 0  # ord(headerinfo[2090:2091])
        self.h_Isoterm2DLMin = 0  # myint(headerinfo[2091:2093])
        self.h_Isoterm2DLMax = 0  # myint(headerinfo[2093:2095])
        self.h_Isoterm2Color = 0  # headerinfo[2095:2099]

        self.h_ZeroActive = 0  # ord(headerinfo[2099:2100])
        self.h_ZeroDL = 0  # myint(headerinfo[2100:2102])
        self.h_PaletteWidth = 0  # myint(headerinfo[2102:2104])
        self.h_PaletteFull = 0  # ord(headerinfo[2104:2105])
        self.h_PTRFrameBufferType = 0  # ord(headerinfo[2105:2106]) # 0=word 1=double
        self.h_ThermoElasticity = 0  # headerinfo[2106:2114] # type double (64 bits)
        self.h_DemodulationFrequency = 0  # myfloat(headerinfo[2114:2118])
        self.h_CoordinatesType = 0  # mylong(headerinfo[2118:2122])
        self.h_CoordinatesXorigin = 0  # mylong(headerinfo[2122:2126])
        self.h_CoordinatesYorigin = 0  # mylong(headerinfo[2126:2130])
        self.h_CoordinatesShowOrigin = 0  # ord(headerinfo[2130:2131])
        self.h_AxeColor = 0  # headerinfo[2131:2135]
        self.h_AxeSize = 0  # mylong(headerinfo[2135:2139])
        self.h_AxeValid = 0  # ord(headerinfo[2139:2140])
        self.h_DistanceOffset = 0  # myfloat(headerinfo[2140:2144])
        self.h_HistoEqualizationEnabled = 0  # ord(headerinfo[2144:2145])
        self.h_HistoEqualizationPercent = 0  # myint(headerinfo[2145:2147])
        self.h_CalibrationFileName = ''  # headerinfo[2147:2403]
        self.h_PTRTopFrameValid = 0  # ord(headerinfo[2403:2404])
        self.h_SubSampling = 0  # myint(headerinfo[2404:2408])
        self.h_CameraHFlip = 0  # ord(headerinfo[2408:2409])
        self.h_CameraHVFlip = 0  # ord(headerinfo[2409:2410])
        self.h_BBTemp = 0  # myfloat(headerinfo[2410:2414])
        self.h_CaptureWheelIndex = 0  # ord(headerinfo[2414:2415])
        self.h_CaptureFocalIndex = 0  # ord(headerinfo[2415:2416])
        self.h_Reserved7 = ''  # headerinfo[2416:3028]
        self.h_Reserved8 = ''  # headerinfo[3028:3076]
        self.h_Framatone = 0  # ord(headerinfo[3076:3077]

        # Container for a single frame
        self.data = []
        self.minval = 0
        self.maxval = 0

        # Frame time
        self.h_frameMinute = 0  # FrameHeader[80:81]
        self.h_frameHour = 0  # FrameHeader[81:82]
        self.h_frameSecond = 0  # h_second+(h_thousands+h_hundred)/1000.0

        # detector / FPA temperature
        self.h_detectorTemp = 0.0  # FrameHeader[228:232]
        self.h_sensorTemp4 = 0.0  # FrameHeader[232:236]


# End of header definition

################################################################

def readPTWHeader(ptwfilename):
    """Given a ptw filename, read the header and return the header to caller

    Args:
        | filename (string) with full path to the ptw file.

    Returns:
        | Header (class) containing all PTW header information.

    Raises:
        | No exception is raised.

    Reference:
       h_variables of the header and byte positions are obtained
       from DL002U-D Altair Reference Guide
     """

    # Define the variables holding the header values
    headerinfo = ''  # the vector holding the file header
    Header = PTWFrameInfo()

    # Read file to get the header size
    Header.FileName = ptwfilename
    fid = open(ptwfilename, 'rb')
    headerinfo = fid.read(16)
    fid.close()
    MainHeaderSize = mylong(headerinfo[11:15])

    # Open file again and read the header information using the header size
    fid = open(ptwfilename, 'rb')
    headerinfo = fid.read(MainHeaderSize)

    if sys.version_info[0] > 2:
        Header.h_Signature = headerinfo[0:3].decode('utf-8')
    else:
        Header.h_Signature = headerinfo[0:3]
    if Header.h_Signature == 'AIO':  # AGEMA
        Header.h_format = 'agema'
    elif Header.h_Signature == 'CED':
        Header.h_format = 'cedip'
        Header.h_unit = 'dl'

    if sys.version_info[0] > 2:
        Header.h_Version = headerinfo[5:10].decode('utf-8')
    else:
        Header.h_Version = headerinfo[5:10]
    if not Header.h_Version[-1] in string.printable:
        Header.h_Version = Header.h_Version[:-1]
    Header.h_EofAsciiCode = mybyte(headerinfo[10:11])
    Header.h_MainHeaderSize = mylong(headerinfo[11:15])
    Header.h_FrameHeaderSize = mylong(headerinfo[15:19])
    Header.h_SizeOfOneFrameAndHeader = mylong(headerinfo[19:23])
    Header.h_SizeOfOneFrame = mylong(headerinfo[23:27])
    Header.h_NumberOfFieldInFile = mylong(headerinfo[27:31])
    # Header.h_CurrentFieldNumber = myint(headerinfo[31:35])
    Header.h_CurrentFieldNumber = mylong(headerinfo[31:35])

    # Header.h_FileSaveDate = '' #[35:39] decoded below
    Header.h_FileSaveYear = myint(headerinfo[35:37])
    Header.h_FileSaveDay = ord(headerinfo[37:38])
    Header.h_FileSaveMonth = ord(headerinfo[38:39])

    # Header.h_FileSaveTime = '' #[39:43] decoded below
    Header.h_FileSaveMinute = ord(headerinfo[39:40])
    Header.h_FileSaveHour = ord(headerinfo[40:41])
    Header.h_FileSaveCent = ord(headerinfo[41:42])
    Header.h_FileSaveSecond = ord(headerinfo[42:43])

    Header.h_Millieme = ord(headerinfo[43:44])

    if sys.version_info[0] > 2:
        stripchar = terminateStrOnZero(headerinfo[44:64]).decode('utf-8')[-1]
        Header.h_CameraName = terminateStrOnZero(headerinfo[44:64]).decode('utf-8').rstrip(stripchar)
        Header.h_LensName = terminateStrOnZero(headerinfo[64:84]).decode('utf-8').rstrip(stripchar)
        Header.h_FilterName = terminateStrOnZero(headerinfo[84:104]).decode('utf-8').rstrip(stripchar)
        Header.h_ApertureName = terminateStrOnZero(headerinfo[104:124]).decode('utf-8').rstrip(stripchar)
    else:
        Header.h_CameraName = terminateStrOnZero(headerinfo[44:64])
        Header.h_LensName = terminateStrOnZero(headerinfo[64:84])
        Header.h_FilterName = terminateStrOnZero(headerinfo[84:104])
        Header.h_ApertureName = terminateStrOnZero(headerinfo[104:124])

    Header.h_IRUSBilletSpeed = myfloat(headerinfo[124:128])  # IRUS
    Header.h_IRUSBilletDiameter = myfloat(headerinfo[128:132])  # IRUS
    Header.h_IRUSBilletShape = myint(headerinfo[132:134])  # IRUS
    Header.h_Reserved134 = mybyte(headerinfo[134:141])
    Header.h_Emissivity = myfloat(headerinfo[141:145])
    Header.h_Ambiant = myfloat(headerinfo[145:149])
    Header.h_Distance = myfloat(headerinfo[149:153])
    Header.h_IRUSInductorCoil = ord(headerinfo[153:154])  # IRUS
    Header.h_IRUSInductorPower = mylong(headerinfo[154:158])  # IRUS
    Header.h_IRUSInductorVoltage = myint(headerinfo[158:160])  # IRUS
    Header.h_IRUSInductorFrequency = mylong(headerinfo[160:164])  # IRUS
    Header.h_Reserved164 = mybyte(headerinfo[164:169])
    Header.h_IRUSSynchronization = ord(headerinfo[169:170])  # IRUS
    Header.h_AtmTransmission = myfloat(headerinfo[170:174])
    Header.h_ExtinctionCoeficient = myfloat(headerinfo[174:178])
    Header.h_Object = myint(headerinfo[178:180])
    Header.h_Optic = myint(headerinfo[180:182])
    Header.h_Atmo = myint(headerinfo[182:184])
    Header.h_AtmosphereTemp = myfloat(headerinfo[184:188])
    Header.h_CutOnWavelength = myfloat(headerinfo[188:192])
    Header.h_CutOffWavelength = myfloat(headerinfo[192:196])
    Header.h_PixelSize = myfloat(headerinfo[196:200])
    Header.h_PixelPitch = myfloat(headerinfo[200:204])
    Header.h_DetectorApperture = myfloat(headerinfo[204:208])
    Header.h_OpticsFocalLength = myfloat(headerinfo[208:212])
    Header.h_HousingTemperature1 = myfloat(headerinfo[212:216])
    Header.h_HousingTemperature2 = myfloat(headerinfo[216:220])
    if sys.version_info[0] > 2:
        stripchar = terminateStrOnZero(headerinfo[220:231]).decode('utf-8')[-1]
        Header.h_CameraSerialNumber = terminateStrOnZero(headerinfo[220:231]).decode('utf-8').rstrip(stripchar)
    else:
        Header.h_CameraSerialNumber = terminateStrOnZero(headerinfo[220:231])

    Header.h_Reserved231 = mybyte(headerinfo[231:239])
    Header.h_DetectorCode = myint(headerinfo[239:243])
    Header.h_DetectorGain = myint(headerinfo[245:247])

    Header.h_MinimumLevelThreshold = myint(headerinfo[245:247])
    Header.h_MaximumLevelThreshold = myint(headerinfo[247:249])
    Header.h_EchelleSpecial = myint(headerinfo[277:279])
    Header.h_EchelleUnit = headerinfo[279:289]
    Header.h_EchelleValue = headerinfo[289:357]  # 16 float values

    if (Header.h_EchelleSpecial == 0):
        Header.h_Units = 'dl'  # [dl T rad]
    else:
        Header.h_Units = Header.h_EchelleUnit  # [dl T rad]

    Header.h_LockinGain = myfloat(headerinfo[357:361])
    Header.h_LockinOffset = myfloat(headerinfo[361:365])
    Header.h_HorizontalZoom = myfloat(headerinfo[365:369])
    Header.h_VerticalZoom = myfloat(headerinfo[369:373])

    Header.h_PixelsPerLine = myint(headerinfo[377:379])
    Header.h_LinesPerField = myint(headerinfo[379:381])
    if Header.h_LinesPerField == 0:
        Header.h_LinesPerField = 128
    if Header.h_PixelsPerLine == 0:
        Header.h_PixelsPerLine = 128

    Header.h_Rows = Header.h_LinesPerField
    Header.h_Cols = Header.h_PixelsPerLine

    Header.h_cliprect = [0, 0, Header.h_Cols - 1, Header.h_Rows - 1]
    Header.h_lastframe = Header.h_NumberOfFieldInFile
    Header.h_FrameSize = Header.h_FrameHeaderSize + Header.h_Cols * Header.h_Rows * 2

    Header.h_ADDynamic = myint(headerinfo[381:383])
    Header.h_SATIRTemporalFrameDepth = myint(headerinfo[383:385])  # SATIR
    Header.h_SATIRLocationLongitude = myfloat(headerinfo[385:389])  # SATIR
    Header.h_SATIRLocationLatitude = myfloat(headerinfo[389:393])  # SATIR South is negative
    Header.h_SATIRLocationAltitude = myfloat(headerinfo[393:397])  # SATIR
    if sys.version_info[0] > 2:
        Header.h_ExternalSynch = headerinfo[397]  # 1=External 0 = Internal
    else:
        Header.h_ExternalSynch = ord(headerinfo[397])  # 1=External 0 = Internal

    Header.h_CEDIPAquisitionPeriod = myfloat(headerinfo[403:407])  # CEDIP seconds
    Header.h_CEDIPIntegrationTime = myfloat(headerinfo[407:411])  # CEDIP seconds
    Header.h_WOLFSubwindowCapability = myint(headerinfo[411:413])  # WOLF
    Header.h_ORIONIntegrationTime = headerinfo[413:437]  # ORION (6 values)
    Header.h_ORIONFilterNames = headerinfo[437:557]  # ORION 6 fields of 20 chars each
    Header.h_NucTable = myint(headerinfo[557:559])
    Header.h_Reserve6 = headerinfo[559:563]
    if sys.version_info[0] > 2:
        stripchar = terminateStrOnZero(headerinfo[563:1563]).decode('utf-8')[-1]
        Header.h_Comment = terminateStrOnZero(headerinfo[563:1563]).decode('utf-8').rstrip(stripchar)
        stripchar = terminateStrOnZero(headerinfo[1563:1663]).decode('utf-8')[-1]
        Header.h_CalibrationFileName = terminateStrOnZero(headerinfo[1563:1663]).decode('utf-8').rstrip(stripchar)
        stripchar = terminateStrOnZero(headerinfo[1663:1919]).decode('utf-8')[-1]
        Header.h_ToolsFileName = terminateStrOnZero(headerinfo[1663:1919]).decode('utf-8').rstrip(stripchar)
    else:
        Header.h_Comment = terminateStrOnZero(headerinfo[563:1563])
        Header.h_CalibrationFileName = terminateStrOnZero(headerinfo[1563:1663])
        Header.h_ToolsFileName = terminateStrOnZero(headerinfo[1663:1919])

    Header.h_PaletteIndexValid = ord(headerinfo[1919:1920])
    Header.h_PaletteIndexCurrent = myint(headerinfo[1920:1922])
    Header.h_PaletteToggle = ord(headerinfo[1922:1923])
    Header.h_PaletteAGC = ord(headerinfo[1923:1924])
    Header.h_UnitIndexValid = ord(headerinfo[1924:1925])
    Header.h_CurrentUnitIndex = myint(headerinfo[1925:1927])
    if sys.version_info[0] > 2:
        stripchar = terminateStrOnZero(headerinfo[1927:1935]).decode('utf-8')[-1]
        Header.h_ZoomPosition = terminateStrOnZero(headerinfo[1927:1935]).decode('utf-8').rstrip(
            stripchar)  # unknown format POINT
        Header.h_KeyFramesInFilm = terminateStrOnZero(headerinfo[1936:2056]).decode('utf-8').rstrip(
            stripchar)  # set of 30 frames
    else:
        Header.h_ZoomPosition = terminateStrOnZero(headerinfo[1927:1935])  # unknown format POINT
        Header.h_KeyFramesInFilm = terminateStrOnZero(headerinfo[1936:2056])  # set of 30 frames
    Header.h_KeyFrameNumber = ord(headerinfo[1935:1936])
    Header.h_PlayerLocked = ord(headerinfo[2056:2057])
    Header.h_FrameSelectionValid = ord(headerinfo[2057:2058])
    Header.h_FrameofROIStart = mylong(headerinfo[2058:2062])
    Header.h_FrameofROIEnd = mylong(headerinfo[2062:2066])
    Header.h_PlayerLockedROI = ord(headerinfo[2066:2067])
    Header.h_PlayerInfinitLoop = ord(headerinfo[2067:2068])
    Header.h_PlayerInitFrame = mylong(headerinfo[2068:2072])

    Header.h_Isoterm0Active = ord(headerinfo[2072:2073])
    Header.h_Isoterm0DLMin = myint(headerinfo[2073:2075])
    Header.h_Isoterm0DLMax = myint(headerinfo[2075:2077])
    Header.h_Isoterm0Color = myRGB(headerinfo[2077:2081])

    Header.h_Isoterm1Active = ord(headerinfo[2081:2082])
    Header.h_Isoterm1DLMin = myint(headerinfo[2082:2084])
    Header.h_Isoterm1DLMax = myint(headerinfo[2084:2086])
    Header.h_Isoterm1Color = myRGB(headerinfo[2086:2090])

    Header.h_Isoterm2Active = ord(headerinfo[2090:2091])
    Header.h_Isoterm2DLMin = myint(headerinfo[2091:2093])
    Header.h_Isoterm2DLMax = myint(headerinfo[2093:2095])
    Header.h_Isoterm2Color = myRGB(headerinfo[2095:2099])

    Header.h_ZeroActive = ord(headerinfo[2099:2100])
    Header.h_ZeroDL = myint(headerinfo[2100:2102])
    Header.h_PaletteWidth = myint(headerinfo[2102:2104])
    Header.h_PaletteFull = ord(headerinfo[2104:2105])
    Header.h_PTRFrameBufferType = ord(headerinfo[2105:2106])  # 0=word 1=double
    Header.h_ThermoElasticity = mydouble(headerinfo[2106:2114])  # type double (64 bits)
    Header.h_DemodulationFrequency = myfloat(headerinfo[2114:2118])
    Header.h_CoordinatesType = mylong(headerinfo[2118:2122])
    Header.h_CoordinatesXorigin = mylong(headerinfo[2122:2126])
    Header.h_CoordinatesYorigin = mylong(headerinfo[2126:2130])
    Header.h_CoordinatesShowOrigin = ord(headerinfo[2130:2131])
    Header.h_AxeColor = myRGB(headerinfo[2131:2135])
    Header.h_AxeSize = mylong(headerinfo[2135:2139])
    Header.h_AxeValid = ord(headerinfo[2139:2140])
    Header.h_DistanceOffset = myfloat(headerinfo[2140:2144])
    Header.h_HistoEqualizationEnabled = ord(headerinfo[2144:2145])
    Header.h_HistoEqualizationPercent = myint(headerinfo[2145:2147])
    if sys.version_info[0] > 2:
        stripchar = terminateStrOnZero(headerinfo[2147:2403]).decode('utf-8')[-1]
        Header.h_CalibrationFileName = terminateStrOnZero(headerinfo[2147:2403]).decode('utf-8').rstrip(stripchar)
    else:
        Header.h_CalibrationFileName = terminateStrOnZero(headerinfo[2147:2403])

    Header.h_PTRTopFrameValid = ord(headerinfo[2403:2404])
    # Header.h_SubSampling = myint(headerinfo[2404:2408])
    Header.h_SubSampling = mylong(headerinfo[2404:2408])
    Header.h_CameraHFlip = ord(headerinfo[2408:2409])
    Header.h_CameraHVFlip = ord(headerinfo[2409:2410])
    Header.h_BBTemp = myfloat(headerinfo[2410:2414])
    Header.h_CaptureWheelIndex = ord(headerinfo[2414:2415])
    Header.h_CaptureFocalIndex = ord(headerinfo[2415:2416])
    Header.h_Reserved7 = headerinfo[2416:3028]
    Header.h_Reserved8 = headerinfo[3028:3076]
    Header.h_Framatone = ord(headerinfo[3076:3077])

    # Read the first video frame info, not the data
    # to determine lockin information

    fid.seek(Header.h_MainHeaderSize, 0)  # ,'bof')  %skip main header
    fid.seek(Header.h_FrameHeaderSize, 1)  # 'cof')  %skip frame header
    firstline = fid.read(Header.h_Cols)  # , 'uint16')  %read one line

    # look if first line contains lockin information
    if (firstline[1:4] == [1220, 3907, 1204, 2382]):
        Header.h_Lockin = 1
        Header.h_Rows = Header.h_Rows - 1#就是说如果查到的lockin 那么rows会被减一并重新赋值
        print('* LOCKIN')
    else:
        Header.h_Lockin = 0

    fid.close()

    return Header

################################################################

myreadframe文件

from myreadhead import  myint
import numpy as np
from tqdm import tqdm
def GetPTWFrameFromFile(header):
    # Check if this is  a cedip file
    errorresult = np.asarray([0])
    if header.h_format!='cedip':
        print('ReadJade Error: file format is not supported')
        return errorresult #如果return后不执行后面的代码

    frameNunber = header.h_NumberOfFieldInFile
    #frameNunber = 100 #测试用
    rows = header.h_Rows#获取行数
    cols = header.h_Cols#获取列数
    dataAll = np.eye(frameNunber*rows, cols)#初始化一个数组 防止后面动态分配速度慢
    dataAll = dataAll.reshape(frameNunber, rows, cols)
    fid = open(header.FileName,'rb')
    fid.seek (header.h_MainHeaderSize,0)# 跳过main header部分 (移动指针)

    for i in tqdm(range(frameNunber)):#有多少帧就循环多少次
        fid.seek(header.h_FrameHeaderSize, 1)#跳过当前帧头

        #方法一
        # header.data = np.eye(cols, rows)#初始化一个图像大小的矩阵 相当于一个转置矩阵
        # for y in range(header.h_Rows):#把一维的序列每两个一组转换成一个单位并循环给矩阵赋值 有没有什么更好的方法
        #     for x in range(header.h_Cols):
        #         header.data[x][y] = myint(fid.read(2))

        #方法二
        bytesdata = fid.read(2 * cols * rows)
        header.data = np.frombuffer(bytesdata, dtype=np.uint16).reshape(cols, rows,order='F')

        if(header.h_Lockin): # 判断是否为锁相 如果是需要多跳过一列 由于储存问题需要跳过两倍的距离
            fid.seek (2*header.h_Cols,1)

        if(header.h_EchelleSpecial):# if a special scale is given then transform the data
            low = min(header.h_EchelleScaleValue)
            high = max(header.h_EchelleScaleValue)
            header.data = header.data * (high-low)/ 2.0**16 + low

        header.data = header.data.conj().transpose()
        dataAll[i, :, :] = header.data

    fid.close()  #close file
    return dataAll

#%%
import myreadhead as ryptw
import myreadframe as ryfrm
from matplotlib import pyplot as plt
import numpy as np
from scipy import io
header = ryptw.readPTWHeader('cap1.ptw')#header检测完的行列就是图像真实行列数 lockin多出来的col已经被减掉了

ryptw.showHeader(header)
data_all = ryfrm.GetPTWFrameFromFile(header)
print(data_all.shape)
#%% 储存为numpy数组格式 对于18000帧的数据是10多g
np.save("cap1.npy", data_all)

#%% 储存为raw格式 对于18000帧的数据大小与np格式一致
data_all.astype(np.uint16)
data_all.tofile('cap1.raw')

#%%储存为mat格式方便Matlab直接使用
import numpy as np
from scipy import io
#data_all = np.load("cap1.npy")
io.savemat('cap1.mat', {'ndarray': data_all})
#%%
plt.imshow(data_all[22])
plt.show()

功能

1??仅需要输入ptw文件位置和输出文件要保存的位置即可无脑转换为raw
2??热像仪后面插了其他线的lockin模式也什么都不用管 无脑转

TODO

后续可能会把需要用到的处理数据的功能写进主函数里。

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-09-15 01:58:19  更:2022-09-15 01:58:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 10:17:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码