Giter Club home page Giter Club logo

contrast-phys's People

Contributors

zhaodongsun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

contrast-phys's Issues

About preprocessing. py(BVP)

I would like to know when using OpenFace to generate .csv file during data preprocessing, as one of the inputs of preprocessing. py, which part of preprocessing. py represents the generated .h5 contains the content of bvp signal?
I read my own generated.h5 file containing only {'imgs': }

about hrv

could you please share the code of hrv calculating?

The R-value of Contrast-phys+ on UBFC-RPPG is low

Hello! Recently, I have been replicating Contrast-phys+ and following the steps to train and test it on UBFC-RPPG. The heart rate values are very close, but the calculated R-value always seems to be low. Do you know why? Here is the method I used to calculate the metrics:
image

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/44.npy
hr_rppg:89.0
hr_bvp:89.0
MAE:0.7537042662656902
MSE:0.8300663503050997
RMSE:0.9110797716474116
R:0.5596756484461523

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/49.npy
hr_rppg:87.0
hr_bvp:86.0
MAE:0.7199145203866735
MSE:0.7962145399996426
RMSE:0.8923085452911692
R:0.5253803322031185

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/45.npy
hr_rppg:112.0
hr_bvp:112.0
MAE:0.7285037279791446
MSE:0.7469296112617089
RMSE:0.8642508960144091
R:0.7825049275582626

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/46.npy
hr_rppg:94.00000000000001
hr_bvp:93.0
MAE:0.7080922650721068
MSE:0.7148581794156879
RMSE:0.8454928618360346
R:0.44332718968619483

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/39.npy
hr_rppg:86.0
hr_bvp:86.0
MAE:0.6574607328649096
MSE:0.707239877504444
RMSE:0.8409755510741344
R:0.5742140765121817

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/43.npy
hr_rppg:99.99999999999999
hr_bvp:99.99999999999999
MAE:0.7248150684600015
MSE:0.7970786218834163
RMSE:0.892792597350256
R:0.6297846759769277

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/38.npy
hr_rppg:112.0
hr_bvp:112.0
MAE:0.7259880192697764
MSE:0.7252420109154502
RMSE:0.8516114201415163
R:0.8895395112121949

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/42.npy
hr_rppg:98.0
hr_bvp:98.0
MAE:0.715046724078873
MSE:0.7930097950294874
RMSE:0.8905109741207502
R:0.7976646616673656

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/40.npy
hr_rppg:88.0
hr_bvp:88.0
MAE:0.6873482923836377
MSE:0.7130528581768157
RMSE:0.8444245722246693
R:0.5340293702773984

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/41.npy
hr_rppg:99.99999999999999
hr_bvp:99.99999999999999
MAE:0.6872299273870676
MSE:0.676695129281339
RMSE:0.822614812218537
R:0.59450306089585

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/47.npy
hr_rppg:117.00000000000001
hr_bvp:117.00000000000001
MAE:0.6963591330861216
MSE:0.6587551545136888
RMSE:0.8116373294234863
R:0.6429711341256403

subject:/home/zhixin_yan/project/contrast-phys/contrast-phys+/results/default/23/1/48.npy
hr_rppg:92.0
hr_bvp:92.0
MAE:0.803484226944761
MSE:0.8820074963485776
RMSE:0.9391525415759558
R:0.3685707944758343
MAE:0.7173289086815637
MSE:0.7534291353862799
RMSE:0.8680029581667794
R:0.5585138808441661

Code on other datasets

Could you provide code for preprocessing on other data sets? Because the UBFC-rPPG dataset is relatively simple, it is easy to handle, but it is not easy to handle when applied to other datasets such as PURE

the loss values are negative and fluctuating without any clear pattern

Hello! I would like to inquire about replicating your paper using the UBFC dataset. I have set the number of epochs to 30, while keeping the other parameters the same as those provided in your paper. However, I noticed that the loss values are negative and fluctuating without any clear pattern. Is this behavior normal?
image

Comparing the Performance of MTCNN and OpenFace Face Acquisition Methods

Hello, I was wondering if the final performance difference between MTCNN face acquisition (the method used in the demo sample folder) and OpenFace Face acquisition is big?
你好,我想知道使用MTCNN人脸获取(demo示例文件夹当中使用的方法)和OpenFace人脸获取最后的性能差异大吗?

Because the methods used in the demo sample folder seem simple and easy to use
因为demo示例文件夹当中使用的方法看起来似乎比较简单,易于操作

