發佈日期:

k-Fold Cross-Validation(交叉驗證)

一般所使用的方式 – Holdout method

這是一種非常基本且簡單的方法,我們將整個數據集分為兩部分,即訓練數據測試數據。顧名思義,我們在訓練數據上訓練模型,然後在測試集上進行評估。通常,訓練數據的大小設置為測試數據的兩倍以上,因此數據按70:30或80:20的比例進行分割。

在這種方法中,數據在分割之前首先被隨機洗牌。由於模型是在不同的數據點組合上訓練的,每次訓練時模型都會給出不同的結果,這可能是不穩定的原因。此外,我們永遠無法保證我們選擇的訓練集能夠代表整個數據集。

此外,當我們的數據集不是太大時,測試數據很可能包含一些我們丟失的重要信息,因為我們沒有在測試集上訓練模型。範例程式如下:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
import tensorflow as tf
  
# 建立圖片資料集
dataset = tf.keras.utils.image_dataset_from_directory(
    'image_directory',
    labels='inferred',
    class_names=None,
    label_mode='int'
)
  
# 計算資料集的大小
dataset_size = tf.data.experimental.cardinality(dataset).numpy()
  
# 計算訓練集和測試集的大小
test_size = int(0.2 * dataset_size)
train_size = dataset_size - test_size
  
# 將資料集分割為訓練集和測試集
train_dataset = dataset.take(train_size)
test_dataset = dataset.skip(train_size)

改善方法 – k-Fold Cross-Validation

K 折交叉驗證是改進的一種方法。這種方法保證了我們模型的分數不依賴於我們選擇訓練集和測試集的方式。將數據集分為 k 個子集,並將保留方法重複 k 次。讓我們分步驟完成這個過程:

  1. 將整個數據集隨機分成 k 個折疊(子集)
  2. 對於數據集中的每個折疊,在數據集的 k – 1 個折疊上構建模型。然後,測試模型以檢查第 k 次折疊的有效性
  3. 重複此操作,直到每個 k 重都作為測試集
  4. k 記錄的準確度的平均值稱為交叉驗證準確度,並將作為模型的性能指標。

因為它確保來自原始數據集的每個觀察結果都有機會出現在訓練和測試集中,所以與其他方法相比,這種方法通常會產生偏差較小的模型。如果我們的輸入數據有限,這是最好的方法之一。 

這種方法的缺點是訓練算法必須從頭開始重新運行 k 次,這意味著進行評估需要 k 倍的計算量。

分層k-Fold – Stratified K Fold Cross Validation

在分類問題上使用 K Fold 可能會很棘手。由於我們隨機打亂數據,然後將其劃分為折疊,因此我們可能會得到高度不平衡的折疊,這可能會導致我們的訓練出現偏差。例如,讓我們以某種方式得到一個折疊,其中大多數屬於一個類(例如正類),而只有少數屬於負類。這肯定會破壞我們的訓練,為了避免這種情況,我們使用分層進行分層折疊。

分層是重新排列數據的過程,以確保每次折疊都能很好地代表整體。例如,在每個類包含 50% 數據的二元分類問題中,最好對數據進行排列,使得在每個折疊中,每個類包含大約一半的實例。

下面這張圖,是在分類問題上使用K-Fold折疊的方式,會發現因為不同分類隨機分組導致每個分組的數量不一,而破壞訓練的狀況

資料狀況: 共三種資料,每一種70張,共210張,訓練集168張,測試集42張

而改使用StratifiedKFold去做資料分組之後,能看到訓練過程更加順利

範例程式

以下為使用Tensorflow加上sklearn.model_selection去做資料分組的測試程式

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import numpy as np
import pathlib
import tensorflow as tf
from sklearn.model_selection import LeaveOneOut,KFold
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import LabelEncoder
from sklearn.datasets import load_iris
 
img_path = 'dice3'
train_ds = tf.keras.utils.image_dataset_from_directory(
  img_path,
  seed=7,
  batch_size=32)
X = []
y = []
 
for images, labels in train_ds:
    X.append(images.numpy())
    y.append(labels.numpy())
 
