122 lines
4.6 KiB
Python
122 lines
4.6 KiB
Python
|
|
import numpy as np
|
|||
|
|
import spectral
|
|||
|
|
import spectral.io.envi as envi
|
|||
|
|
|
|||
|
|
def downsample_bands_extract(data, factor=3, offset=0):
|
|||
|
|
"""抽取降采样:每 factor 个波段取第 (offset+1) 个波段"""
|
|||
|
|
rows, cols, bands = data.shape
|
|||
|
|
new_bands = bands // factor
|
|||
|
|
indices = [offset + i * factor for i in range(new_bands)]
|
|||
|
|
# 边界保护
|
|||
|
|
indices = [idx for idx in indices if idx < bands]
|
|||
|
|
return data[:, :, indices].astype(np.float32)
|
|||
|
|
|
|||
|
|
def process_bsq_chunked(input_path, output_path, scale=10000, factor=3, offset=0, chunk_lines=500):
|
|||
|
|
"""
|
|||
|
|
分块处理,抽取降采样,正确写入 BSQ 格式
|
|||
|
|
BSQ 格式:每个波段的所有行数据连续存储
|
|||
|
|
"""
|
|||
|
|
img = spectral.open_image(input_path)
|
|||
|
|
hdr = img.metadata.copy()
|
|||
|
|
n_rows, n_cols, n_bands = img.nrows, img.ncols, img.nbands
|
|||
|
|
new_bands = n_bands // factor # 抽取后的波段数
|
|||
|
|
|
|||
|
|
new_hdr = hdr.copy()
|
|||
|
|
new_hdr['samples'] = n_cols
|
|||
|
|
new_hdr['lines'] = n_rows
|
|||
|
|
new_hdr['bands'] = new_bands
|
|||
|
|
new_hdr['data type'] = 12
|
|||
|
|
new_hdr['interleave'] = 'bsq'
|
|||
|
|
|
|||
|
|
# 波长抽取(优先使用原始 header 中的波长)
|
|||
|
|
if 'wavelength' in hdr:
|
|||
|
|
waves = np.array(hdr['wavelength'])
|
|||
|
|
if len(waves) >= new_bands * factor:
|
|||
|
|
extracted_waves = waves[offset::factor][:new_bands]
|
|||
|
|
new_hdr['wavelength'] = extracted_waves.tolist()
|
|||
|
|
else:
|
|||
|
|
print("警告: 原始波长数量不足,跳过")
|
|||
|
|
|
|||
|
|
# FWHM 处理
|
|||
|
|
if 'fwhm' in hdr:
|
|||
|
|
fwhm = np.array(hdr['fwhm'])
|
|||
|
|
if len(fwhm) >= new_bands * factor:
|
|||
|
|
new_hdr['fwhm'] = fwhm[offset::factor][:new_bands].tolist()
|
|||
|
|
|
|||
|
|
out_file = output_path + '.bsq'
|
|||
|
|
|
|||
|
|
# 关键修改:BSQ 格式要求每个波段的所有行连续存储
|
|||
|
|
# 先处理所有数据到内存缓冲区(按波段组织),再写入文件
|
|||
|
|
# 对于大文件,我们按波段分批处理
|
|||
|
|
|
|||
|
|
print(f"开始处理 {n_rows} 行 x {n_cols} 列 x {new_bands} 波段...")
|
|||
|
|
|
|||
|
|
# 为每个波段创建一个临时文件(避免内存溢出)
|
|||
|
|
import tempfile
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
temp_files = []
|
|||
|
|
temp_band_data = []
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 初始化每个波段的临时文件
|
|||
|
|
for b in range(new_bands):
|
|||
|
|
fd, temp_path = tempfile.mkstemp(suffix=f'_band_{b}.tmp')
|
|||
|
|
os.close(fd)
|
|||
|
|
temp_files.append(temp_path)
|
|||
|
|
temp_band_data.append(open(temp_path, 'wb'))
|
|||
|
|
|
|||
|
|
# 第一遍:分块读取,按波段写入临时文件
|
|||
|
|
for start_row in range(0, n_rows, chunk_lines):
|
|||
|
|
end_row = min(start_row + chunk_lines, n_rows)
|
|||
|
|
print(f"处理行 {start_row}-{end_row-1}...")
|
|||
|
|
|
|||
|
|
chunk = img.read_subregion((start_row, end_row), (0, n_cols))
|
|||
|
|
chunk_down = downsample_bands_extract(chunk, factor, offset)
|
|||
|
|
chunk_scaled = chunk_down * scale
|
|||
|
|
chunk_uint16 = np.clip(chunk_scaled, 0, 65535).astype(np.uint16)
|
|||
|
|
|
|||
|
|
# 将每个波段的数据写入对应的临时文件
|
|||
|
|
for b in range(new_bands):
|
|||
|
|
band_data = chunk_uint16[:, :, b].tobytes()
|
|||
|
|
temp_band_data[b].write(band_data)
|
|||
|
|
|
|||
|
|
del chunk, chunk_down, chunk_scaled, chunk_uint16
|
|||
|
|
|
|||
|
|
# 关闭所有临时文件
|
|||
|
|
for f in temp_band_data:
|
|||
|
|
f.close()
|
|||
|
|
|
|||
|
|
# 第二遍:按 BSQ 格式合并(波段0全部数据 → 波段1全部数据 → ...)
|
|||
|
|
print("合并为 BSQ 格式...")
|
|||
|
|
with open(out_file, 'wb') as fout:
|
|||
|
|
for b in range(new_bands):
|
|||
|
|
with open(temp_files[b], 'rb') as fband:
|
|||
|
|
# 读取该波段的全部行数据并写入最终文件
|
|||
|
|
while True:
|
|||
|
|
data = fband.read(1024 * 1024) # 1MB 块读取
|
|||
|
|
if not data:
|
|||
|
|
break
|
|||
|
|
fout.write(data)
|
|||
|
|
print(f" 波段 {b+1}/{new_bands} 写入完成")
|
|||
|
|
|
|||
|
|
print(f"BSQ 文件写入完成: {out_file}")
|
|||
|
|
|
|||
|
|
finally:
|
|||
|
|
# 清理临时文件
|
|||
|
|
for temp_path in temp_files:
|
|||
|
|
try:
|
|||
|
|
os.remove(temp_path)
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
# 写入头文件
|
|||
|
|
envi.write_envi_header(output_path + '.hdr', new_hdr)
|
|||
|
|
print(f"完成!输出: {out_file}")
|
|||
|
|
print(f" 尺寸: {n_rows} 行 x {n_cols} 列 x {new_bands} 波段")
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
input_base = r"D:\BaiduNetdiskDownload\yaobao\caijain.hdr"
|
|||
|
|
output_base = r"D:\BaiduNetdiskDownload\yaobao\test"
|
|||
|
|
process_bsq_chunked(input_base, output_base, scale=10000, factor=3, offset=0, chunk_lines=2000)
|