Why Does the Model Performance Deteriorate Over Time?

A week ago, I trained a model that worked well on the test set, I saved the corresponding weights, but now when I used the weights again to inference (the test set did not change), I found that the effect was poor, why?

In addition, when I ran test.py a week ago, I saved the predicted and true values of each subject in a.npy file, and now when I evaluate r,mae,rmse directly on this.npy file, the result is normal (very good). But now when I re-predict with the model weights that I used at that time, and find r,mae,rmse, the result becomes very bad

Moreover, I re-downloaded the code of the official website, and started training and testing again, and found that the effect was still very poor.

I wonder, what is going on here?

Label Preprocessing

Hi,

What kind of pre-processing actions do we need to take for Contrast-Phys++? Any kind of normalization, etc.?

Thanks

Saliency map

Hi, could you please share the code for calculating saliency maps? Thanks.

A bug related to contrast-phys

Hello, thank you very much for your kind words. I recently encountered a bug while trying to reproduce contrast-phys. I am using the UBFC-phys dataset for training and followed the preprocessing steps mentioned in the readme. However, when running train.py, I encountered the following error. Do you have any idea what could be causing the problem?

image

Time-domain map was not aligned

Hello, after generating the .npy file using test.py, I tried to visualize it and plot the time-domain graph. However, I found that the predicted rPPG signal does not align with the GT (Ground Truth) signal. Below are the images and code. Do you have any suggestions?
The code:

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import welch, resample
from utils_sig import hr_fft, butter_bandpass, normalize

# 读取 .npy 文件
data = np.load(r'C:\first-year graduate\contrast-phys-master\results\2\3\subject38_pred.npy', allow_pickle=True)
rppg_list = data.item().get('rppg_list')
bvp_list = data.item().get('bvp_list')

# 展平信号
rppg = np.concatenate(rppg_list)
bvp = np.concatenate(bvp_list)

# 标准化信号
rppg_norm = normalize(rppg)
bvp_norm = normalize(bvp)

# 计算信号长度和采样率
signal_length = len(rppg_norm)
fs = 30  # 假设采样率为 30Hz

# 对标准化信号进行滤波
rppg_filtered = butter_bandpass(rppg_norm, lowcut=0.6, highcut=4, fs=fs)
bvp_filtered = butter_bandpass(bvp_norm, lowcut=0.6, highcut=4, fs=fs)

# 计算预测心率和地面真值心率
hr_rppg, rppg_psd, rppg_hr = hr_fft(rppg_filtered, fs=fs)
hr_bvp, bvp_psd, bvp_hr = hr_fft(bvp_filtered, fs=fs)

# 计算评估指标
mae = np.mean(np.abs(rppg_filtered - bvp_filtered))
mse = np.mean((rppg_filtered - bvp_filtered) ** 2)
rmse = np.sqrt(mse)
r = np.corrcoef(rppg_filtered, bvp_filtered)[0, 1]

# 绘制时域图和频谱图
fig, (ax1, ax2) = plt.subplots(2, figsize=(20, 10))

# 时域图
time = np.arange(signal_length) / fs
ax1.plot(time, rppg_filtered, label='Predicted Signal')
ax1.plot(time, bvp_filtered, label='Ground Truth Signal')
ax1.set_xlabel('Time (s)')
ax1.set_ylabel('Amplitude')
ax1.set_title('Predicted Signal vs Ground Truth Signal')
ax1.legend()
ax1.grid('on')

# 频谱图
ax2.plot(rppg_hr, rppg_psd, label='Predicted Signal')
ax2.plot(bvp_hr, bvp_psd, label='Ground Truth Signal')
ax2.set_xlabel('Heart Rate (bpm)')
ax2.set_ylabel('Power Spectral Density')
ax2.set_xlim([40, 200])
ax2.set_title('Power Spectral Density of Predicted Signal vs Ground Truth Signal')
ax2.legend()
ax2.grid('on')