X = np.concatenate(X, axis=0)
y = np.concatenate(y, axis=0)
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y)
 
def class_names(path):
    return np.array(sorted([item.name for item in pathlib.Path(path).glob('*') if
                            "LICENSE.txt" != item.name]))
def create_model():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Rescaling(1. / 255))
    model.add(tf.keras.layers.Conv2D(32, kernel_size=7, activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.Conv2D(64, kernel_size=5, activation='relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(64, activation='relu'))
    model.add(tf.keras.layers.Dense(len(class_names(img_path))))
    model.compile(
        optimizer='adam',
        loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
        metrics=['accuracy'])
    return model
 
def make_dataset(X_data,y_data,n_splits):
    def gen():
        for train_index, test_index in KFold(n_splits).split(X_data):
            X_train, X_test = X_data[train_index], X_data[test_index]
            y_train, y_test = y_data[train_index], y_data[test_index]
            yield X_train,y_train,X_test,y_test
 
    return tf.data.Dataset.from_generator(gen, (tf.float64,tf.float64,tf.float64,tf.float64))
 
dataset=make_dataset(X,y,5)
 
 
for X_train,y_train,X_test,y_test in dataset:
    print(len(X_train), len(y_train), len(X_test), len(y_test))
    model = create_model()
    model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_test, y_test))

若要改成使用分層K-Fold,則要使用sklearn.model_selection的StratifiedKFold

1
2
3
4
5
6
7
8
9
def make_dataset(X_data,y_data,n_splits):
    def gen():
        kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=123)
        for train_index, test_index in kfold.split(X_data, y_data):
            X_train, X_test = X_data[train_index], X_data[test_index]
            y_train, y_test = y_data[train_index], y_data[test_index]
            yield X_train,y_train,X_test,y_test
 
    return tf.data.Dataset.from_generator(gen, (tf.float64,tf.float64,tf.float64,tf.float64))

資料集長這樣

參考資料

發佈日期:

Optimization loop failed: Cancelled: Operation was cancelled

遇到的錯誤訊息

2023-06-14 16:32:39.652288: W tensorflow/core/data/root_dataset.cc:167] Optimization loop failed: Cancelled: Operation was cancelled

這個錯誤訊息代表TensorFlow 在優化過程中遇到了問題,並且操作被取消了。當操作被取消時,TensorFlow 無法完成所需的計算任務,因此無法產生期望的結果。具體來說,如果你在使用 TensorFlow 的 CPU 模式下運行一個優化循環,並且該循環被取消,以下情況可能發生:

  • 訓練過程中斷:如果你正在訓練一個模型,操作取消將導致訓練過程中止。你將無法完成整個訓練過程,無法獲得最終訓練好的模型。
  • 中斷的結果:在某些情況下,如果操作被取消,TensorFlow 可能會嘗試返回已經計算出的部分結果。這取決於具體的操作和中斷發生的時間點。然而,這些部分結果通常不是完整或不可用的。
  • 推斷錯誤:如果你正在使用模型進行推斷(如輸入一條文本獲取其分類),而操作被取消,你將無法獲得模型對輸入的預測結果。這可能導致你無法得知模型的輸出,或者輸出結果不完整、不准確。

為什麼會出現此錯誤

大部分的狀況是因為資源不足,導致運算被中斷

  • 計算資源不足:TensorFlow 在 CPU 模式下運行時,對於大型、複雜的模型或數據集,可能需要較長的時間來完成計算。如果計算資源有限,操作可能會被取消。
  • 內存不足:TensorFlow 在處理大型模型或數據時需要大量內存。如果內存不足,操作可能會被取消。
  • 配置錯誤:有時,TensorFlow 的配置可能不正確,導致操作無法成功完成。這可能包括錯誤的版本或依賴問題。

觀察電腦CPU與MEMORY使用狀況

要解決此問題,首先要先觀察是哪一部分出了問題,就需要在程式內去監控資源的使用。請參考此篇教學: How to get current CPU and RAM usage in Python

