ct_load.exe

  href="../../../../style.css" type="text/css" rel="stylesheet" />  


进程知识库

ct_load - ct_load.exe - 进程信息 name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-5572165936844014&dt=1195292630218&lmt=1195292627&format=336x280_as&output=html&correlator=1195292630218&url=file%3A%2F%2F%2FC%3A%2FDocuments%2520and%2520Settings%2Flhh1%2F%E6%A1%8C%E9%9D%A2%2F000stthk.exe.htm&color_bg=FFFFFF&color_text=000000&color_link=000000&color_url=FFFFFF&color_border=FFFFFF&ad_type=text&ga_vid=1397507768.1195292630&ga_sid=1195292630&ga_hid=1390572954&flash=9&u_h=768&u_w=1024&u_ah=740&u_aw=1024&u_cd=32&u_tz=480&u_java=true" frameborder="0" width="336" scrolling="no" height="280" allowtransparency="allowtransparency">

进程文件: ct_load 或者 ct_load.exe
进程名称: CyDoor Spyware
 
描述:
ct_load.exe是CyDoor间谍软件相关程序。该软件会弹出广告,并回传计算机使用习惯用于分析。

出品者: CyDoor advertising
属于: CyDoor advertising spyware

系统进程:
使用网络:
使用网络:

硬件相关:
常见错误: 未知N/A

内存使用: 未知N/A
 
安全等级 (0-5): 2

间谍软件:
广告软件:
病毒:
木马:

其它进程列表

 Other Processes  
 00thotkey.exe 12popup.exe 2portalmon.exe
 3capplnk.exe 3cdminic.exe 3cmcnkw.exe
 3cmlink.exe 3cmlinkw.exe 3cmlnkw.exe
 3deepctl.exe 3dfxman.exe 3dldemon.exe
 3dlman.exe 3dxsrv.exe 3qdctl.exe
 a4proxy.exe absr.exe abyssws.exe
 acbtnmgr_xxx.exe accelerate.exe access.exe
 acmonitor_xxx.exe acombo3d.exe aconti.exe
 acoustic.exe acroaum.exe acrobat elements.exe
 acrodist.exe act.exe actionagent.exe
 activation.exe activeeyes.exe activemenu.exe
 activeplus.exe ad-aware.exe ad-watch.exe
 ad2kclient.exe adblck.exe adc.exe
 addestroyer.exe addrbook.exe adg.exe
 AdManCtl.exe admillikeep.exe admilliserv.exe
 admlib32.exe admunch.exe Adobe Gamma Loader.exe
 adobes.exe adp.exe adsgone.exe
 adss.exe adstatserv.exe adsub.exe
 adtray.exe advapi.exe advchk.exe
 aeiwlsta.exe aelaunch.exe aexswdusr.exe
 agfaclnk.exe agsatellite.exe ahfp.exe
 ahnsd.exe ahqinit.exe ahqtb.exe
 aiepk.exe aiepk2.exe aimaol.exe
 aimingclick.exe airplus.exe airsvcu.exe
 akiller.exe alarm.app.exe AlarmApp.exe
 alarmwatcher.exe ALCMTR.EXE ALCWZRD.EXE
 alcxmntr.exe aldaemon.exe alertserver.exe
 AlertSvc.exe almxptray.exe ALUNotify.exe
 am32.exe amoumain.exe anote.exe
 AOLServiceHost.exe  aoltpspd.exe aoltray.exe
 aoltsmon.exe aornum.exe ap9h4qmo.exe
 Apache.exe appservices.exe aptezbp.exe
 apvxdwin.exe AquariumDesktop.exe armon32.exe
 armon32a.exe arupld32.exe ashDisp.exe
 ashmaisv.exe ashserv.exe asmproserver.exe
 aspi_me.exe  aspnet_admin.exe asupport.exe
 aswupdsv.exe athoc.exe ati2mdxx.exe
 ati2plab.exe aticwd32.exe ATIDtct.EXE
 atitask.exe atwtusb.exe ausvc.exe
 autoreg.exe autorun.exe autoupdate.exe
 avgamsvr.exe avgcc.exe avgcc32.exe
 avgemc.exe avgnt.exe avguard.exe
 avpm.exe avsched32.exe avwupsrv.exe
 awhost32.exe awrem32.exe babylon.exe
 BackItUp.exe backupnotify.exe bagent.exe
 bass.exe bboy.exe bcb.exe
 bcmwltry.exe bdmcon.exe bdss.exe
 bearshare.exe benetns.exe bengine.exe
 benser.exe beremote.exe beserver.exe
 bigfix.exe billmind.exe bkupexec.exe
 blocker.exe bmupdate.exe boot.exe
 bp.exe bpftp.exe bpk.exe
 bpsinstall.exe brad32.exe brmecom.exe
 bsoft.exe btstackserver.exe bttray.exe
 bwgo0000 cagent.exe cagent32.exe
 calcheck.exe CAP2LAK.EXE CAP2RSK.EXE
 CAP2SWK.EXE capfax.exe cc.exe
 cclaw.exe ccmagent.exe cconnect.exe
 ccsetmgr.exe cd_load.exe cdf.exe
 cdspeed.exe ceekey.exe cepmtray.exe
 cgtask.exe chkadmin.exe chkras.exe
 choke.exe cij3p2ps.exe client.exe
 clipsrv.exe clonecdtray.exe cmgrdian.exe
 cmluc.exe cnqmax.exe CnxMon.exe
 comctl_32.exe comet.exe command.exe
 compaq-rba.exe conf.exe config.exe
 conmgr.exe ConquerCam.exe consol32.exe
 cool.exe CoverDesigner.exe cpdclnt.exe
 cpqa1000.exe cpqdfwag.exe cpqeadm.exe
 cpqinet.exe Cpqset.exe cpunumber.exe
 createcd.exe createcd50.exe crsss.exe
 csinject.exe csinsm32.exe csinsmnt.exe
 Csrrs.exe csrsc.exe csrss32.exe
 ct_load.exe ctbclick.exe ctdvddet.exe
 cteaxspl.exe ctfmon32.exe ctrlvol.exe
 ctsrreg.exe ctsysvol.exe cusrvc.exe
 cuteftp.exe cutftp.exe cyb2k.exe
 cygrunsrv.exe cz.exe d4.exe
 daconfig.exe daemon.exe datalayer.exe
 ddhelper32.exe de_serv.exe defscangui.exe
 delldmi.exe dellmmkb.exe delmsbb.exe
 desk98.exe DeskAdKeep.exe DeskAdServ.exe
 dexplore.exe diagent.exe dialer.exe
 directx.exe directxset.exe dla.exe
 dlgli.exe dlt.exe dluca.exe
 dmremote.exe dmxlauncher.exe dnar.exe
 dnetc.exe dns.exe download.exe
 downloadplus.exe dragdrop.exe dreamweaver.exe
 drgtodsc.exe drivespeed.exe drvddll.exe
 drvlsnr.exe DRWTSN16.EXE dsagnt.exe
 dseraser.exe dslagent.exe dslmon.exe
 dsnthapp.exe dsnthser.exe dvdlauncher.exe
 DVDRegionFree.exe dvldr32.exe dvremind.exe
 DWHeartbeatMonitor.exe DxDebugService.exe DXEnum.exe
 dxnf.exe e-s0bic1.exe e_s0hic1.exe
 e_srcv03.exe eabservr.exe EasyAV.exe
 ebrr.exe edisk.exe edonkey.exe
 ee.exe ehmsas.exe ehrec.exe
 ehSched.exe ehshell.exe ehtray.exe
 elbycheck.exe elccest.exe emule.exe
 enbiei.exe encmontr.exe engutil.exe
 ensmix32.exe enternet.exe essdc.exe
 eudora.exe eusexe.exe EvtEng.exe
 expl32.exe explorer32.exe explorere.exe
 express.exe exshow95.exe ezejmnap.exe
 ezulumain.exe fameh32.exe fan.exe
 farmmext.exe fastdown.exe faxsvc.exe
 fbdirect.exe fc.exe fch32.exe
 fgadmin.exe fih32.exe finder.exe
 flashfxp.exe flashksk.exe flatbed.exe
 fnrb32.exe FONTVIEW.EXE forte.exe
 fpdisp4.exe fpxpress.exe frameworkservic.exe
 freedom.exe frontpage.exe frsk.exe
 fs20.exe fsaa.exe fsav32.exe
 fsbwlan.exe fsdfwd.exe fsg.exe
 fsg_3202.exe fsgk32.exe fsgk32st.exe
 fsm32.exe fsma32.exe fsmb32.exe
 fsscrctl.exe fssm32.exe fsw.exe
 ftpte.exe fts.exe fwenc.exe
 fxredir.exe gah95on6.exe gain_trickler_3202.exe
 gbtray.exe gcASCleaner.exe gcasDtServ.exe
 gcasInstallHelper.exe gcASNotice.exe gcasServ.exe
 gcasServAlert.exe gcasSWUpdater.exe gdonkey.exe
 gesfm32.exe gfxacc.exe Ghostexp.exe
 GHOSTS~2.EXE ghoststartservice.exe ghoststarttrayapp.exe
 giantantispywaremain.exe GIANTAntiSpywareUpdater.exe gnetmous.exe
 gnotify.exe go.exe GoogleDesktop.exe
 gozilla.exe gra.exe graph.exe
 GrpWise.exe gsicon.exe gstartup.exe
 gtwatch.exe gwmdmpi.exe gwsystemservice.exe
 hcontrol.exe helpexp.exe HelpHost.exe
 helpsvc.exe hhw.exe hidden32.exe
 hjym.exe hkserv.exe hkss.exe
 hkwnd.exe hotkeyapp.exe hotsync.exe
 hottray.exe hpbpro.exe hpdrv.exe
 hphmon03.exe hphmon04.exe hphmon06.exe
 hphupd04.exe hphupd05.exe hphupd06.exe
 hpnra.exe hpobnz08.exe hpoddt01.exe
 hpodev07.exe hpoevm07.exe hpohmr08.exe
 hpoopm07.exe hposol08.exe hpqcmon.exe
 hpqgalry.exe hpsjvxd.exe hpwuschd.exe
 HPWuSchd2.exe hpzstatn.exe hpztsb03.exe
 hpztsb09.exe hpztsb10.exe htmdeng.exe
 hypertrm.exe i8kfangui.exe iaanotif.exe
 iaantmon.exe ibmpmsvc.exe iconfig.exe
 icqlite.exe icsmgr.exe icwconn2.exe
 icwtutor.exe iexpiore.exe iexplore32.exe
 iFrmewrk.exe  igfxsrvc.dll ImageDrive.exe
 IMApp.exe IMEKRMIG.EXE imjpmig.exe
 imonnt.exe imontray.exe imscinst.exe
 incd.exe InCDsrv.exe IncMail.exe
 incredimail.exe inetd32.exe InfoTool.exe
 inicio.exe initsdk.exe inotask.exe
 IntelMEM.exe internet.exe ipclient.exe
 ipssvc.exe ireike.exe isignup.exe
 islp2sta.exe ismserv.exe isstart.exe
 itouch.exe iwctrl.exe ixapplet.exe
 JAMMER2ND.EXE javaws.exe jetcar.exe
 jucheck.exe jushed.exe jushed32.exe
 kavsvc.exe kazaalite.exe KB891711.EXE
 kencapi.exe kencli.exe kencron.exe
 kendns.exe kenftpgw.exe keninet.exe
 kenmail.exe kenmap.exe kenproxy.exe
 kenserv.exe kensocks.exe kentbcli.exe
 kernal32.exe keyhook.exe keylogger.exe
 keyword.exe KHALMNPR.exe khooker.exe
 kmw_run.exe kodakccs.exe kodakimage.exe
 kodakprv.exe kodorjan.exe kpf4gui.exe
 lao.exe launch.exe launchap.exe
 launcher.exe launchpd.exe leerlaufprozess
 lexplore.exe lexstart.exe lights.exe
 lmgrd.exe lmpdpsrv.exe load32.exe
 logitray.exe logon.exe lorena.exe
 LSAS.exe Lsass32.exe lsassa.exe
 lsasss.exe lsserv.exe ltcm000c.exe
 ltdmgr.exe ltmoh.exe ltmsg.exe
 lxdboxcp.exe main.exe mainserv.exe
 manager.exe mapiicon.exe master.exe
 matcli.exe mathchk.exe mbm4.exe
 mbm5.exe mc.exe mcagent.exe
 mcappins.exe mcdlc.exe McEPOC.exe
 McEPOCfg.exe mcinfo.exe mcmnhdlr.exe
 mcpalmcfg.exe mcpserver.exe mcupdate.exe
 mcvsshld.exe McWCE.exe McWCECfg.exe
 mediaaccess.exe MediaAccK.exe mediaman.exe
 mediapass.exe mediapassk.exe members-area.exe
 memorymeter.exe messenger.exe mgactrl.exe
 mgaqdesk.exe mgasc.exe mgavrtcl.exe
 mgui.exe mhotkey.exe microsoft.exe
 mim.exe minibug.exe minilog.exe
 mirc.exe MIRC32.exe mm_server.exe
 mmdiag.exe mmtray.exe mmtray2k.exe
 mmtraylsi.exe mmups.exe mmusbkb2.exe
 mnsvc.exe mnyexpr.exe monitor.exe
 monitr32.exe morpheus.exe moviemk.exe
 movieplace.exe Mozilla.exe mp3serch.exe
 mpbtn.exe mpf.exe mpfagent.exe
 mpfservice.exe mpftray.exe mpservic.exe
 mpsetup.exe mqtgsvc.exe msaccess.exe
 msams.exe msc32.exe mscifapp.exe
 mscnsz.exe mscommand.exe msconfig32.exe
 mscvb32.exe MSD.EXE mse7.exe
 msg32.exe msgloop.exe msgplus.exe
 mskagent.exe msmgs.exe msndc.exe
 MSNIASVC.EXE msoffice.exe mspmspv.exe
 mspub.exe msqry32.exe msscli.exe
 mssearch.exe msstat.exe mssvr.exe
 mstore.exe MSupdate.exe msvcmm32.exe
 mtx.exe muamgr.exe musirc4.71.exe
 mwd.exe mxoaldr.exe mxtask.exe
 myfastupdate.exe mysqld-nt.exe nail.exe
 naimag32.exe navapp.exe nbj.exe
 nbr.exe nclaunch.exe nddeagnt.exe
 NDSTray.exe neo.exe neoCapture.exe
 neoCopy.exe neoDVD.exe neoDVDstd.exe
 neotrace.exe nero.exe nerosmartstart.exe
 nerosvc.exe netmail.exe  netsurf.exe
 newdot.exe newsupd.exe ngctw32.exe
 nilaunch.exe NIP.exe nipsvc.exe
 NJeeves.exe nkvmon.exe noads.exe
 notify.exe npfmntor.exe NPFMSG.exe
 npscheck.exe npssvc.exe NRMENCTB.exe
 nscheck.exe nsl.exe NSMdtr.exe
 NSMdtr.exe nsvr.exe nsvsvc.exe
 nt_usdm.exe ntaskldr.exe ntfrs.exe
 ntmulti.exe ntrtscan.exe NVCOAS.exe
 NVCPL.EXE NVCSched.exe nvmctray
 nvsvc.exe nwtray.exe Nymse.exe
 ocxdll.exe oeloader.exe ois.exe
 Olehelp.exe Omniserv.exe onetouch.exe
 oodag.exe opera.exe opware12.exe
 owmngr.exe P2P Networking2.exe P2P Networking3.exe
 pacis.exe PACKAGER.EXE packethsvc.exe
 pav.exe pavfires.exe pavsrv51.exe
 pcard.exe pccclient.exe pccguide.exe
 pccnt.exe pccntmon.exe pccntupd.exe
 PcCtlCom.exe pcfmgr.exe pchbutton.exe
 pcscan.exe PCTVoice.exe pdsched.exe
 PDVDServ.exe persfw.exe pg_ctl.exe
 pgptray.exe phbase.exe photoshop.exe
 picsvr.exe pinball.exe pkjobs.exe
 plauto.exe player.exe plguni.exe
 pmr.exe pmxinit.exe pop3pack.exe
 popupkiller.exe postgres.exe postmaster.exe
 pow.exe powerdvd.exe powerkey.exe
 powerpnt.exe powers.exe ppmemcheck.exe
 pptd40nt.exe pptview.exe ppwebcap.exe
 pqhelper.exe PQIBrowser.exe PQIMountSvc.exe
 pqinit.exe pqtray.exe PQV2ISECURITY.EXE
 pqv2isvc.exe printnow.exe PRISMSTA.EXE
 PRISMSVR.EXE profiler.exe proflwiz.exe
 pruttct.exe psdrvcheck.exe psimsvc.exe
 pssvc.exe pts.exe ptssvc.exe
 pull.exe PureVoice.exe pvlsvr.exe
 qbdagent2002.exe qbupdate.exe qclean.exe
 qconsvc.exe qctray.exe qcwlicon.exe
 qdcsfs.exe Qoeloader.exe qtaet2s.exe
 qtask.exe qtzgacer.exe QuickBooks
 quickdcf.exe quickres.exe quicktimeplayer.exe
 qvp32.exe Radio.exe RadioSvr.EXE
 randomdigits.exe rapapp.exe rasman.exe
 RAVMOND.exe rcapi.exe rds.exe
 reader_sl.exe realjbox.exe realmon.exe
 realpopup.exe realshed.exe register.exe
 regloadr.exe regsrv.exe RegSrvc.exe
 regsvc32.exe remind.exe Remind_XP.exe
 remind32.exe reminder.exe removed.exe
 remupd.exe retrorun.exe rftray.exe
 rlid.exe RM_SV.exe  rosnmgr.exe
 rsrcmtr.exe rtlrack.exe rtmanager.exe
 rtmc.exe rtmservice.exe rtos.exe
 rtvscn95.exe runservice.exe s24evmon.exe
 s3tray2.exe sage.exe saimon.exe
 saproxy.exe sbdrvdet.exe sbserv.exe
 sbsetup.exe scanexplicit.exe ScanMailOutLook.exe
 scanregistry.exe scanserver.exe scards32.exe
 scardsvr32.exe scbar.exe sccenter.exe
 scchost.exe SCHDPL32.EXE schedhlp.exe
 schedul2.exe scheduler.exe schedulerv2.exe
 schost.exe schupd.exe scm.exe
 scrfs.exe scsiaccess.exe sdii.exe
 sdstat.exe se.exe searchnav.exe
 searchnavversion.exe sentstrt.exe service5.exe
 sessmgr.exe sethook.exe seti@home.exe
 setlang.exe sgbhp.exe sgmain.exe
 shadowbar.exe sharedprem.exe shell32.exe
 shine.exe shpc32.exe shstart.exe
 shwicon.exe silent.exe SIMETER.EXE
 sistray.exe sisusbrg.exe sixtypopsix.exe
 ska.exe skynetave.exe Skype.exe
 slee401.exe sllights.exe slpv24s.exe
 slserv.exe sm56hlpr.exe smagent.exe
 smartagt.exe SmartExplorer.exe SmartFTP.exe
 SMax4.exe SMax4PNP.exe SMceMan.exe
 smlogsvc.exe smOutlookPack.exe sms.exe
 smsmon32.exe smsss.exe smsx.exe
 smtray.exe sndrec32.exe sniffer.exe
 snmptrap.exe soffice.exe sointgr.exe
 SonicStageMonitoring.exe soundtrax.exe spamsub.exe
 SpamSubtract.exe SPBBCSvc.exe speedmgr.exe
 speedupmypc.exe splash.exe spool.exe
 spoolsrv.exe SpoolSvc.exe sptip.dll
 spvic.exe spyagent4.exe spyblast.exe
 spybotsd.exe spybuddy.exe spysweeper.exe
 spyware.exe sqlagent.exe sqlmangr.exe
 sqlservr.exe srv32.exe ss.exe
 ssgrate.exe sshd.exe ssonsvr.exe
 sstray.exe Stacmon.exe StatusClient.exe
 supporter5.exe surveyor.exe suss.exe
 svaplayer.exe svchoost.exe svchos1.exe
 svchosl.exe svcinit.exe svcproc.exe
 svhost.exe SwiftBTN.exe swimsuitnetwork.exe
 sws.exe sxgdsenu.exe symlcsvc.exe
 SymSPort.exe synchost.exe sysagent.exe
 syscfg32.exe syscnfg.exe sysformat.exe
 syshost.exe syslog.exe sysmon.exe
 sysmonnt.exe sysreg.exe SYSsfitb.exe
 systask32l.exe systemdll.exe systime.exe
 systray32.exe Szchost.exe tapicfg.exe
 task32.exe taskbar.exe taskpanl.exe
 taskswitch.exe tbctray.exe TBMon.exe
 tbpanel.exe tc.exe termsrv.exe
 testing.exe tfncky.exe tfnf5.exe
 The Weather Channel.exe thotkey.exe timershot.exe
 timeup.exe tintsetp.exe tmksrvi.exe
 tmksrvu.exe tmpfw.exe toadimon.exe
 topdesk.exe toscdspd.exe TouchED.exe
 tp4ex.exe tp4mon.exe tp4serv.exe
 tphkmgr.exe TPONSCR.exe TpScrex.exe
 tpsmain.exe TPTray.exe tpwrtray.exe
 tray.exe trayclnt.exe traymon.exe
 traymonitor.exe traysaver.exe trayserver.exe
 tskdbg.exe tskmgr32.exe tsl2.exe
 tsp2.exe tstool.exe tv_media.exe
 twunk_64.exe uc.exe udserve.exe
 unins000.exe unsecapp.exe upd.exe
 upgrade.exe ups.exe usb.exe
 usbmmkbd.exe usbmonit.exe usrmlnka.exe
 utilman.exe v2iconsole.exe vaserv.exe
 vettray.exe vi_grm.exe videodrv.exe
 view.exe viewport.exe virtualbouncer.exe
 visio.exe vmnat.exe vmss.exe
 vobregcheck.exe vsmain.exe vsserv.exe
 vssvc.exe VzFw.exe w3dbsmgr.exe
 watch.exe watchdog.exe WaveEdit.exe
 wbload.exe wbsched.exe wbss.exe
 wbutton.exe wcesmgr.exe weather.exe
 webcamrt.exe webcolct.exe webinstall.exe
 webtrapnt.exe welcome.exe wfxctl32.exe
 wfxsnt40.exe wfxswtch.exe whagent.exe
 whSurvey.exe win32api.exe WinAce.exe
 winadm.exe winadserv.exe winadslave.exe
 Winaw32.exe winbackup.exe WinCinemaMgr.exe
 wincomm.exe wincomp.exe winctlad.exe
 WinCtlAdAlt.exe winde.exe windefault.exe
 winex.exe winfs.exe wingate.exe
 winhelp.exe winhlp32.exe winhost.exe
 wininfo.exe winkey.exe winlog.exe
 winmgm32.exe winmysqladmin.exe Winpack.exe
 winproj.exe winproxy.exe winpsd.exe
 winpup32.exe winrecon.exe winroute.exe
 wins.exe winserv.exe winservad.exe
 winservices.exe winservs.exe winservsuit.exe
 winsocks.exe winsrv32.exe winstat.exe
 winsys.exe winsys32.exe wintask.exe
 wintasks.exe winvnc.exe winwan.exe
 wiseupdt.exe wkdetect.exe wkfud.exe
 wkqkpick.exe wkscal.exe wkssb.exe
 wlansta.exe wmburn.exe wmencagt.exe
 wmexe.exe wmiapsrv.exe WMPBurn.exe
 wntsf.exe workflow.exe wp.exe
 wptel.exe wsys.exe wuloader.exe
 wupdated.exe ww.exe x10nets.exe
 x1exec.exe xcommsvr.exe xferwan.exe
 xtcfgloader.exe ymsgr_tray.exe ydownloader.exe
 YServer.exe yupdater.exe Zanda.exe
 zbase32.exe zcast.exe zClientm.exe
 ZLH.exe ZStatus.exe