# 显示评估指标和预测心率
print(f"MAE: {mae:.4f}")
print(f"MSE: {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"Pearson's Correlation Coefficient (r): {r:.4f}")
print(f"Predicted Heart Rate: {hr_rppg:.2f} bpm")
print(f"Ground Truth Heart Rate: {hr_bvp:.2f} bpm")

plt.show()

Figure_1

The .h5 file does not have bvp data

Hello, I'm reproducing the code and encountered an error when running train.py. The error indicates that there is no bvp data in the preprocessed.h5 file. Can you provide a method or example for handling this?

There is a problem with the hr_fft_batch function in the utils_sig.py file

Some errors occurred when I used the following code to calculateMAE, RMSE,and Pearson coefficients:

for h5_path in test_list:
        h5_path = str(h5_path)

        with h5py.File(h5_path, 'r') as f:
            imgs = f['imgs']
            subject_name = os.path.basename(h5_path)[:-3]
            bvp_path = f"/share2/data/zhouwenqing/UBFC_rPPG/dataset2/{subject_name}/ground_truth.txt"
            bvp = np.loadtxt(bvp_path).reshape((-1, 1))
            # bvppeak = f['bvp_peak']
            fs = config_train['fs']
            
            # duration表示秒数,fs表示frame per seccond 
            duration = np.min([imgs.shape[0], bvp.shape[0]]) / fs
            num_blocks = int(duration // time_interval)
            # 从整个视频当中截取出num_blocks个视频片段,这些片段之间是连续的(指从原视频当中截取的方式)
            rppg_list = []
            bvp_list = []
            # bvppeak_list = []
            for b in range(num_blocks):
                rppg_clip = dl_model(imgs[b*time_interval*fs:(b+1)*time_interval*fs])
                rppg_list.append(rppg_clip)

                bvp_list.append(bvp[b*time_interval*fs:(b+1)*time_interval*fs])
                # bvppeak_list.append(bvppeak[b*time_interval*fs:(b+1)*time_interval*fs])

            rppg_list = np.array(rppg_list)
            bvp_list = np.array(bvp_list)
            results = {'rppg_list': rppg_list, 'bvp_list': bvp_list}
            np.save(pred_exp_dir+'/'+h5_path.split('/')[-1][:-3], results)
            
            bvp_list = bvp_list.reshape(num_blocks, -1)
  
            
            # 通带滤波
            hr_pred = butter_bandpass_batch(rppg_list, lowcut=0.6, highcut=4, fs=30)
            hr_gt = butter_bandpass_batch(bvp_list, lowcut=0.6, highcut=4, fs=30)
            print("通带滤波 hr_pred.shape: ", hr_pred.shape)
            print("通带滤波 hr_gt.shape: ", hr_gt.shape)
            
            # 计算预测心率和地面真值心率
            hr_pred = torch.tensor(hr_fft_batch(hr_pred, 30))
            hr_gt = torch.tensor(hr_fft_batch(hr_gt, 30))
            print("hr_fft_batch hr_pred.shape: ", hr_pred.shape)
            print("hr_fft_batch hr_gt.shape: ", hr_gt.shape)
            
            mae_all = mae_loss_func(hr_pred, hr_gt)
            mse_all = mse_loss_func(hr_pred, hr_gt)
            rmse = np.sqrt(mse_all)
            correlation_coefficients = []
            for i in range(num_blocks):
                # 计算每个维度的皮尔逊相关系数
                correlation_coefficient, _ = pearsonr(hr_pred[i].flatten(), hr_gt[i].flatten())
                correlation_coefficients.append(correlation_coefficient)
            print("Evaluation Result\n MAE: {:.4f}; RMSE: {:.4f}; R: {:.4f};".format(
                mae_all, rmse, sum(correlation_coefficients) / len(correlation_coefficients)))

The output of the above code shows: a tensor with shape of (2, 900) after hr_fft_batch() function shape changed to (2,), and then I applied the output tensor to the scipy.stats.pearsonr functionto find the Pearson coefficient, but error:

File "test.py", line 132, in my_main
correlation_coefficient, _ = pearsonr(hr_pred[i].flatten(), hr_gt[i].flatten())
The File "/ share1 / home/zhouwenqing/anaconda3 / envs/RPPG/lib/python3.6 / site - packages/scipy/stats/stats. Py", line 3838, in pearsonr
raise ValueError('x and y must have length at least 2.')
ValueError: x and y must have length at least 2.

That is, thepearsonr()function requires that the input arrays x and y have at least two elements, because the Pearson correlation coefficient is a statistic that measures the strength and direction of the linear relationship between two variables. What should I do about it?

The MAE, MSE, and RMSE values appear to be normal, but the R-value is extremely small.

The MAE, MSE, and RMSE values appear to be normal, but the R-value is extremely small.Can you tell me why?
Here's my evaluation function: I can make sure it's not wrong:

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import welch, resample
from utils_sig import hr_fft, butter_bandpass, normalize
import os


root_dir = "./results_cnn_pure/1/1"
for file in os.listdir(root_dir):
    
    if not file.endswith(".npy"):
        continue
    print(file)
    data = np.load(os.path.join(root_dir, file), allow_pickle=True)
    rppg_list = data.item().get('rppg_list')
    bvp_list = data.item().get('bvp_list')
    
    # 展平信号
    rppg = np.concatenate(rppg_list)
    # print(rppg.shape) # (1500,)
    bvp = np.concatenate(bvp_list)

    # 标准化信号
    rppg_norm = normalize(rppg)
    bvp_norm = normalize(bvp)
    

    # 计算信号长度和采样率
    signal_length = len(rppg_norm)
    fs = 30  # 假设采样率为 30Hz

    # 对标准化信号进行滤波
    rppg_filtered = butter_bandpass(rppg_norm, lowcut=0.6, highcut=4, fs=fs)
    bvp_filtered = butter_bandpass(bvp_norm, lowcut=0.6, highcut=4, fs=fs)

    # 计算预测心率和地面真值心率
    hr_rppg, rppg_psd, rppg_hr = hr_fft(rppg_filtered, fs=fs)
    hr_bvp, bvp_psd, bvp_hr = hr_fft(bvp_filtered, fs=fs)

    # 计算评估指标
    mae = np.mean(np.abs(rppg_filtered - bvp_filtered))
    mse = np.mean((rppg_filtered - bvp_filtered) ** 2)
    rmse = np.sqrt(mse)
    # print(np.corrcoef(rppg_filtered, bvp_filtered))
    r = np.corrcoef(rppg_filtered, bvp_filtered)[0, 1]

    # 绘制时域图和频谱图
    fig, (ax1, ax2) = plt.subplots(2, figsize=(20, 10))

    # 时域图
    time = np.arange(signal_length) / fs
    ax1.plot(time, rppg_filtered, label='Predicted Signal')
    ax1.plot(time, bvp_filtered, label='Ground Truth Signal')
    ax1.set_xlabel('Time (s)')
    ax1.set_ylabel('Amplitude')
    ax1.set_title('Predicted Signal vs Ground Truth Signal')
    ax1.legend()
    ax1.grid('on')

    # 频谱图
    ax2.plot(rppg_hr, rppg_psd, label='Predicted Signal')
    ax2.plot(bvp_hr, bvp_psd, label='Ground Truth Signal')
    ax2.set_xlabel('Heart Rate (bpm)')
    ax2.set_ylabel('Power Spectral Density')
    ax2.set_xlim([40, 200])
    ax2.set_title('Power Spectral Density of Predicted Signal vs Ground Truth Signal')
    ax2.legend()
    ax2.grid('on')

    # 显示评估指标和预测心率
    print(f"MAE: {mae:.4f}")
    print(f"MSE: {mse:.4f}")
    print(f"RMSE: {rmse:.4f}")
    print(f"Pearson's Correlation Coefficient (r): {r:.4f}")
    print(f"Predicted Heart Rate: {hr_rppg:.2f} bpm")
    print(f"Ground Truth Heart Rate: {hr_bvp:.2f} bpm")

    plt.show()

Trained weights of Contrast-phys+

Hi,
thanks for this wonderful work. i see the accuracy is much better then previous iteration of your method.
will u update this repo demo folder with latest trained weights(0%,60% & 100%) respectively.
Thanks.

HRV assessment details for UBFC-RPPG

Hello! I would like to ask you about the HRV evaluation details of UBFC-RPPG in Table 2. How did you transform the bvp label and rppg into various HRV indicators for evaluation? Is there code for that?
image

Why is performance poor on PURE data sets

For PURE, first, I combine the frame images into videos. Then I follow the same pre-processing procedures by using openface to get landmarks and crop faces to save each video into .h5 files, but the performance is poor on PURE data sets.The result is shown below: MAE and RMSE appear slightly normal, but why is the R coefficient so small or even zero?I can make sure that I didn't miswrite my evaluate function, because the same evaluate function works fine on the UBFC-rPPG dataset
d8f89b577332f576a93224bb53eda12
Here is my test.py file:

import numpy as np
import h5py
from torch import nn
import torch
from PhysNetModel import PhysNet
from utils_data import *
from utils_sig import *
from sacred import Experiment
from sacred.observers import FileStorageObserver
import json
from scipy.stats import pearsonr
from ViT import VideoViT
from ViT2 import TransformerImageResizer
from UniFormer import Uniformer
from CNNandTransformer import MyNet
from scipy import interpolate
import json

ex = Experiment('model_pred', save_git_info=False)

@ex.config
def my_config():
    e = 29 # the model checkpoint at epoch e
    train_exp_num = 3 # the training experiment number
    train_exp_dir = './results4cnn_pure/%d'%train_exp_num # training experiment directory
    
    time_interval = 10 # get rppg for 30s video clips, too long clips might cause out of memory

    ex.observers.append(FileStorageObserver(train_exp_dir))

    if torch.cuda.is_available():
        device = torch.device('cuda')
        torch.backends.cudnn.enabled = True
        torch.backends.cudnn.benchmark = True
    
    else:
        device = torch.device('cpu')

@ex.automain
def my_main(_run, e, train_exp_dir, device, time_interval):

    # load test file paths
    test_list = list(np.load(train_exp_dir + '/test_list.npy'))
    pred_exp_dir = train_exp_dir + '/%d'%(int(_run._id)) # prediction experiment directory

    with open(train_exp_dir+'/config.json') as f:
        config_train = json.load(f)

    model = PhysNet(config_train['S'], config_train['in_ch']).to(device).eval()
    
    
    model.load_state_dict(torch.load(train_exp_dir+'/epoch%d.pt'%(e), map_location=device)) # load weights to the model
    # model.load_state_dict(torch.load("./demo/model_weights.pt", map_location=device))

    @torch.no_grad()
    def dl_model(imgs_clip):
        # model inference
        img_batch = imgs_clip
        img_batch = img_batch.transpose((3,0,1,2))
        # 在img_batch前面新增了一个批量大小的维度(批量大小为1)
        img_batch = img_batch[np.newaxis].astype('float32')
        img_batch = torch.tensor(img_batch).to(device)

        rppg = model(img_batch)[:,-1, :] # (1, 5, T) -> (1, T)
        rppg = rppg[0].detach().cpu().numpy()
        return rppg
    
    @torch.no_grad()
    def read_wave(info, target_length):
        bvp = []
        hr = []
        for signal in info["/FullPackage"]:
            bvp.append(signal["Value"]["waveform"])
            hr.append(signal["Value"]["pulseRate"])
        
        T = len(bvp)
        gts = list()
        x_new = np.linspace(0, T - 1, num=target_length)  
        bvp_func = interpolate.CubicSpline(range(T), bvp)
        gts.append(bvp_func(x_new))
        hr_func = interpolate.CubicSpline(range(T), hr)
        gts.append(hr_func(x_new))

        return np.asarray(gts)

    
    for h5_path in test_list:
        h5_path = str(h5_path)
        # print(h5_path)
        with h5py.File(h5_path, 'r') as f:
            imgs = f['imgs']
            subject_name = os.path.basename(h5_path)[:-3]
            json_path = f"/share2/data/PURE/{subject_name}/{subject_name}.json"
            with open(json_path) as f:
                info = json.load(f)
                gts = read_wave(info, imgs.shape[0])
                # print(gts.shape)
            bvp = gts[0].reshape(-1, 1)
            # bvppeak = f['bvp_peak']
            fs = config_train['fs']
            
            print(subject_name, imgs.shape, bvp.shape)
            
            # duration表示秒数,fs表示frame per seccond 
            duration = np.min([imgs.shape[0], bvp.shape[0]]) / fs
            num_blocks = int(duration // time_interval)
            # 从整个视频当中截取出num_blocks个视频片段,这些片段之间是连续的(指从原视频当中截取的方式)
            rppg_list = []
            bvp_list = []
            # bvppeak_list = []
            for b in range(num_blocks):
                rppg_clip = dl_model(imgs[b*time_interval*fs:(b+1)*time_interval*fs])
                rppg_list.append(rppg_clip)

                bvp_list.append(bvp[b*time_interval*fs:(b+1)*time_interval*fs])
                # bvppeak_list.append(bvppeak[b*time_interval*fs:(b+1)*time_interval*fs])

            rppg_list = np.array(rppg_list)
            bvp_list = np.array(bvp_list)
            bvp_list = bvp_list.reshape(num_blocks, -1)
            print(num_blocks, rppg_list.shape, bvp_list.shape)
            # bvppeak_list = np.array(bvppeak_list)
            # results = {'rppg_list': rppg_list, 'bvp_list': bvp_list, 'bvppeak_list':bvppeak_list}
            results = {'rppg_list': rppg_list, 'bvp_list': bvp_list}
            np.save(pred_exp_dir+'/'+h5_path.split('/')[-1][:-3], results)
            

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.