01
02
03
04
05
06
07
08
09
10
11
12
13
# Importing the library
import psutil
 
 
# Getting % usage of virtual_memory ( 3rd field)
if psutil.virtual_memory()[2] > 80:
    print(time.strftime("%Y-%m-%d_%H-%M-%S") + ' RAM memory % used:', psutil.virtual_memory()[2])
    # Getting usage of virtual_memory in GB ( 4th field)
    print('RAM Used (GB):', psutil.virtual_memory()[3]/1000000000)
 
cpu_usage = psutil.cpu_percent()
if cpu_usage > 80:
    print(time.strftime("%Y-%m-%d_%H-%M-%S") + ' The CPU usage is: ', cpu_usage)

解決 – 若是使用太多記憶體

在Tensorflow裡面有一篇討論串,滿多人都會遇到此問題,其中一位大大建議在GPU上提高可使用的內存,可解決此問題

1
2
gpus = tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(gpu, True)

討論串: Optimization loop failed: Cancelled: Operation was cancelled

另外也可以考慮設置合適的 batch_size、限制模型大小或減少數據集規模是一種在沒有 GPU 或內存受限的情況下控制內存使用的方法。下面是一些方法可以考慮:

  • 調整 batch_size:batch_size 是每次模型訓練時用於處理的樣本數量。較大的 batch_size 可能會導致內存不足。你可以嘗試降低 batch_size 的值,以減少內存需求。但要注意,較小的 batch_size 可能會影響訓練過程的穩定性和收斂速度。
  • 限制模型的大小:模型的大小直接影響內存使用。你可以嘗試以下方法來限制模型的大小:
    • 減少模型的層數或參數數量。
    • 使用輕量級模型架構,例如 MobileNet、SqueezeNet 等。
    • 使用模型剪枝技術,減少模型中冗餘參數的數量。
    • 使用低精度(如 16 位浮點數)表示模型的參數,以減少內存佔用。
  • 減少數據集規模:如果數據集過大而導致內存不足,你可以考慮以下方法來減少數據集的規模:
    • 隨機採樣:從原始數據集中隨機選擇一部分樣本作為子集進行訓練。確保採樣後的數據集仍具有代表性。
    • 數據集切片:將大型數據集切割成多個較小的部分,逐個部分進行訓練。可以使用交叉驗證等技術來利用所有數據並評估模型性能。

解決 – 若是使用太多CPU

可嘗試以下方法來減少 CPU 的消耗:

  • 減少數據的處理量:優化輸入數據的大小和復雜度,可以減少 CPU 的負載。例如,可以嘗試縮小圖像尺寸、減少輸入序列的長度或降低數據的維度。
  • 使用批處理預測:通過將多個樣本組成一個批次進行預測,可以減少每個樣本的計算和內存佔用。 Model.predict() 方法默認可以處理批量輸入,你可以將多個樣本一起傳遞給該方法進行預測。
  • 並行處理:如果你的系統支持多線程或多進程,並且你的模型和數據可以進行並行處理,可以使用並行化方法來提高 CPU 的利用率。例如,使用 Python 的 multiprocessing 模塊或 TensorFlow 的 tf.data.Dataset 的並行化功能。
  • 優化模型:檢查你的模型結構和計算圖,看是否有可以優化的部分。可能存在一些冗餘計算或可以簡化的操作。你可以嘗試使用 TensorFlow 的圖優化工具,如 tf.function 和 tf.autograph,來加速模型的計算過程。
  • 使用更高效的庫或算法:某些情況下,使用其他庫或算法可能比 TensorFlow 更高效。你可以嘗試使用其他機器學習庫,如 PyTorch 或 Scikit-learn,並根據你的需求選擇更適合的庫和算法。
  • 升級硬件:如果你的計算機配置允許,可以考慮升級到更強大的 CPU 或添加更多的 CPU 核心。這樣可以提高系統的並行處理能力和整體性能。
發佈日期:

計算兩個點之間的直線距離

使用math.hypot

math.hypot 是 Python 內置的數學模塊 math 中的函數。它接受兩個參數,分別代表兩點的 x 和 y 坐標差值,然後返回它們的歐幾里德距離(即直線距離)。