Other Process Categories:
- 系统进程列表
- 存在安全风险进程列表
- 应用程序进程列表

 

 

 

©Copyright 2000-2005 小土豆。版权所有。
  

import os import sys import numpy as np import pydicom import dicom_numpy import vtk from vtk.util import numpy_support from PyQt5.QtWidgets import ( QApplication, QMainWindow, QFileDialog, QVBoxLayout, QHBoxLayout, QWidget, QSlider, QLabel, QPushButton, QMessageBox, QProgressDialog ) from PyQt5.QtCore import Qt, QThread, pyqtSignal import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor def fix_qt_plugin_path(): """解决 Qt 平台插件无法初始化的问题""" try: from PyQt5.QtCore import QLibraryInfo plugin_path = QLibraryInfo.location(QLibraryInfo.PluginsPath) if os.path.exists(plugin_path): os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path return except ImportError: pass paths_to_try = [ os.path.join(sys.prefix, 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins'), os.path.join(sys.prefix, 'Library', 'plugins'), os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Qt', 'plugins') ] for path in paths_to_try: if os.path.exists(path): os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = path break fix_qt_plugin_path() class DICOMLoader(QThread): progress_updated = pyqtSignal(int) loading_complete = pyqtSignal(object, object, object, object) # volume_array, spacing, origin, vtk_image loading_failed = pyqtSignal(str) def __init__(self, directory): super().__init__() self.directory = directory def run(self): try: # 获取所有DICOM文件 dicom_files = self.get_all_dicom_files(self.directory) if not dicom_files: self.loading_failed.emit("未找到DICOM文件") return # 读取并分组DICOM文件 series_dict = self.read_and_group_dicom_files(dicom_files) if not series_dict: self.loading_failed.emit("没有有效的DICOM图像") return # 选择第一个系列进行处理 series_uid = next(iter(series_dict)) datasets = series_dict[series_uid] # 处理DICOM数据集 volume_array, spacing, origin = self.process_dicom_datasets(datasets) # 转换为VTK图像 vtk_image = self.numpy_to_vtk(volume_array, spacing, origin) self.loading_complete.emit(volume_array, spacing, origin, vtk_image) except Exception as e: self.loading_failed.emit(f"加载DICOM文件失败: {str(e)}") def get_all_dicom_files(self, directory): """获取目录下所有DICOM文件""" dicom_files = [] for root, _, files in os.walk(directory): for file in files: if file.lower().endswith(('.dcm', '.dicm', '.dicom')): dicom_files.append(os.path.join(root, file)) return dicom_files def read_and_group_dicom_files(self, file_paths): """读取并分组DICOM文件""" series_dict = {} for i, file_path in enumerate(file_paths): try: ds = pydicom.dcmread(file_path) # 检查是否包含像素数据 if not hasattr(ds, 'pixel_array'): continue # 检查必要的定位信息 required_tags = ['ImagePositionPatient', 'ImageOrientationPatient', 'PixelSpacing'] if not all(hasattr(ds, tag) for tag in required_tags): continue # 按系列实例UID分组 series_uid = ds.SeriesInstanceUID if series_uid not in series_dict: series_dict[series_uid] = [] series_dict[series_uid].append(ds) # 更新进度 self.progress_updated.emit(int((i + 1) / len(file_paths) * 100)) except Exception as e: print(f"无法读取文件 {file_path}: {str(e)}") continue # 对每个系列按切片位置排序 for series_uid in series_dict: try: series_dict[series_uid].sort(key=lambda ds: float(ds.ImagePositionPatient[2])) except: pass # 如果排序失败,保持原顺序 return series_dict def process_dicom_datasets(self, datasets): """处理DICOM数据集并返回体积数据""" try: # 使用dicom-numpy组合体积数据 volume_array, ijk_to_xyz = dicom_numpy.combine_slices(datasets) # 获取间距和原点 spacing = np.array([ np.linalg.norm(ijk_to_xyz[:3, 0]), # X spacing np.linalg.norm(ijk_to_xyz[:3, 1]), # Y spacing np.linalg.norm(ijk_to_xyz[:3, 2]) # Z spacing ]) origin = ijk_to_xyz[:3, 3] # 调整数组方向以匹配VTK坐标系 volume_array = np.transpose(volume_array, (2, 1, 0)) return volume_array, spacing, origin except dicom_numpy.DicomImportException as e: raise Exception(f"DICOM导入错误: {str(e)}") except Exception as e: raise Exception(f"处理DICOM数据时出错: {str(e)}") def numpy_to_vtk(self, volume_array, spacing, origin): """将numpy数组转换为VTK图像""" # 确保数组是连续的 volume_array = np.ascontiguousarray(volume_array) # 根据数据类型选择合适的VTK类型 if volume_array.dtype == np.uint8: vtk_type = vtk.VTK_UNSIGNED_CHAR elif volume_array.dtype == np.int16: vtk_type = vtk.VTK_SHORT elif volume_array.dtype == np.uint16: vtk_type = vtk.VTK_UNSIGNED_SHORT elif volume_array.dtype == np.float32: vtk_type = vtk.VTK_FLOAT else: # 不支持的格式转换为float32 volume_array = volume_array.astype(np.float32) vtk_type = vtk.VTK_FLOAT # 转换为VTK数组 vtk_data = numpy_support.numpy_to_vtk( volume_array.ravel(), deep=True, array_type=vtk_type ) # 创建VTK图像 image = vtk.vtkImageData() image.SetDimensions(volume_array.shape) image.SetSpacing(spacing) image.SetOrigin(origin) image.GetPointData().SetScalars(vtk_data) return image class MedicalViewer(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("DICOM 三维可视化工具") self.setGeometry(100, 100, 1200, 800) self.current_path_points = [] self.path_planning_mode = False self.slice_views = {} self.init_ui() def init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QHBoxLayout(central_widget) # 左侧控制面板 control_panel = QWidget() control_layout = QVBoxLayout(control_panel) control_layout.setContentsMargins(5, 5, 5, 5) self.load_button = QPushButton("加载 DICOM 文件夹") self.load_button.clicked.connect(self.load_dicom) control_layout.addWidget(self.load_button) # 切片控制滑块 self.axial_slider = self.create_slice_control("轴向切片:") self.coronal_slider = self.create_slice_control("冠状切片:") self.sagittal_slider = self.create_slice_control("矢状切片:") control_layout.addWidget(self.axial_slider['container']) control_layout.addWidget(self.coronal_slider['container']) control_layout.addWidget(self.sagittal_slider['container']) # 窗宽窗位控制 self.ww_slider = self.create_window_control("窗宽:") self.wl_slider = self.create_window_control("窗位:") control_layout.addWidget(self.ww_slider['container']) control_layout.addWidget(self.wl_slider['container']) # 等值面阈值控制 self.threshold_slider = self.create_threshold_control("等值面阈值:") control_layout.addWidget(self.threshold_slider['container']) control_layout.addStretch() # 路径规划按钮 self.path_button = QPushButton("开始路径规划") self.path_button.clicked.connect(self.toggle_path_planning) self.path_button.setEnabled(False) control_layout.addWidget(self.path_button) self.clear_path_button = QPushButton("清除路径") self.clear_path_button.clicked.connect(self.clear_path) self.clear_path_button.setEnabled(False) control_layout.addWidget(self.clear_path_button) # 导出按钮 self.export_mesh_button = QPushButton("导出网格为 DAE") self.export_mesh_button.clicked.connect(self.export_mesh_to_dae) self.export_mesh_button.setEnabled(False) control_layout.addWidget(self.export_mesh_button) self.export_path_button = QPushButton("导出路径为 DAE") self.export_path_button.clicked.connect(self.export_path_to_dae) self.export_path_button.setEnabled(False) control_layout.addWidget(self.export_path_button) # 右侧显示区域 display_panel = QWidget() display_layout = QVBoxLayout(display_panel) # 2D 切片显示 self.figure, self.axes = plt.subplots(1, 3, figsize=(12, 4)) self.figure.subplots_adjust(left=0.02, right=0.98, bottom=0.02, top=0.95, wspace=0.05, hspace=0) self.canvas = FigureCanvas(self.figure) display_layout.addWidget(self.canvas) # 初始化2D视图 self.slice_views["axial"] = { "axis": self.axes[0], "slider": self.axial_slider['slider'] } self.slice_views["coronal"] = { "axis": self.axes[1], "slider": self.coronal_slider['slider'] } self.slice_views["sagittal"] = { "axis": self.axes[2], "slider": self.sagittal_slider['slider'] } # 连接信号 for view in self.slice_views.values(): view["axis"].axis("off") view["image"] = None view["slider"].valueChanged.connect(self.update_slice_views) # 3D VTK 渲染窗口 self.vtk_widget = QVTKRenderWindowInteractor() display_layout.addWidget(self.vtk_widget) main_layout.addWidget(control_panel, stretch=1) main_layout.addWidget(display_panel, stretch=4) def create_slice_control(self, label_text): """创建切片控制滑块""" container = QWidget() layout = QVBoxLayout(container) layout.setContentsMargins(0, 0, 0, 0) label = QLabel(label_text) layout.addWidget(label) slider = QSlider(Qt.Horizontal) slider.setEnabled(False) layout.addWidget(slider) return {'container': container, 'slider': slider} def create_window_control(self, label_text): """创建窗宽窗位控制滑块""" container = QWidget() layout = QVBoxLayout(container) layout.setContentsMargins(0, 0, 0, 0) label = QLabel(label_text) layout.addWidget(label) slider = QSlider(Qt.Horizontal) slider.setRange(0, 4000) slider.setValue(2000) slider.setEnabled(False) slider.valueChanged.connect(self.apply_window_level) layout.addWidget(slider) return {'container': container, 'slider': slider} def create_threshold_control(self, label_text): """创建等值面阈值控制滑块""" container = QWidget() layout = QVBoxLayout(container) layout.setContentsMargins(0, 0, 0, 0) label = QLabel(label_text) layout.addWidget(label) slider = QSlider(Qt.Horizontal) slider.setRange(-1000, 1000) slider.setValue(500) slider.setEnabled(False) slider.valueChanged.connect(self.update_3d_renderer) layout.addWidget(slider) return {'container': container, 'slider': slider} def load_dicom(self): """加载DICOM文件夹""" directory = QFileDialog.getExistingDirectory(self, "选择 DICOM 文件夹") if not directory: return # 创建进度对话框 progress_dialog = QProgressDialog("正在加载DICOM文件...", "取消", 0, 100, self) progress_dialog.setWindowTitle("加载中") progress_dialog.setWindowModality(Qt.WindowModal) progress_dialog.setAutoClose(True) # 创建并启动加载线程 self.loader = DICOMLoader(directory) self.loader.progress_updated.connect(progress_dialog.setValue) self.loader.loading_complete.connect(self.on_dicom_loaded) self.loader.loading_failed.connect(lambda msg: ( progress_dialog.cancel(), QMessageBox.critical(self, "错误", msg) )) self.loader.finished.connect(progress_dialog.deleteLater) self.loader.start() def on_dicom_loaded(self, volume_array, spacing, origin, vtk_image): """DICOM加载完成后的处理""" self.volume_array = volume_array self.vtk_image = vtk_image self.spacing = spacing self.origin = origin # 设置滑块范围 self.axial_slider['slider'].setRange(0, volume_array.shape[0] - 1) self.coronal_slider['slider'].setRange(0, volume_array.shape[1] - 1) self.sagittal_slider['slider'].setRange(0, volume_array.shape[2] - 1) # 启用滑块 self.axial_slider['slider'].setEnabled(True) self.coronal_slider['slider'].setEnabled(True) self.sagittal_slider['slider'].setEnabled(True) self.threshold_slider['slider'].setEnabled(True) # 设置初始位置 self.axial_slider['slider'].setValue(volume_array.shape[0] // 2) self.coronal_slider['slider'].setValue(volume_array.shape[1] // 2) self.sagittal_slider['slider'].setValue(volume_array.shape[2] // 2) # 启用窗宽窗位控制 self.ww_slider['slider'].setEnabled(True) self.wl_slider['slider'].setEnabled(True) # 初始化3D视图 self.setup_3d_renderer() # 更新2D视图 self.update_slice_views() # 启用其他按钮 self.path_button.setEnabled(True) self.export_mesh_button.setEnabled(True) def setup_3d_renderer(self): """初始化3D渲染器""" self.renderer = vtk.vtkRenderer() self.vtk_widget.GetRenderWindow().AddRenderer(self.renderer) self.interactor = self.vtk_widget.GetRenderWindow().GetInteractor() # 初始3D重建 self.update_3d_renderer() # 设置背景和相机 self.renderer.SetBackground(0.2, 0.3, 0.4) self.renderer.ResetCamera() # 添加光源 light1 = vtk.vtkLight() light1.SetPosition(0, 0, 1) light1.SetFocalPoint(self.renderer.GetActiveCamera().GetFocalPoint()) self.renderer.AddLight(light1) light2 = vtk.vtkLight() light2.SetPosition(0, 1, 0) light2.SetFocalPoint(self.renderer.GetActiveCamera().GetFocalPoint()) self.renderer.AddLight(light2) # 初始化交互器 self.interactor.Initialize() self.interactor.Start() def update_3d_renderer(self): """更新3D重建""" if not hasattr(self, "vtk_image"): return # 移除旧的actor if hasattr(self, "mesh_actor"): self.renderer.RemoveActor(self.mesh_actor) # 获取当前阈值 threshold = self.threshold_slider['slider'].value() # Marching Cubes表面重建 marching_cubes = vtk.vtkMarchingCubes() marching_cubes.SetInputData(self.vtk_image) marching_cubes.SetValue(0, threshold) # 平滑滤波器 smoother = vtk.vtkWindowedSincPolyDataFilter() smoother.SetInputConnection(marching_cubes.GetOutputPort()) smoother.SetNumberOfIterations(20) smoother.BoundarySmoothingOn() smoother.FeatureEdgeSmoothingOff() smoother.SetPassBand(0.1) smoother.NonManifoldSmoothingOn() smoother.NormalizeCoordinatesOn() smoother.Update() # 创建mapper和actor mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(smoother.GetOutputPort()) mapper.ScalarVisibilityOff() self.mesh_actor = vtk.vtkActor() self.mesh_actor.SetMapper(mapper) self.mesh_actor.GetProperty().SetColor(0.9, 0.75, 0.6) self.mesh_actor.GetProperty().SetOpacity(0.8) self.mesh_actor.GetProperty().SetSpecular(0.3) self.mesh_actor.GetProperty().SetSpecularPower(20) self.renderer.AddActor(self.mesh_actor) self.vtk_widget.GetRenderWindow().Render() # 保存平滑后的网格用于导出 self.smoothed_mesh = smoother.GetOutput() def update_slice_views(self): """更新所有切片视图""" if not hasattr(self, "volume_array"): return # 获取当前切片位置 axial_pos = self.axial_slider['slider'].value() coronal_pos = self.coronal_slider['slider'].value() sagittal_pos = self.sagittal_slider['slider'].value() # 更新轴向视图 axial_slice = self.volume_array[axial_pos, :, :] self.slice_views["axial"]["image"] = axial_slice self.slice_views["axial"]["axis"].clear() self.slice_views["axial"]["axis"].imshow(axial_slice.T, cmap="gray", origin="lower") self.slice_views["axial"]["axis"].set_title(f"轴向: {axial_pos}/{self.volume_array.shape[0]-1}") self.slice_views["axial"]["axis"].axis("off") # 更新冠状视图 coronal_slice = self.volume_array[:, coronal_pos, :] self.slice_views["coronal"]["image"] = coronal_slice self.slice_views["coronal"]["axis"].clear() self.slice_views["coronal"]["axis"].imshow(coronal_slice.T, cmap="gray", origin="lower") self.slice_views["coronal"]["axis"].set_title(f"冠状: {coronal_pos}/{self.volume_array.shape[1]-1}") self.slice_views["coronal"]["axis"].axis("off") # 更新矢状视图 sagittal_slice = self.volume_array[:, :, sagittal_pos] self.slice_views["sagittal"]["image"] = sagittal_slice self.slice_views["sagittal"]["axis"].clear() self.slice_views["sagittal"]["axis"].imshow(sagittal_slice.T, cmap="gray", origin="lower") self.slice_views["sagittal"]["axis"].set_title(f"矢状: {sagittal_pos}/{self.volume_array.shape[2]-1}") self.slice_views["sagittal"]["axis"].axis("off") # 应用窗宽窗位 self.apply_window_level() # 如果有路径点,在2D视图中显示 if hasattr(self, "current_path_points") and self.current_path_points: self.draw_path_on_slices() self.canvas.draw() def apply_window_level(self): """应用窗宽窗位设置""" if not hasattr(self, "volume_array"): return ww = self.ww_slider['slider'].value() wl = self.wl_slider['slider'].value() for view in self.slice_views.values(): if view["image"] is not None: for img in view["axis"].get_images(): img.set_clim(wl - ww/2, wl + ww/2) self.canvas.draw() def draw_path_on_slices(self): """在切片上绘制路径点""" if not self.current_path_points: return # 将世界坐标转换为图像坐标 for view_name, view in self.slice_views.items(): view["axis"].clear() # 重新绘制图像 if view["image"] is not None: view["axis"].imshow(view["image"].T, cmap="gray", origin="lower") view["axis"].axis("off") # 绘制路径点 for i, point in enumerate(self.current_path_points): # 转换为图像坐标 img_coord = (np.array(point) - self.origin) / self.spacing # 根据视图类型确定要显示的坐标 if view_name == "axial": x, y = img_coord[1], img_coord[2] # 注意坐标顺序 current_slice = self.axial_slider['slider'].value() if abs(img_coord[0] - current_slice) < 1.0: view["axis"].plot(x, y, "r+", markersize=10) view["axis"].text(x, y, str(i), color="red") elif view_name == "coronal": x, y = img_coord[0], img_coord[2] current_slice = self.coronal_slider['slider'].value() if abs(img_coord[1] - current_slice) < 1.0: view["axis"].plot(x, y, "r+", markersize=10) view["axis"].text(x, y, str(i), color="red") elif view_name == "sagittal": x, y = img_coord[0], img_coord[1] current_slice = self.sagittal_slider['slider'].value() if abs(img_coord[2] - current_slice) < 1.0: view["axis"].plot(x, y, "r+", markersize=10) view["axis"].text(x, y, str(i), color="red") def toggle_path_planning(self): """切换路径规划模式""" self.path_planning_mode = not self.path_planning_mode if self.path_planning_mode: self.path_button.setText("完成路径规划") self.clear_path_button.setEnabled(False) self.current_path_points = [] self.export_path_button.setEnabled(False) # 设置交互回调 self.interactor.AddObserver(vtk.vtkCommand.LeftButtonPressEvent, self.add_path_point) else: self.path_button.setText("开始路径规划") self.clear_path_button.setEnabled(len(self.current_path_points) > 0) # 移除交互回调 self.interactor.RemoveObservers(vtk.vtkCommand.LeftButtonPressEvent) if len(self.current_path_points) > 1: self.draw_3d_path() self.export_path_button.setEnabled(True) def add_path_point(self, obj, event): """添加路径点""" click_pos = self.interactor.GetEventPosition() # 使用拾取器获取3D坐标 picker = vtk.vtkCellPicker() picker.SetTolerance(0.005) picker.Pick(click_pos[0], click_pos[1], 0, self.renderer) if picker.GetCellId() != -1: world_pos = picker.GetPickPosition() self.current_path_points.append(world_pos) # 在2D视图中显示标记 self.draw_path_on_slices() self.canvas.draw() def draw_3d_path(self): """绘制3D路径""" if len(self.current_path_points) < 2: return # 如果已有路径,先移除 if hasattr(self, "path_actor"): self.renderer.RemoveActor(self.path_actor) # 创建路径线条 points = vtk.vtkPoints() lines = vtk.vtkCellArray() lines.InsertNextCell(len(self.current_path_points)) for i, point in enumerate(self.current_path_points): points.InsertNextPoint(point) lines.InsertCellPoint(i) poly_data = vtk.vtkPolyData() poly_data.SetPoints(points) poly_data.SetLines(lines) # 创建顶点(用于显示点) vertices = vtk.vtkCellArray() for i in range(len(self.current_path_points)): vert = vtk.vtkVertex() vert.GetPointIds().SetId(0, i) vertices.InsertNextCell(vert) poly_data.SetVerts(vertices) # 创建mapper和actor mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(poly_data) self.path_actor = vtk.vtkActor() self.path_actor.SetMapper(mapper) self.path_actor.GetProperty().SetColor(1, 0, 0) self.path_actor.GetProperty().SetLineWidth(3) self.path_actor.GetProperty().SetPointSize(8) self.renderer.AddActor(self.path_actor) self.vtk_widget.GetRenderWindow().Render() # 保存路径数据用于导出 self.path_data = poly_data def clear_path(self): """清除路径""" if hasattr(self, "path_actor"): self.renderer.RemoveActor(self.path_actor) del self.path_actor self.vtk_widget.GetRenderWindow().Render() self.current_path_points = [] self.clear_path_button.setEnabled(False) self.export_path_button.setEnabled(False) self.update_slice_views() def export_mesh_to_dae(self): """导出网格为DAE格式""" if not hasattr(self, "smoothed_mesh"): QMessageBox.warning(self, "警告", "没有可导出的网格") return options = QFileDialog.Options() file_path, _ = QFileDialog.getSaveFileName( self, "保存网格为 DAE 文件", "", "Collada 文件 (*.dae);;所有文件 (*)", options=options, ) if file_path: try: if not file_path.lower().endswith(".dae"): file_path += ".dae" # 创建导出器 exporter = vtk.vtkGLTFExporter() exporter.SetFileName(file_path) exporter.InlineDataOn() # 创建一个临时渲染窗口用于导出 render_window = vtk.vtkRenderWindow() renderer = vtk.vtkRenderer() render_window.AddRenderer(renderer) # 只添加网格actor renderer.AddActor(self.mesh_actor) renderer.SetBackground(0, 0, 0) exporter.SetRenderWindow(render_window) exporter.Write() QMessageBox.information(self, "成功", f"网格已保存到 {file_path}") except Exception as e: QMessageBox.critical(self, "错误", f"导出网格失败:\n{str(e)}") def export_path_to_dae(self): """导出路径为DAE格式""" if not hasattr(self, "path_data"): QMessageBox.warning(self, "警告", "没有可导出的路径") return options = QFileDialog.Options() file_path, _ = QFileDialog.getSaveFileName( self, "保存路径为 DAE 文件", "", "Collada 文件 (*.dae);;所有文件 (*)", options=options, ) if file_path: try: if not file_path.lower().endswith(".dae"): file_path += ".dae" # 创建路径的actor mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(self.path_data) path_actor = vtk.vtkActor() path_actor.SetMapper(mapper) path_actor.GetProperty().SetColor(1, 0, 0) path_actor.GetProperty().SetLineWidth(3) path_actor.GetProperty().SetPointSize(8) # 创建导出器 exporter = vtk.vtkGLTFExporter() exporter.SetFileName(file_path) exporter.InlineDataOn() # 创建一个临时渲染窗口用于导出 render_window = vtk.vtkRenderWindow() renderer = vtk.vtkRenderer() render_window.AddRenderer(renderer) renderer.AddActor(path_actor) renderer.SetBackground(0, 0, 0) exporter.SetRenderWindow(render_window) exporter.Write() QMessageBox.information(self, "成功", f"路径已保存到 {file_path}") except Exception as e: QMessageBox.critical(self, "错误", f"导出路径失败:\n{str(e)}") def main(): app = QApplication(sys.argv) if hasattr(Qt, 'AA_EnableHighDpiScaling'): QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) if hasattr(Qt, 'AA_UseHighDpiPixmaps'): QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) viewer = MedicalViewer() viewer.show() sys.exit(app.exec_()) if __name__ == "__main__": if sys.platform == 'win32' and sys.executable.endswith('python.exe'): try: import subprocess subprocess.Popen([sys.executable.replace('python.exe', 'pythonw.exe')] + sys.argv) sys.exit(0) except: pass main() 该代码是通过读取DICOM文件并进行处理来实现CT图像的三维建模,但在使用过程中发现无法正确读取DICOM文件,所以我想要nii格式文件来进行图像处理实现CT图像的三维重建
07-12
import os import sys import cv2 import numpy as np import subprocess import tempfile import shutil import random import wave import struct from PIL import Image, ImageDraw, ImageFont # 尝试导入PyQt5,如果失败则提供友好的错误信息 try: from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog, QMessageBox, QProgressBar, QGroupBox, QTextEdit, QCheckBox, QListWidget, QListWidgetItem, QComboBox) from PyQt5.QtCore import Qt, QThread, pyqtSignal from PyQt5.QtGui import QFont, QPalette, QColor, QIcon except ImportError: print("错误: 需要安装PyQt5库") print("请运行: pip install PyQt5") sys.exit(1) # 尝试导入GPU相关库 try: import pyopencl as cl OPENCL_AVAILABLE = True except ImportError: OPENCL_AVAILABLE = False try: import cupy as cp CUDA_AVAILABLE = True except ImportError: CUDA_AVAILABLE = False # 尝试导入scipy try: import scipy.io.wavfile as wavfile SCIPY_AVAILABLE = True except ImportError: SCIPY_AVAILABLE = False class AudioSteganography: """音频隐写处理类""" @staticmethod def embed_message(audio_path, message, output_path): """使用改进的LSB算法在音频中嵌入消息""" try: # 读取音频文件 with wave.open(audio_path, 'rb') as audio: params = audio.getparams() frames = audio.readframes(audio.getnframes()) # 将音频数据转换为字节数组 audio_data = bytearray(frames) # 将消息转换为二进制 binary_message = ''.join(format(ord(char), '08b') for char in message) binary_message += '00000000' # 添加终止符 # 检查音频容量是否足够 if len(binary_message) > len(audio_data) * 8: raise ValueError("音频文件太小,无法嵌入消息") # 使用改进的LSB算法嵌入消息(每4个样本嵌入1位) sample_interval = 4 # 每4个样本嵌入1位 message_index = 0 for i in range(0, len(audio_data), sample_interval): if message_index >= len(binary_message): break # 修改每个样本的最低有效位 audio_data[i] = (audio_data[i] & 0xFE) | int(binary_message[message_index]) message_index += 1 # 保存带有隐写信息的音频 with wave.open(output_path, 'wb') as output_audio: output_audio.setparams(params) output_audio.writeframes(bytes(audio_data)) return True, "音频隐写成功" except Exception as e: return False, f"音频隐写失败: {str(e)}" @staticmethod def extract_message(audio_path): """从音频中提取隐藏的消息""" try: # 读取音频文件 with wave.open(audio_path, 'rb') as audio: frames = audio.readframes(audio.getnframes()) # 将音频数据转换为字节数组 audio_data = bytearray(frames) # 提取LSB位 binary_message = '' sample_interval = 4 # 与嵌入时保持一致 for i in range(0, len(audio_data), sample_interval): binary_message += str(audio_data[i] & 1) # 将二进制转换为字符 message = '' for i in range(0, len(binary_message), 8): byte = binary_message[i:i+8] if len(byte) < 8: break char = chr(int(byte, 2)) if char == '\0': # 遇到终止符停止 break message += char return True, message except Exception as e: return False, f"消息提取失败: {str(e)}" class VideoProcessor(QThread): progress_updated = pyqtSignal(int) status_updated = pyqtSignal(str) finished = pyqtSignal(bool, str) batch_progress = pyqtSignal(int, int) # 当前处理, 总计 def __init__(self, video_a_path, video_b_paths, output_dir, use_gpu=False, gpu_type="auto"): super().__init__() self.video_a_path = video_a_path self.video_b_paths = video_b_paths self.output_dir = output_dir self.use_gpu = use_gpu self.gpu_type = gpu_type self.temp_dir = tempfile.mkdtemp() self.gpu_context = None self.gpu_queue = None self.gpu_device = None # 初始化GPU环境 if self.use_gpu: self.init_gpu() def init_gpu(self): """初始化GPU环境""" try: if self.gpu_type == "cuda" or (self.gpu_type == "auto" and CUDA_AVAILABLE): # 使用CUDA self.status_updated.emit("初始化CUDA环境...") # 检查可用GPU devices = cp.cuda.runtime.getDeviceCount() if devices > 0: self.status_updated.emit(f"找到 {devices} 个NVIDIA GPU") # 使用第一个可用的GPU cp.cuda.Device(0).use() self.gpu_type = "cuda" return True else: self.status_updated.emit("未找到NVIDIA GPU,尝试使用OpenCL") self.gpu_type = "opencl" if self.gpu_type == "opencl" or (self.gpu_type == "auto" and OPENCL_AVAILABLE): # 使用OpenCL self.status_updated.emit("初始化OpenCL环境...") platforms = cl.get_platforms() # 优先寻找Intel Arc显卡 intel_arc_found = False for platform in platforms: devices = platform.get_devices(device_type=cl.device_type.GPU) for device in devices: device_name = device.name if "Intel" in device_name and ("Arc" in device_name or "A770" in device_name): self.status_updated.emit(f"找到Intel Arc显卡: {device_name}") self.gpu_context = cl.Context([device]) self.gpu_queue = cl.CommandQueue(self.gpu_context) self.gpu_device = device self.gpu_type = "opencl" intel_arc_found = True break if intel_arc_found: break # 如果没有找到Intel Arc,寻找其他GPU if not intel_arc_found: for platform in platforms: devices = platform.get_devices(device_type=cl.device_type.GPU) if devices: self.gpu_context = cl.Context(devices) self.gpu_queue = cl.CommandQueue(self.gpu_context) self.gpu_device = devices[0] self.status_updated.emit(f"找到OpenCL GPU: {devices[0].name}") self.gpu_type = "opencl" return True # 如果没有找到GPU,尝试使用CPU if not intel_arc_found: for platform in platforms: devices = platform.get_devices(device_type=cl.device_type.CPU) if devices: self.gpu_context = cl.Context(devices) self.gpu_queue = cl.CommandQueue(self.gpu_context) self.gpu_device = devices[0] self.status_updated.emit(f"使用OpenCL CPU: {devices[0].name}") self.gpu_type = "opencl" return True self.status_updated.emit("未找到OpenCL设备,将使用CPU") self.use_gpu = False return False else: self.status_updated.emit("未安装GPU支持库,将使用CPU") self.use_gpu = False return False except Exception as e: self.status_updated.emit(f"GPU初始化失败: {str(e)},将使用CPU") self.use_gpu = False return False def run(self): try: # 创建OK文件夹 ok_dir = os.path.join(self.output_dir, "OK") os.makedirs(ok_dir, exist_ok=True) total_videos = len(self.video_b_paths) for idx, video_b_path in enumerate(self.video_b_paths): output_filename = f"output_{os.path.basename(video_b_path).split('.')[0]}.mp4" output_path = os.path.join(ok_dir, output_filename) self.batch_progress.emit(idx + 1, total_videos) self.status_updated.emit(f"处理视频 {idx + 1}/{total_videos}: {os.path.basename(video_b_path)}") # 处理单个视频对 success, message = self.process_single_video(self.video_a_path, video_b_path, output_path) if not success: self.finished.emit(False, f"处理失败: {message}") return self.finished.emit(True, f"批量处理完成!共处理 {total_videos} 个视频,输出保存在 {ok_dir}") except Exception as e: import traceback error_details = traceback.format_exc() self.finished.emit(False, f"处理过程中出现错误: {str(e)}\n详细信息:\n{error_details}") finally: # 清理临时文件 if os.path.exists(self.temp_dir): try: shutil.rmtree(self.temp_dir) except: pass def process_single_video(self, video_a_path, video_b_path, output_path): """处理单个视频对""" try: self.status_updated.emit("开始处理视频...") # 提取音频 self.status_updated.emit("提取音频...") audio_path = self.extract_audio(video_a_path) self.progress_updated.emit(10) # 处理视频A self.status_updated.emit("处理视频A...") a_frames_dir = self.process_video_a(video_a_path) self.progress_updated.emit(30) # 处理视频B self.status_updated.emit("处理视频B...") b_frames_dir = self.process_video_b(video_b_path, len(os.listdir(a_frames_dir))) self.progress_updated.emit(50) # 嵌入隐写 self.status_updated.emit("嵌入隐写信息...") stego_frames_dir = self.embed_steganography(a_frames_dir, b_frames_dir) self.progress_updated.emit(70) # 处理音频并合成最终视频 self.status_updated.emit("处理音频并合成最终视频...") self.process_audio_and_assemble(stego_frames_dir, audio_path, output_path) self.progress_updated.emit(90) # 添加随机元数据 self.status_updated.emit("添加元数据...") self.add_random_metadata(output_path) self.progress_updated.emit(100) return True, "处理完成" except Exception as e: import traceback error_details = traceback.format_exc() return False, f"处理过程中出现错误: {str(e)}\n详细信息:\n{error_details}" def extract_audio(self, video_path): """提取音频并转换为单声道""" audio_path = os.path.join(self.temp_dir, "audio.wav") # 使用ffmpeg提取音频并转换为单声道 cmd = [ 'ffmpeg', '-i', video_path, '-vn', '-ac', '1', '-ar', '44100', '-y', audio_path ] try: subprocess.run(cmd, check=True, capture_output=True) return audio_path except subprocess.CalledProcessError as e: # 如果提取失败,创建一个默认的音频文件 self.status_updated.emit("警告: 无法提取音频,将创建默认音频") try: # 创建一个1秒的静音音频 sample_rate = 44100 duration = 10 # 默认10秒 # 获取视频时长 probe_cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', video_path] result = subprocess.run(probe_cmd, capture_output=True, text=True) if result.returncode == 0: duration = float(result.stdout.strip()) # 生成静音 t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) silence = np.zeros_like(t) # 保存为WAV文件 if SCIPY_AVAILABLE: wavfile.write(audio_path, sample_rate, silence.astype(np.float32)) else: # 使用wave模块创建WAV文件 with wave.open(audio_path, 'wb') as wav_file: wav_file.setnchannels(1) wav_file.setsampwidth(2) wav_file.setframerate(sample_rate) # 将静音数据转换为字节 max_amplitude = 32767 # 16位有符号整数的最大值 for sample in silence: data = struct.pack('<h', int(sample * max_amplitude)) wav_file.writeframesraw(data) return audio_path except Exception as e2: self.status_updated.emit(f"创建默认音频失败: {str(e2)}") # 最后的手段,创建一个空的音频文件 open(audio_path, 'a').close() return audio_path def process_video_a(self, video_path): """处理视频A""" # 创建输出目录 output_dir = os.path.join(self.temp_dir, "video_a_frames") os.makedirs(output_dir, exist_ok=True) # 获取视频信息 cap = cv2.VideoCapture(video_path) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) fps = cap.get(cv2.CAP_PROP_FPS) width, height = 1080, 2336 # 生成不可见黑色扰动背景 for i in range(total_frames): # 创建带有轻微扰动的黑色背景 background = np.random.randint(0, 3, (height, width, 3), dtype=np.uint8) # 读取原始帧 ret, frame = cap.read() if not ret: break # 调整原始帧大小并居中放置 h, w = frame.shape[:2] scale = min((width-10)/w, (height-10)/h) new_w, new_h = int(w * scale), int(h * scale) resized_frame = cv2.resize(frame, (new_w, new_h)) # 将调整后的帧放置在背景上,设置不透明度为98% x_offset = (width - new_w) // 2 y_offset = (height - new_h) // 2 # 创建叠加层 overlay = background.copy() roi = overlay[y_offset:y_offset+new_h, x_offset:x_offset+new_w] # 使用加权叠加实现98%不透明度 cv2.addWeighted(resized_frame, 0.98, roi, 0.02, 0, roi) # 保存帧 cv2.imwrite(os.path.join(output_dir, f"frame_{i:06d}.png"), overlay) # 更新进度 if i % 10 == 0: self.status_updated.emit(f"处理视频A帧: {i}/{total_frames}") cap.release() return output_dir def process_video_b(self, video_path, total_frames_needed): """处理视频B""" # 创建输出目录 output_dir = os.path.join(self.temp_dir, "video_b_frames") os.makedirs(output_dir, exist_ok=True) # 获取视频B的信息 cap_b = cv2.VideoCapture(video_path) total_frames_b = int(cap_b.get(cv2.CAP_PROP_FRAME_COUNT)) fps_b = cap_b.get(cv2.CAP_PROP_FPS) width, height = 1080, 2336 # 计算需要从视频B中提取的帧 start_frame = 0 if total_frames_b > total_frames_needed: start_frame = random.randint(0, total_frames_b - total_frames_needed) # 生成不可见黑色扰动背景并处理视频B for i in range(total_frames_needed): # 创建带有轻微扰动的黑色背景 background = np.random.randint(0, 3, (height, width, 3), dtype=np.uint8) # 读取原始帧(从适当的位置) frame_idx = min(start_frame + i, total_frames_b - 1) cap_b.set(cv2.CAP_PROP_POS_FRAMES, frame_idx) ret, frame = cap_b.read() if not ret: break # 调整原始帧大小并居中放置 h, w = frame.shape[:2] scale = min((width-10)/w, (height-10)/h) new_w, new_h = int(w * scale), int(h * scale) resized_frame = cv2.resize(frame, (new_w, new_h)) # 将调整后的帧放置在背景上 x_offset = (width - new_w) // 2 y_offset = (height - new_h) // 2 # 创建叠加层 overlay = background.copy() roi = overlay[y_offset:y_offset+new_h, x_offset:x_offset+new_w] # 叠加帧 cv2.addWeighted(resized_frame, 1.0, roi, 0.0, 0, roi) # 保存帧 cv2.imwrite(os.path.join(output_dir, f"frame_{i:06d}.png"), overlay) # 更新进度 if i % 10 == 0: self.status_updated.emit(f"处理视频B帧: {i}/{total_frames_needed}") cap_b.release() return output_dir def dct_embed_gpu(self, carrier, secret): """使用GPU加速的DCT隐写""" if self.gpu_type == "cuda" and CUDA_AVAILABLE and self.use_gpu: # 使用CuPy进行GPU加速 carrier_gpu = cp.asarray(carrier) secret_gpu = cp.asarray(secret) # 转换为YUV颜色空间 carrier_yuv = cp.zeros_like(carrier_gpu) secret_yuv = cp.zeros_like(secret_gpu) # RGB到YUV转换矩阵 transform = cp.array([[0.299, 0.587, 0.114], [-0.14713, -0.28886, 0.436], [0.615, -0.51499, -0.10001]]) # 应用转换矩阵 for i in range(carrier_gpu.shape[0]): for j in range(carrier_gpu.shape[1]): carrier_yuv[i, j] = cp.dot(transform, carrier_gpu[i, j]) secret_yuv[i, j] = cp.dot(transform, secret_gpu[i, j]) # 只使用Y通道进行DCT变换 carrier_y = carrier_yuv[:,:,0].astype(cp.float32) secret_y = secret_yuv[:,:,0].astype(cp.float32) # 对载体和秘密图像进行DCT变换 carrier_dct = cp.fft.dct(cp.fft.dct(carrier_y, axis=0), axis=1) secret_dct = cp.fft.dct(cp.fft.dct(secret_y, axis=0), axis=1) # 嵌入强度因子 alpha = 0.03 # 在DCT域中嵌入秘密图像 stego_dct = carrier_dct + alpha * secret_dct # 进行逆DCT变换 stego_y = cp.fft.idct(cp.fft.idct(stego_dct, axis=1), axis=0) # 将结果放回YUV图像中 stego_yuv = carrier_yuv.copy() stego_yuv[:,:,0] = stego_y # YUV到RGB转换矩阵 inv_transform = cp.linalg.inv(transform) # 转换回RGB颜色空间 stego_bgr = cp.zeros_like(stego_yuv) for i in range(stego_yuv.shape[0]): for j in range(stego_yuv.shape[1]): stego_bgr[i, j] = cp.dot(inv_transform, stego_yuv[i, j]) return cp.clip(stego_bgr, 0, 255).astype(cp.uint8).get() elif self.gpu_type == "opencl" and OPENCL_AVAILABLE and self.use_gpu and self.gpu_context: # 使用OpenCL进行GPU加速,特别优化Intel Arc显卡 return self.dct_embed_opencl(carrier, secret) else: # 使用CPU版本 return self.dct_embed_cpu(carrier, secret) def dct_embed_opencl(self, carrier, secret): """使用OpenCL进行DCT隐写,特别优化Intel Arc显卡""" try: # 将图像转换为YUV颜色空间 carrier_yuv = cv2.cvtColor(carrier, cv2.COLOR_BGR2YUV) secret_yuv = cv2.cvtColor(secret, cv2.COLOR_BGR2YUV) # 只使用Y通道进行DCT变换 carrier_y = carrier_yuv[:,:,0].astype(np.float32) secret_y = secret_yuv[:,:,0].ast(np.float32) # 创建OpenCL缓冲区 carrier_buffer = cl.Buffer(self.gpu_context, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=carrier_y) secret_buffer = cl.Buffer(self.gpu_context, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=secret_y) # 创建输出缓冲区 stego_dct_buffer = cl.Buffer(self.gpu_context, cl.mem_flags.WRITE_ONLY, carrier_y.nbytes) stego_y_buffer = cl.Buffer(self.gpu_context, cl.mem_flags.WRITE_ONLY, carrier_y.nbytes) # 编译OpenCL程序 - 修复了内核代码中的语法错误 dct_program = cl.Program(self.gpu_context, """ __kernel void dct_embed(__global float* carrier, __global float* secret, __global float* stego_dct, __global float* stego_y, float alpha, int width, int height) { int x = get_global_id(0); int y = get_global_id(1); int idx = y * width + x; // DCT变换 (简化实现) // 在实际应用中,这里应该实现完整的2D DCT算法 // 这里使用简化的DCT近似 float dct_carrier = carrier[idx] * cos((2*x+1)*y*M_PI/(2*width)); float dct_secret = secret[idx] * cos((2*x+1)*y*M_PI/(2*width)); // 嵌入秘密图像 stego_dct[idx] = dct_carrier + alpha * dct_secret; // 逆DCT变换 stego_y[idx] = stego_dct[idx] * cos((2*x+1)*y*M_PI/(2*width)); } """).build() # 设置内核参数 width, height = carrier_y.shape[1], carrier_y.shape[0] alpha = np.float32(0.03) # 执行内核 dct_program.dct_embed(self.gpu_queue, carrier_y.shape, None, carrier_buffer, secret_buffer, stego_dct_buffer, stego_y_buffer, alpha, np.int32(width), np.int32(height)) # 读取结果 stego_y = np.empty_like(carrier_y) cl.enqueue_copy(self.gpu_queue, stego_y, stego_y_buffer) # 将结果放回YUV图像中 stego_yuv = carrier_yuv.copy() stego_yuv[:,:,0] = stego_y # 转换回BGR颜色空间 stego_bgr = cv2.cvtColor(stego_yuv, cv2.COLOR_YUV2BGR) return np.clip(stego_bgr, 0, 255).astype(np.uint8) except Exception as e: self.status_updated.emit(f"OpenCL处理失败: {str(e)},将使用CPU") return self.dct_embed_cpu(carrier, secret) def dct_embed_cpu(self, carrier, secret): """在DCT域中嵌入秘密图像(CPU版本)""" # 将图像转换为YUV颜色空间 carrier_yuv = cv2.cvtColor(carrier, cv2.COLOR_BGR2YUV) secret_yuv = cv2.cvtColor(secret, cv2.COLOR_BGR2YUV) # 只使用Y通道进行DCT变换 carrier_y = carrier_yuv[:,:,0].astype(np.float32) secret_y = secret_yuv[:,:,0].astype(np.float32) # 对载体和秘密图像进行DCT变换 carrier_dct = cv2.dct(carrier_y) secret_dct = cv2.dct(secret_y) # 嵌入强度因子 alpha = 0.03 # 在DCT域中嵌入秘密图像 stego_dct = carrier_dct + alpha * secret_dct # 进行逆DCT变换 stego_y = cv2.idct(stego_dct) # 将结果放回YUV图像中 stego_yuv = carrier_yuv.copy() stego_yuv[:,:,0] = stego_y # 转换回BGR颜色空间 stego_bgr = cv2.cvtColor(stego_yuv, cv2.COLOR_YUV2BGR) return np.clip(stego_bgr, 0, 255).ast(np.uint8) def embed_steganography(self, a_frames_dir, b_frames_dir): """嵌入隐写信息""" # 创建输出目录 output_dir = os.path.join(self.temp_dir, "stego_frames") os.makedirs(output_dir, exist_ok=True) # 获取帧列表 a_frames = sorted([f for f in os.listdir(a_frames_dir) if f.endswith('.png')]) total_frames = len(a_frames) for i, frame_name in enumerate(a_frames): # 读取A视频帧 carrier_frame = cv2.imread(os.path.join(a_frames_dir, frame_name)) # 读取B视频帧 secret_frame = cv2.imread(os.path.join(b_frames_dir, frame_name)) # 调整秘密图像大小以匹配载体图像 secret_frame = cv2.resize(secret_frame, (carrier_frame.shape[1], carrier_frame.shape[0])) # 在DCT域中嵌入秘密图像 if self.use_gpu: stego_frame = self.dct_embed_gpu(carrier_frame, secret_frame) else: stego_frame = self.dct_embed_cpu(carrier_frame, secret_frame) # 添加数字水印(版权保护)- 透明度极低,人眼几乎不可见 stego_frame = self.add_watermark(stego_frame) # 保存处理后的帧 cv2.imwrite(os.path.join(output_dir, frame_name), stego_frame) # 更新进度 if i % 10 == 0: self.status_updated.emit(f"嵌入隐写帧: {i}/{total_frames}") return output_dir def add_watermark(self, image): """添加数字水印 - 透明度极低,人眼几乎不可见""" # 将OpenCV图像转换为PIL图像 pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) # 创建一个绘图对象 draw = ImageDraw.Draw(pil_image, 'RGBA') # 使用默认字体 try: font = ImageFont.load_default() # 尝试加载系统字体 try: font = ImageFont.truetype("arial.ttf", 20) except: pass except: pass # 添加水印文本 - 使用极低的透明度 (约0.5%) watermark_text = "Copyright Protected" # 获取文本尺寸 try: # 对于较新版本的Pillow bbox = draw.textbbox((0, 0), watermark_text, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] except: # 对于较旧版本的Pillow try: text_width, text_height = draw.textsize(watermark_text, font=font) except: # 如果所有方法都失败,使用估计值 text_width, text_height = 150, 20 # 在多个位置添加水印 positions = [ (10, 10), # 左上角 (image.shape[1] - text_width - 10, 10), # 右上角 (10, image.shape[0] - text_height - 10), # 左下角 (image.shape[1] - text_width - 10, image.shape[0] - text_height - 10) # 右下角 ] for position in positions: # 添加文本 - 使用极低的透明度 (约0.5%) draw.text(position, watermark_text, (255, 255, 255, 2), font=font) # 透明度从5降低到2 # 转换回OpenCV图像 return cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) def process_audio_and_assemble(self, frames_dir, audio_path, output_path): """处理音频并合成最终视频""" # 生成随机噪声音频 noise_audio_path = os.path.join(self.temp_dir, "noise_audio.wav") # 获取音频信息 try: cmd = ['ffprobe', '-i', audio_path, '-show_entries', 'format=duration', '-v', 'quiet', '-of', 'csv=p=0'] result = subprocess.run(cmd, capture_output=True, text=True) duration = float(result.stdout.strip()) # 生成随机噪声(极低音量) sample_rate = 44100 t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) noise = 0.0005 * np.random.randn(len(t)) # 极低音量噪声 # 保存噪声音频 if SCIPY_AVAILABLE: wavfile.write(noise_audio_path, sample_rate, noise.astype(np.float32)) else: # 使用wave模块创建WAV文件 with wave.open(noise_audio_path, 'wb') as wav_file: wav_file.setnchannels(1) wav_file.setsampwidth(2) wav_file.setframerate(sample_rate) # 将噪声数据转换为字节 max_amplitude = 32767 # 16位有签名整数的最大值 for sample in noise: data = struct.pack('<h', int(sample * max_amplitude)) wav_file.writeframesraw(data) except: # 如果生成噪声失败,创建一个空的音频文件 open(noise_audio_path, 'a').close() # 在音频中嵌入隐写信息 stego_audio_path = os.path.join(self.temp_dir, "stego_audio.wav") message = "HiddenSteganoMessage2023" success, msg = AudioSteganography.embed_message(audio_path, message, stego_audio_path) if not success: self.status_updated.emit(f"音频隐写警告: {msg}") stego_audio_path = audio_path # 使用原始音频 # 合并音频(左声道为隐写音频,右声道为噪声) mixed_audio_path = os.path.join(self.temp_dir, "mixed_audio.wav") cmd = [ 'ffmpeg', '-y', '-i', stego_audio_path, '-i', noise_audio_path, '-filter_complex', '[0:a][1:a]amerge=inputs=2,pan=stereo|c0<c0+c1|c1<c2+c3[aout]', '-map', '[aout]', mixed_audio_path ] try: subprocess.run(cmd, check=True, capture_output=True) except: # 如果合并失败,使用隐写音频 mixed_audio_path = stego_audio_path # 使用ffmpeg从帧序列创建视频 frame_pattern = os.path.join(frames_dir, "frame_%06d.png") # 生成随机比特率 (5000-7000kbps) bitrate = random.randint(5000, 7000) self.status_updated.emit(f"使用比特率: {bitrate}kbps") # 使用H.264编码和指定的参数 cmd = [ 'ffmpeg', '-y', '-framerate', '30', '-i', frame_pattern, '-i', mixed_audio_path, '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-crf', '18', # 较低CRF值以获得更高质量 '-preset', 'fast' if not self.use_gpu else 'medium', '-b:v', f'{bitrate}k', '-maxrate', f'{bitrate + 1000}k', '-bufsize', f'{bitrate * 2}k', '-s', '1080x2336', '-c:a', 'aac', '-b:a', '128k', '-metadata', 'title=Processed Video', output_path ] try: subprocess.run(cmd, check=True, capture_output=True) except subprocess.CalledProcessError as e: # 如果添加音频失败,尝试创建没有音频的视频 cmd_no_audio = [ 'ffmpeg', '-y', '-framerate', '30', '-i', frame_pattern, '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-crf', '18', '-preset', 'fast' if not self.use_gpu else 'medium', '-b:v', f'{bitrate}k', '-maxrate', f'{bitrate + 1000}k', '-bufsize', f'{bitrate * 2}k', '-s', '1080x2336', '-metadata', 'title=Processed Video', output_path ] subprocess.run(cmd_no_audio, check=True, capture_output=True) # 添加声纹去重处理(简化实现) self.add_voiceprint_processing(output_path) def add_voiceprint_processing(self, video_path): """添加声纹去重处理(简化实现)""" # 在实际应用中,这里应该实现复杂的声纹处理算法 # 这里只是一个简单的示例,添加一些元数据标记 temp_output = video_path + ".temp.mp4" cmd = [ 'ffmpeg', '-i', video_path, '-metadata', 'voiceprint_processed=true', '-metadata', 'voiceprint_hash=' + ''.join(random.choices('0123456789abcdef', k=32)), '-codec', 'copy', '-y', temp_output ] try: subprocess.run(cmd, check=True, capture_output=True) # 替换原文件 os.replace(temp_output, video_path) except: # 如果添加元数据失败,保持原文件不变 if os.path.exists(temp_output): os.remove(temp_output) def add_random_metadata(self, video_path): """添加随机元数据到视频文件""" metadata_options = { 'creation_time': ['2023-01-15T10:30:00', '2023-02-20T14:45:30', '2023-03-10T09:15:45'], 'location': ['New York, USA', 'London, UK', 'Tokyo, Japan', 'Paris, France'], 'device': ['iPhone 14 Pro', 'Samsung Galaxy S23', 'Canon EOS R5', 'Sony A7IV'], 'description': ['Beautiful landscape', 'Urban exploration', 'Nature documentary', 'Travel vlog'], 'encoder': ['H.264', 'HEVC', 'AV1', 'VP9'] } # 随机选择元数据 selected_metadata = { 'creation_time': random.choice(metadata_options['creation_time']), 'location': random.choice(metadata_options['location']), 'device': random.choice(metadata_options['device']), 'description': random.choice(metadata_options['description']), 'encoder': random.choice(metadata_options['encoder']) } # 创建临时输出文件 temp_output = video_path + ".temp.mp4" # 使用ffmpeg添加元数据 cmd = [ 'ffmpeg', '-i', video_path, '-metadata', f'creation_time={selected_metadata["creation_time"]}', '-metadata', f'location={selected_metadata["location"]}', '-metadata', f'device={selected_metadata["device"]}', '-metadata', f'description={selected_metadata["description"]}', '-metadata', f'encoder={selected_metadata["encoder"]}', '-codec', 'copy', '-y', temp_output ] try: subprocess.run(cmd, check=True, capture_output=True) # 替换原文件 os.replace(temp_output, video_path) except: # 如果添加元数据失败,保持原文件不变 if os.path.exists(temp_output): os.remove(temp_output) class VideoSteganographyApp(QMainWindow): def __init__(self): super().__init__() self.video_a_path = "" self.video_b_paths = [] self.output_dir = "" self.use_gpu = False self.gpu_type = "auto" self.initUI() def initUI(self): self.setWindowTitle('视频隐写处理工具 - 最终版') self.setGeometry(100, 100, 900, 800) # 设置应用程序图标 if hasattr(sys, '_MEIPASS'): # 打包后的路径 icon_path = os.path.join(sys._MEIPASS, 'app_icon.ico') else: # 开发时的路径 icon_path = 'app_icon.ico' if os.path.exists(icon_path): self.setWindowIcon(QIcon(icon_path)) # 设置暗色主题样式 self.setStyleSheet(""" QMainWindow { background-color: #2b2b2b; color: #cccccc; } QGroupBox { font-weight: bold; border: 2px solid #444444; border-radius: 5px; margin-top: 1ex; padding-top: 10px; background-color: #3c3c3c; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 5px 0 5px; color: #ffffff; } QPushButton { background-color: #4CAF50; border: none; color: white; padding: 10px 20px; text-align: center; text-decoration: none; font-size: 16px; margin: 4px 2px; border-radius: 5px; } QPushButton:hover { background-color: #45a049; } QPushButton:disabled { background-color: #555555; } QPushButton:checked { background-color: #2196F3; } QLabel { padding: 5px; color: #cccccc; } QProgressBar { border: 2px solid #444444; border-radius: 5px; text-align: center; background-color: #3c3c3c; } QProgressBar::chunk { background-color: #4CAF50; width: 10px; } QCheckBox { padding: 5px; color: #cccccc; } QCheckBox::indicator { width: 15px; height: 15px; } QCheckBox::indicator:unchecked { border: 1px solid #555555; background-color: #3c3c3c; } QCheckBox::indicator:checked { border: 1px solid #555555; background-color: #4CAF50; } QListWidget { border: 1px solid #444444; border-radius: 3px; background-color: #3c3c3c; color: #cccccc; } QComboBox { border: 1px solid #444444; border-radius: 3px; padding: 5px; background-color: #3c3c3c; color: #cccccc; } QComboBox QAbstractItemView { border: 1px solid #444444; background-color: #3c3c3c; color: #cccccc; selection-background-color: #4CAF50; } QTextEdit { background-color: #3c3c3c; color: #cccccc; border: 1px solid #444444; border-radius: 3px; } """) central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) # 标题 title_label = QLabel("视频隐写处理工具 - 最终版") title_label.setAlignment(Qt.AlignCenter) title_font = QFont() title_font.setPointSize(20) title_font.setBold(True) title_label.setFont(title_font) layout.addWidget(title_label) # 加速选项 acceleration_group = QGroupBox("加速选项") acceleration_layout = QHBoxLayout() self.gpu_checkbox = QCheckBox("使用GPU加速") self.gpu_checkbox.setChecked(False) self.gpu_checkbox.stateChanged.connect(self.toggle_gpu_acceleration) acceleration_layout.addWidget(self.gpu_checkbox) self.gpu_type_combo = QComboBox() self.gpu_type_combo.addItems(["自动检测", "NVIDIA CUDA", "OpenCL (Intel/AMD)"]) self.gpu_type_combo.currentIndexChanged.connect(self.change_gpu_type) acceleration_layout.addWidget(QLabel("GPU类型:")) acceleration_layout.addWidget(self.gpu_type_combo) acceleration_group.setLayout(acceleration_layout) layout.addWidget(acceleration_group) # 视频A选择区域 video_a_group = QGroupBox("视频A (主视频)") video_a_layout = QVBoxLayout() self.video_a_label = QLabel("未选择文件") video_a_layout.addWidget(self.video_a_label) video_a_btn = QPushButton("选择视频A") video_a_btn.clicked.connect(self.select_video_a) video_a_layout.addWidget(video_a_btn) video_a_group.setLayout(video_a_layout) layout.addWidget(video_a_group) # 视频B选择区域 video_b_group = QGroupBox("视频B (隐写视频 - 可多选)") video_b_layout = QVBoxLayout() self.video_b_list = QListWidget() video_b_layout.addWidget(self.video_b_list) video_b_btn_layout = QHBoxLayout() add_video_b_btn = QPushButton("添加视频B") add_video_b_btn.clicked.connect(self.add_video_b) video_b_btn_layout.addWidget(add_video_b_btn) remove_video_b_btn = QPushButton("移除选中") remove_video_b_btn.clicked.connect(self.remove_video_b) video_b_btn_layout.addWidget(remove_video_b_btn) clear_video_b_btn = QPushButton("清空列表") clear_video_b_btn.clicked.connect(self.clear_video_b) video_b_btn_layout.addWidget(clear_video_b_btn) video_b_layout.addLayout(video_b_btn_layout) video_b_group.setLayout(video_b_layout) layout.addWidget(video_b_group) # 输出选择区域 output_group = QGroupBox("输出设置") output_layout = QVBoxLayout() self.output_label = QLabel("未选择输出目录") output_layout.addWidget(self.output_label) output_btn = QPushButton("选择输出目录") output_btn.clicked.connect(self.select_output) output_layout.addWidget(output_btn) output_group.setLayout(output_layout) layout.addWidget(output_group) # 进度区域 progress_group = QGroupBox("处理进度") progress_layout = QVBoxLayout() self.batch_label = QLabel("准备就绪") progress_layout.addWidget(self.batch_label) self.status_label = QLabel("等待开始...") progress_layout.addWidget(self.status_label) self.progress_bar = QProgressBar() self.progress_bar.setValue(0) progress_layout.addWidget(self.progress_bar) progress_group.setLayout(progress_layout) layout.addWidget(progress_group) # 处理按钮 self.process_btn = QPushButton("开始批量处理") self.process_btn.clicked.connect(self.process_videos) self.process_btn.setEnabled(False) layout.addWidget(self.process_btn) # 日志区域 log_group = QGroupBox("处理日志") log_layout = QVBoxLayout() self.log_text = QTextEdit() self.log_text.setReadOnly(True) log_layout.addWidget(self.log_text) log_group.setLayout(log_layout) layout.addWidget(log_group) # 初始化日志 self.log_text.append("应用程序已启动") self.log_text.append(f"CUDA可用: {CUDA_AVAILABLE}") self.log_text.append(f"OpenCL可用: {OPENCL_AVAILABLE}") self.log_text.append(f"Scipy可用: {SCIPY_AVAILABLE}") def toggle_gpu_acceleration(self, state): self.use_gpu = (state == Qt.Checked) if self.use_gpu: self.log_text.append("已启用GPU加速") else: self.log_text.append("已禁用GPU加速,使用CPU处理") def change_gpu_type(self, index): if index == 0: self.gpu_type = "auto" self.log_text.append("GPU类型: 自动检测") elif index == 1: self.gpu_type = "cuda" self.log_text.append("GPU类型: NVIDIA CUDA") elif index == 2: self.gpu_type = "opencl" self.log_text.append("GPU类型: OpenCL (Intel/AMD)") def select_video_a(self): file_path, _ = QFileDialog.getOpenFileName( self, "选择视频A文件", "", "视频文件 (*.mp4 *.avi *.mov *.mkv)" ) if file_path: self.video_a_path = file_path self.video_a_label.setText(f"已选择: {os.path.basename(file_path)}") self.log_text.append(f"已选择视频A: {file_path}") self.check_ready() def add_video_b(self): file_paths, _ = QFileDialog.getOpenFileNames( self, "选择视频B文件", "", "视频文件 (*.mp4 *.avi *.mov *.mkv)" ) if file_paths: for file_path in file_paths: if file_path not in self.video_b_paths: self.video_b_paths.append(file_path) self.video_b_list.addItem(os.path.basename(file_path)) self.log_text.append(f"已添加视频B: {file_path}") self.check_ready() def remove_video_b(self): selected_items = self.video_b_list.selectedItems() for item in selected_items: index = self.video_b_list.row(item) removed_path = self.video_b_paths.pop(index) self.video_b_list.takeItem(index) self.log_text.append(f"已移除视频B: {removed_path}") self.check_ready() def clear_video_b(self): self.video_b_paths.clear() self.video_b_list.clear() self.log_text.append("已清空视频B列表") self.check_ready() def select_output(self): dir_path = QFileDialog.getExistingDirectory( self, "选择输出目录" ) if dir_path: self.output_dir = dir_path self.output_label.setText(f"输出目录: {dir_path}") self.log_text.append(f"已选择输出目录: {dir_path}") self.check_ready() def check_ready(self): if self.video_a_path and self.video_b_paths and self.output_dir: self.process_btn.setEnabled(True) else: self.process_btn.setEnabled(False) def process_videos(self): self.process_btn.setEnabled(False) self.log_text.append("开始批量处理视频...") self.processor = VideoProcessor( self.video_a_path, self.video_b_paths, self.output_dir, self.use_gpu, self.gpu_type ) self.processor.progress_updated.connect(self.update_progress) self.processor.status_updated.connect(self.update_status) self.processor.finished.connect(self.processing_finished) self.processor.batch_progress.connect(self.update_batch_progress) self.processor.start() def update_progress(self, value): self.progress_bar.setValue(value) def update_status(self, message): self.status_label.setText(message) self.log_text.append(message) def update_batch_progress(self, current, total): self.batch_label.setText(f"处理进度: {current}/{total}") self.log_text.append(f"开始处理第 {current} 个视频,共 {total} 个") def processing_finished(self, success, message): self.process_btn.setEnabled(True) self.status_label.setText("处理完成" if success else "处理失败") self.log_text.append(message) if success: QMessageBox.information(self, "成功", message) else: QMessageBox.warning(self, "错误", message) def main(): app = QApplication(sys.argv) # 设置应用程序样式为Fusion,支持暗色主题 app.setStyle('Fusion') # 设置调色板为暗色主题 palette = QPalette() palette.setColor(QPalette.Window, QColor(43, 43, 43)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(25, 25, 25)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(42, 130, 218)) palette.setColor(QPalette.Highlight, QColor(42, 130, 218)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) window = VideoSteganographyApp() window.show() sys.exit(app.exec_()) if __name__ == '__main__': main() 生成的软件提示: 处理失败: 处理过程中出现错误: 'numpy.ndarray' object has no attribute 'ast' 详细信息: Traceback (most recent call last): File "main.py", line 278, in process_single_video File "main.py", line 642, in embed_steganography File "main.py", line 616, in dct_embed_cpu AttributeError: 'numpy.ndarray' object has no attribute 'ast' 请修正一下
08-22
D:\Anaconda\envs\myenv\python.exe E:/学习/代码/MCG_diffusion-main/MCG_diffusion-main/run_CT_recon.py 2025-03-18 10:17:35.996412: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`. 2025-03-18 10:17:36.738870: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`. D:\Anaconda\envs\myenv\lib\site-packages\torch\utils\cpp_extension.py:346: UserWarning: Error checking compiler version for cl: [WinError 2] 系统找不到指定的文件。 warnings.warn(f'Error checking compiler version for {compiler}: {error}') ��Ϣ: ���ṩ��ģʽ�޷��ҵ��ļ��� Traceback (most recent call last): File "E:\学习\代码\MCG_diffusion-main\MCG_diffusion-main\run_CT_recon.py", line 12, in <module> from models import ncsnpp File "E:\学习\代码\MCG_diffusion-main\MCG_diffusion-main\models\ncsnpp.py", line 18, in <module> from . import utils, layers, layerspp, normalization File "E:\学习\代码\MCG_diffusion-main\MCG_diffusion-main\models\layerspp.py", line 20, in <module> from . import up_or_down_sampling File "E:\学习\代码\MCG_diffusion-main\MCG_diffusion-main\models\up_or_down_sampling.py", line 10, in <module> from op import upfirdn2d File "E:\学习\代码\MCG_diffusion-main\MCG_diffusion-main\op\__init__.py", line 1, in <module> from .fused_act import FusedLeakyReLU, fused_leaky_relu File "E:\学习\代码\MCG_diffusion-main\MCG_diffusion-main\op\fused_act.py", line 11, in <module> fused = load( File "D:\Anaconda\envs\myenv\lib\site-packages\torch\utils\cpp_extension.py", line 1202, in load return _jit_compile( File "D:\Anaconda\envs\myenv\lib\site-packages\torch\utils\cpp_extension.py", line 1425, in _jit_compile _write_ninja_file_and_build_library( File "D:\Anaconda\envs\myenv\lib\site-packages\torch\utils\cpp_extension.py", line 1524, in _write_ninja_file_and_build_library _write_ninja_file_to_build_library( File "D:\Anaconda\envs\myenv\lib\site-packages\torch\utils\cpp_extension.py", line 1963, in _write_ninja_file_to_build_library _write_ninja_file( File "D:\Anaconda\envs\myenv\lib\site-packages\torch\utils\cpp_extension.py", line 2090, in _write_ninja_file cl_paths = subprocess.check_output(['where', File "D:\Anaconda\envs\myenv\lib\subprocess.py", line 424, in check_output return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, File "D:\Anaconda\envs\myenv\lib\subprocess.py", line 528, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['where', 'cl']' returned non-zero exit status 1. 进程已结束,退出代码1
03-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值