1
2
3
4
5
6
7
import math
 
x1, y1 = 1, 2
x2, y2 = 3, 4
 
distance = math.hypot(x2 - x1, y2 - y1)
print(distance)

使用np.sqrt

np.sqrt 是 NumPy 庫中的函數,用於計算給定數值的平方根。要使用 np.sqrt 計算兩點之間的距離,你需要首先計算兩點在 x 和 y 坐標軸上的差值的平方和,然後將它們相加,再使用 np.sqrt 對結果進行平方根運算。

1
2
3
4
5
6
7
import numpy as np
 
x1, y1 = 1, 2
x2, y2 = 3, 4
 
distance = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
print(distance)
發佈日期:

對輪廓的點做旋轉計算

使用角度的正弦和餘弦函數,將長方形的寬度和高度乘以正確的係數,以獲得旋轉後的角點座標。

下面為一個將一個長方形的四個點作45度旋轉的簡單範例:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import cv2
import numpy as np
import math
 
base_x = 100
base_y = 100
width = 100
height = 50
angle = 45
 
# 計算長方形中心點座標
center_x = base_x + (width // 2)
center_y = base_y + (height // 2)
 
# 將角度轉換為弧度
angle_rad = math.radians(angle)
 
# 計算長方形四個角的相對座標
cos_val = math.cos(angle_rad)
sin_val = math.sin(angle_rad)
x = width / 2
y = height / 2
 
# 計算四個角點座標
point1 = (int(center_x - x * cos_val + y * sin_val), int(center_y - x * sin_val - y * cos_val))
point2 = (int(center_x + x * cos_val + y * sin_val), int(center_y + x * sin_val - y * cos_val))
point3 = (int(center_x + x * cos_val - y * sin_val), int(center_y + x * sin_val + y * cos_val))
point4 = (int(center_x - x * cos_val - y * sin_val), int(center_y - x * sin_val + y * cos_val))
 
print("Point 1:", point1)
print("Point 2:", point2)
print("Point 3:", point3)
print("Point 4:", point4)
 
# 創建空白影像
image = np.zeros((500, 500, 3), dtype=np.uint8)
 
# 轉換座標為Numpy陣列
pts = np.array([point1, point2, point3, point4], dtype=np.int32)
 
# 繪製多邊形
cv2.polylines(image, [pts], True, (0, 255, 0), thickness=2)
 
# 顯示結果
cv2.imshow('Rectangle', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

顯示結果如下

發佈日期:

點到多邊形的最短距離

cv2.pointPolygonTest是OpenCV中的一個函數,用於計算點到多邊形的最短距離或點是否在多邊形內。

函數的語法如下:

1
_, intersection = cv2.pointPolygonTest(rect3, tuple(line1_midpoint), measureDist=False)
  • contour:多邊形的輪廓,可以是Numpy陣列或OpenCV的輪廓物件。
  • point:要計算距離的點,通常是一個(x, y)座標元組。
  • measureDist:指定是否計算點到多邊形的最短距離。如果為True,則返回距離值;如果為False,則返回一個整數值表示點的位置關係:正數表示點在多邊形內部、負數表示點在多邊形外部、0表示點在多邊形邊界上。

相關函數請參考: cv2.distanceTransform

另外要畫出多邊形可使用cv2.polylines,如以下範例

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
import cv2
import numpy as np
 
# 定義長方形的四個角點
rect1 = np.array([[100, 100], [300, 100], [300, 200], [100, 200]])
 
# 創建空白影像
image = np.zeros((500, 500, 3), dtype=np.uint8)
 
# 繪製長方形
cv2.polylines(image, [rect1], True, (0, 255, 0), thickness=2)
 
# 顯示結果
cv2.imshow('Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
發佈日期:

使用OpenCV將圖形轉正

旋轉圖片的方法

若是單純只是要把圖片做角度的旋轉,可以直接使用OpenCV 的 cv2.rotate() 函数。可按指定的方向旋轉圖像。如下:

01
02
03
04
05
06
07
08
09
10
11
12
import cv2
 
# 讀取圖像
image = cv2.imread('your_image.jpg')
 
# 將圖像旋轉90度
rotated_image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
 
# 顯示旋轉後的圖像
cv2.imshow('Rotated Image', rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

翻轉圖片的方法

cv2.flip() 是 OpenCV 中用於圖像翻轉的函數。它可以在水平、垂直或兩個方向上翻轉圖像。該函數接受三個參數:輸入圖像、翻轉的模式和輸出圖像的可選參數。

1
dst = cv2.flip(src, flipCode[, dst])

flipCode:翻轉的模式。可以是以下值之一:

  • 0:水平翻轉(沿著垂直軸翻轉)。
  • 1:垂直翻轉(沿著水平軸翻轉)。
  • -1:同時在水平和垂直方向上翻轉。

cv2.flip() 函數和 cv2.rotate() 函數都可以用於實現圖像的旋轉和翻轉,但它們的效果是不同的。

cv2.flip() 函數可以在水平和垂直方向上翻轉圖像,包括水平翻轉、垂直翻轉和同時在水平和垂直方向上翻轉。例如,使用 cv2.flip(image, -1) 可以同時在水平和垂直方向上翻轉圖像。

cv2.rotate() 函數用於對圖像進行旋轉。通過指定旋轉的角度和旋轉中心點,可以實現不同角度的旋轉。例如,使用 cv2.rotate(image, cv2.ROTATE_180_CLOCKWISE) 可以將圖像順時針旋轉180度。

雖然cv2.flip(image, -1)cv2.rotate(image, cv2.ROTATE_180_CLOCKWISE) 可以實現類似的效果,將圖像翻轉或旋轉180度,但它們的內部操作是不同的。 cv2.flip() 是基於軸對稱翻轉實現的,而 cv2.rotate() 是基於旋轉變換實現的。

針對形狀做角度校正

在許多圖像偵測的狀況,我們仍然會需要針對物件去做旋轉,首先我們一定是先用cv2.findContours取得輪廓,然後取得該物件輪廓的角度。這邊很重要的,就是要取得物件輪廓的角度,要取得角度,首先就要先去做輪廓擬合(請參考: OpenCV裡面形狀擬合的幾種方法)。

這邊我大推使用橢圓去做輪廓擬合並且取得軸心的角度,為什麼呢? 雖然cv2.minAreaRect() 可計算最小擬合矩形,但是這個矩形會非常容易受到輪廓的些微影響而改變擬合的方式,例如以下圖為例,就有可能有黑框、紅色框兩種的最小擬合矩形(會視當下輪廓取得的細微變化而改變)。也因此所取得的角度會非常多變,後續的辨識也會更困難

但是使用最小擬合橢圓,對於像上面這種左右、上下為對稱,但是長寬不同的形狀來說,非常適合使用最小擬合橢圓cv2.fitEllipse(),使用範例如下

01
02
03
04
05
06
07
08
09
10
11
12
13
import cv2
 
image = cv2.imread('./333_2023-06-08_19-57-30.jpg')
canny = cv2.Canny(image , 50, 250)
cnts, hier = cv2.findContours(canny , cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 執行最小橢圓擬合
ellipse = cv2.fitEllipse(cnts[0])
(center, axes, angle) = ellipse
cv2.ellipse(image, ellipse, (0, 255, 0), 2)
# 顯示結果
cv2.imshow('image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

上面可以看到(center, axes, angle) = ellipse這邊的angle就是所偵測到的輪廓的角度,接著可以用cv2.warpAffine方法將圖像轉正

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
def rotatedDice(image, cnt):
    # 取得最小擬合橢圓並對圖像做翻轉
    ellipse = cv2.fitEllipse(cnt)
    (center, axes, angle) = ellipse
    angle = angle + 90
    rotation_matrix = cv2.getRotationMatrix2D(tuple(center), angle, 1)
    image = cv2.warpAffine(image, rotation_matrix,(image.shape[1], image.shape[0]))
    # 計算裁切位置
    mark = np.zeros_like(image)
    cv2.drawContours(mark, [cnt], 0, (255, 255, 255), -1)
    mark = cv2.warpAffine(mark, rotation_matrix,(mark.shape[1], mark.shape[0]))
    mark = cv2.cvtColor(mark, cv2.COLOR_RGB2GRAY)
    cnts, hier = cv2.findContours(mark, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    x, y, w, h = cv2.boundingRect(cnts[0])
    matting_result = image[y:y+h,x:x+w,:]
    return matting_result

從上面,我們使用cv2.warpAffine來做圖片角度的校正,warpAffine裡面有要輸入一個旋轉的矩陣的參數,在上面的範例,我們使用cv2.getRotationMatrix2D,這個參數是單純做形狀旋轉,但是在真實的世界當中,大部分3D的角度轉換也會帶有著深度的轉換,如下圖

這時候就會需要使用cv2.getAffineTransform來取得這個旋轉矩陣

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import cv2
import numpy as np
 
# 定義三個點的坐標
point1 = (106, 92)
point2 = (28, 91)
point3 = (154, 33)
 
# 定義旋轉角度
rotation_angle = -45
 
# 創建一個空白圖像
image = np.zeros((500, 500), dtype=np.uint8)
 
# 在圖像上繪製三角形
cv2.drawContours(image, [np.array([point1, point2, point3])], 0, (255), thickness=2)
 
# 計算旋轉中心
center = np.mean([point1, point2, point3], axis=0)
 
# 構建旋轉矩陣
rotation_matrix = cv2.getRotationMatrix2D(tuple(center), rotation_angle, 1)
 
# 對整個圖像進行旋轉
rotated_image = cv2.warpAffine(image, rotation_matrix, (image.shape[1], image.shape[0]))
 
# 顯示旋轉後的圖像
cv2.imshow("Rotated Image", rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
發佈日期:

限制ffmpeg初始連接的時間

ffmpeg中的analyzeduration和probesize

在FFmpeg中,-analyzeduration和-probesize是用於設置媒體分析的參數。

-analyzeduration參數用於指定分析媒體文件的持續時間。當你在FFmpeg中打開一個媒體文件時,它需要一些時間來分析文件的內容,以確定其格式、編解碼器和其他相關的信息。這個參數設置了分析的時間長度。較長的-analyzeduration值可能會導致更準確的分析結果,但同時也會增加打開文件的時間。預設值為5,000,000微秒(5秒)。

-probesize參數用於指定分析媒體文件時讀取的數據大小。當FFmpeg分析媒體文件時,它會從文件中讀取一些數據並進行分析。這個參數設置了從媒體文件中讀取的數據大小。較大的-probesize值可能會導致更準確的分析結果,但同時也會增加分析的時間和記憶體使用量。預設值為50,000字節。

前置metadata

播放器在網絡點播場景下去請求MP4 視頻數據,需要先獲取到文件的metadata,解析出該文件的編碼、幀率等信息後才能開始邊下邊播。如果MP4 的metadata 數據塊被編碼在文件尾部,這種情況會導致播放器只有下載完整個文件後才能成功解析並播放這個視頻。對於這種視頻,我們最好能夠在服務端將其重新編碼,將metadata 數據塊轉移到靠近文件頭部的位置,保證播放器在線請求時能較快播放。比如FFmpeg 的下列命令就可以支持這個操作:

1
ffmpeg -i bad.mp4 -movflags faststart good.mp4

控制讀取的數據量大小

在外部可以通過設置 probesize 和 analyzeduration 兩個參數來控制該函數讀取的數據量大小和分析時長為比較小的值來降低 avformat_find_stream_info 的耗時,從而優化播放器首屏秒開。但是,需要注意的是這兩個參數設置過小時,可能會造成預讀數據不足,無法解析出碼流信息,從而導致播放失敗、無音頻或無視頻的情況。所以,在服務端對視頻格式進行標準化轉碼,從而確定視頻格式,進而再去推算 avformat_find_stream_info 分析碼流信息所兼容的最小的 probesize 和analyzeduration,就能在保證播放成功率的情況下最大限度地區優化首屏秒開。

probesize 和 analyzeduration太短的可能影響


如果將-probesize和-analyzeduration設置得太短,可能會導致以下問題:

  • 不準確的媒體分析:probesize和analyzeduration參數用於指定媒體分析的數據大小和時間長度。如果這兩個值設置得太短,FFmpeg可能無法讀取足夠的數據或分析足夠長的時間,從而導致分析結果的不準確性。這可能會影響到媒體文件的正確解碼、格式識別和相關信息的獲取。
  • 遺漏關鍵信息:媒體文件中的關鍵信息通常在文件的早期部分或特定位置。如果probesize和analyzeduration設置得太短,FFmpeg可能無法讀取到這些關鍵信息,進而影響解碼、播放或處理過程的正確性和完整性。
  • 性能問題:probesize和analyzeduration參數的值也會影響處理媒體文件所需的時間和資源。如果值設置得太短,FFmpeg可能需要更頻繁地從媒體文件中讀取數據或進行分析,增加了I/O操作和CPU負載,進而導致性能下降。
發佈日期:

為影片產生會議紀錄及重點擷取

將影片轉為MP3

先照這篇的方式安裝FFMPEG,接著就可以使用ffmpeg將影片轉成mp3檔案

1
ffmpeg -i input.mp4 -vn -acodec libmp3lame output.mp3

在上述命令中,input.mp4是輸入的MP4文件路徑,output.mp3是輸出的MP3文件路徑。

從語音檔案使用AI提取文字

這個功能在WORD就有了,若是沒有WORD,GOOGLE文件也有相似的聽寫功能,以下為我使用Office內建聽寫功能的示範

先使用轉錄功能

接著選擇輸入語言為台灣國語,並上傳剛剛擷取出來的mp3檔案

選擇完檔案會開始上傳MP3並且擷取音檔內的文字,這也是為什麼一開始我會希望將mp4轉成mp3,因為含影像的檔案較大,純音檔較小,上傳較小的檔案這邊所花費的時間會少一點

當節錄文字完成後,選擇將文字加到檔案內,就會出現如下的語音謄錄文字

讓文字更易懂

使用工具: ChatGPT

一直到這邊所產生的文字,都很不容易讓人理解,因為所擷取出的文字很容易會有錯別字,例如: 【視障小孩】可能會被聽寫成【師丈小孩】,根本意義完全不同,讓人難以理解。

但是ChatGPT對於理解這樣的錯別字,比對上下文去猜出正確辭意的能力頗強,所以可以使用ChatGPT請他幫忙整理內容

例如上面的文字GTP所整理出的內容如下

接著再重複使用上面產生的內容,請GPT產生摘要、標題,我們只需要作內容審核、確認、修正即可,可以大幅節省人力唷!

另外,對CHATGPT所下的指令也會影響到產出,例如上面我使用【順成文章】的指令,所以最後面CHATGPT就自己唬爛了一些不相關的內容(甚麼不僅僅是個人問題之類的老師根本沒有講)。這時候就可以改使用【順過讓文字更好讀】這樣的指令,就比較不會產生不相關的內容。

建議可以多嘗試幾種不同的指令,直接針對他所整理過的不滿意的方向請她重新整理,直到CHATGPT給出較滿意的產出後,再自行做驗證/整理

發佈日期:

使用OBS做會議錄影

使用OBS把線上會議錄起來

若是線上會議,推薦使用OBS可以很方便的對所有類型的會議做錄影。

這邊是OBS的下載位置: https://obsproject.com/

OBS可以擷取視窗螢幕,只要是可以在桌面上執行的軟體,都可以從【+】 ->【 Window Capture】去獲取該程式的畫面並放置於OBS畫面上。使用這個方式錄影的優點是,我們可以在錄影的時候同時使用其他的軟體,並且不會被錄到除了你所選擇的應用程式以外的電腦上的操作,OBS只會持續的錄該程式的畫面。

例如下面這樣子就只會錄到所選擇的Chrome的視窗,我們在電腦上回Line訊息、開別的Chrome瀏覽網頁,都不會被錄進去

這樣的方式在Window上直接就會可以錄到畫面+聲音,但是若你的電腦是Mac,則需要另外做額外的設定,才能夠讓OBS取得電腦內部的聲音。這邊為教學文章: https://www.casper.tw/life/2021/06/13/mac-reacord-by-soundflower/

開始錄影

在上面設定完之後,按下右下的【開始錄製】並在會議結束時按下【結束錄製】,就可以在本機>影片找到錄好的影片,檔名會是錄影的時間

安裝FFMPEG

接著則要做影片的剪裁+合成,以下是一些簡單的影片操作指令

首先,下載別人已經Build好的ffmpeg: https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-5.1.2-full_build.7z

接著,把ffmpeg.exe丟到系統的路徑裡面,如C:\Windows\System32,或者將ffmpeg的路徑加到環境變數的Path裡面,讓系統能夠找的到ffmpeg的位置。

驗證方式: 開啟命令提示字元,輸入ffmpeg,如果能出現以下畫面,代表設定成功

做一些簡單的影片操作

擷取影片片段

1
ffmpeg -i input.mp4 -ss 00:12 -to 00:15:30 -c:v copy -c:a copy output.mp4

在上述命令中,input.mp4是輸入的視頻文件路徑,output.mp4是輸出的視頻文件路徑。 00:12表示起始時間,00:15:30表示結束時間。

刪除影片片段

1
ffmpeg -i input.mp4 -filter_complex "[0:v]trim=0:4,setpts=PTS-STARTPTS[v1]; [0:v]trim=8,setpts=PTS-STARTPTS[v2]; [v1][v2]concat=n=2:v=1:a=0" -c:v libx264 -preset veryfast -crf 18 output.mp4

在上述命令中,input.mp4是輸入的視頻文件路徑,output.mp4是輸出的視頻文件路徑。 0:4表示要刪除的起始時間範圍,8表示要刪除的結束時間。

合併多個影片檔案

  • 創建一個文本文件,例如input.txt,其中包含要合併的視頻文件的列表。每行包含一個視頻文件的路徑,例如:
1
2
3
file 'video1.mp4'
file 'video2.mp4'
file 'video3.mp4'
  • 將文件內所寫的影片使用ffmpeg合併為一個影片
1
ffmpeg -f concat -safe 0 -i input.txt -c copy output.mp4

為影片加上浮水印

1
ffmpeg -i input.mp4 -i watermark.png -filter_complex "overlay=W-w-10:H-h-10" -c:a copy output.mp4

在上述命令中,input.mp4是輸入的視頻文件路徑,watermark.png是水印圖片文件的路徑,output.mp4是輸出的帶有水印的視頻文件路徑。

-filter_complex “overlay=W-w-10:H-h-10″:使用filter_complex選項應用複雜的過濾器。 overlay過濾器用於將水印圖像疊加在視頻上。 W-w-10表示水印圖像的水平位置,H-h-10表示水印圖像的垂直位置。在這個示例中,水印位於視頻的右下角,並與邊緣保持10像素的間距。

發佈日期:

Ubuntu 18.04的apt update更新失敗

錯誤訊息

W: Failed to fetch http://repo.mysql.com/apt/ubuntu/dists/bionic/InRelease The following signatures couldn’t be verified because the public key is not available: NO_PUBKEY 467B942D3A79BD29 W: Some index files failed to download. They have been ignored, or old ones used instead.

問題原因

這個錯誤訊息是說您的系統在嘗試從 http://repo.mysql.com/apt/ubuntu/dists/bionic/ 的軟體庫中下載軟體索引時發生了問題。這可能是由於網路連線中斷或是軟體庫網址變更所導致。

訊息中提到的 “NO_PUBKEY 467B942D3A79BD29” 表示您的系統沒有此軟體庫的公鑰,導致無法驗證下載的軟體索引是否是正確的。

解決方案

要解決這個問題,您可以試著更新系統的軟體庫並且安裝相關的公鑰。在終端機中執行以下指令:

1
2
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 467B942D3A79BD29
sudo apt-get update

這個指令會從 Ubuntu 的公鑰伺服器中下載指定的公鑰,並且更新系統的軟體庫。完成後,您可以再次嘗試安裝所需的軟體,看看問題是否已經解決了。