發佈日期:

取得輪廓的中心點

以下為範例程式:

import cv2

# 讀取圖像,並將其轉換為灰度圖像
img = cv2.imread("image.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 進行閾值處理,以便更好地找到輪廓
ret, thresh = cv2.threshold(gray, 127, 255, 0)

# 查找輪廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 繪製輪廓,以便在圖像上進行視覺化
cv2.drawContours(img, contours, -1, (0, 255, 0), 3)

# 計算輪廓的中心點
if len(contours) > 0:
    # 取最大的輪廓
    c = max(contours, key=cv2.contourArea)
    # 計算輪廓的矩
    M = cv2.moments(c)
    # 計算中心點座標
    center_x = int(M["m10"] / M["m00"])
    center_y = int(M["m01"] / M["m00"])
    # 繪製中心點,以便在圖像上進行視覺化
    cv2.circle(img, (center_x, center_y), 5, (255, 0, 0), -1)

# 顯示圖像
cv2.imshow("image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
發佈日期:

如何分割黏在一起的撲克牌

範例說明

本文為參考下面的文章:
Image Segmentation with Distance Transform and Watershed Algorithm

這篇文章是OpenCV官方網站上的一篇教程,介紹了如何使用distanceTransform函數進行圖像分割。在這篇教程中,作者首先介紹了distanceTransform函數的基本概念和用法,然後通過一個實例演示了如何使用distanceTransform函數對圖像進行分割。

範例程式碼

以下為程式範例

from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse
import random as rng
rng.seed(12345)
parser = argparse.ArgumentParser(description='Code for Image Segmentation with Distance Transform and Watershed Algorithm.\
    Sample code showing how to segment overlapping objects using Laplacian filtering, \
    in addition to Watershed and Distance Transformation')
parser.add_argument('--input', help='Path to input image.', default='cards.png')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))
if src is None:
    print('Could not open or find the image:', args.input)
    exit(0)
# Show source image
cv.imshow('Source Image', src)
src[np.all(src == 255, axis=2)] = 0
# Show output image
cv.imshow('Black Background Image', src)
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
# do the laplacian filtering as it is
# well, we need to convert everything in something more deeper then CV_8U
# because the kernel has some negative values,
# and we can expect in general to have a Laplacian image with negative values
# BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
# so the possible negative number will be truncated
imgLaplacian = cv.filter2D(src, cv.CV_32F, kernel)
sharp = np.float32(src)
imgResult = sharp - imgLaplacian
# convert back to 8bits gray scale
imgResult = np.clip(imgResult, 0, 255)
imgResult = imgResult.astype('uint8')
imgLaplacian = np.clip(imgLaplacian, 0, 255)
imgLaplacian = np.uint8(imgLaplacian)
#cv.imshow('Laplace Filtered Image', imgLaplacian)
cv.imshow('New Sharped Image', imgResult)
bw = cv.cvtColor(imgResult, cv.COLOR_BGR2GRAY)
_, bw = cv.threshold(bw, 40, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow('Binary Image', bw)
dist = cv.distanceTransform(bw, cv.DIST_L2, 3)
# Normalize the distance image for range = {0.0, 1.0}
# so we can visualize and threshold it
cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX)
cv.imshow('Distance Transform Image', dist)
_, dist = cv.threshold(dist, 0.4, 1.0, cv.THRESH_BINARY)
# Dilate a bit the dist image
kernel1 = np.ones((3,3), dtype=np.uint8)
dist = cv.dilate(dist, kernel1)
cv.imshow('Peaks', dist)
dist_8u = dist.astype('uint8')
# Find total markers
_, contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# Create the marker image for the watershed algorithm
markers = np.zeros(dist.shape, dtype=np.int32)
# Draw the foreground markers
for i in range(len(contours)):
    cv.drawContours(markers, contours, i, (i+1), -1)
# Draw the background marker
cv.circle(markers, (5,5), 3, (255,255,255), -1)
markers_8u = (markers * 10).astype('uint8')
cv.imshow('Markers', markers_8u)
cv.watershed(imgResult, markers)
#mark = np.zeros(markers.shape, dtype=np.uint8)
mark = markers.astype('uint8')
mark = cv.bitwise_not(mark)
# uncomment this if you want to see how the mark
# image looks like at that point
#cv.imshow('Markers_v2', mark)
# Generate random colors
colors = []
for contour in contours:
    colors.append((rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)))
# Create the result image
dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8)
# Fill labeled objects with random colors
for i in range(markers.shape[0]):
    for j in range(markers.shape[1]):
        index = markers[i,j]
        if index > 0 and index <= len(contours):
            dst[i,j,:] = colors[index-1]
# Visualize the final image
cv.imshow('Final Result', dst)
cv.waitKey()

distanceTransform

distanceTransform函數是OpenCV中的一個函數,用於計算圖像中每個非零點到最近背景像素的距離。distanceTransform函數的第二個Mat矩陣參數dst保存了每個點與最近的零點的距離信息,圖像上越亮的點,代表了離零點的距離越遠。在這篇文章中,作者通過一個實例演示了如何使用distanceTransform函數對圖像進行分割。

在這個實例中,作者首先讀取了一張灰度圖像,然後使用threshold函數對圖像進行二值化處理。接著,作者使用distanceTransform函數計算了圖像中每個非零點到最近背景像素的距離,並將結果保存在了一個Mat矩陣中。最後,作者使用threshold函數對Mat矩陣進行二值化處理,得到了一張分割後的圖像。

需要注意的是,在使用distanceTransform函數時,需要先將圖像進行二值化處理。此外,在計算距離時,可以選擇歐氏距離、L1距離或L-infinity距離等不同的計算方式。

處理的過程圖片





其他參考資料

OpenCV C++/Obj-C: Advanced square detection

發佈日期:

拆分相黏的正方形

原始圖片

在寫純OpenCV的圖像辨識上,最困難的是當要找尋的目標的邊界因為模糊或相黏,而無法抓出正確的邊界的狀況
因為一般使用OpenCV做圖像辨識,我們都會需要先抓到例如說畫面中的某種色塊、再找尋某種符合某條件的形狀之類的
例如以修圖軟體而言,可能會需要先抓取膚色,然後轉換膚色的灰度階層取得面部的高低起伏,再根據灰度階層去抓取符合某種形狀的高低(如鼻子)或顏色差(如嘴巴、眼睛)

也因此,抓取正確的形狀在純粹的圖像辨識(沒有機器學習)的狀況下非常重要,而拆分相黏的形狀(如手放在臉前面),仍然要正確的辨識目標物件,也成了圖像辨識的一大挑戰
關於這一系列的其他文章,請見:

以下為這次我們要挑戰的目標,就是將這兩個黏在一起的正方形拆分為兩個正方形

解決問題的思考方向

首先我先參考官網的分水嶺演算法介紹:Image Segmentation with Watershed Algorithm
這邊的狀況和我們的需求很類似,都是將相黏的物件拆分開來

下面是在stackoverflow裡一位大大分享的他使用分水嶺演算法的範例程式:

from scipy.ndimage import label
import cv2
import numpy
def segment_on_dt(img):
    dt = cv2.distanceTransform(img, cv2.DIST_L2, 3) # L2 norm, 3x3 mask
    dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(numpy.uint8)
    dt = cv2.threshold(dt, 100, 255, cv2.THRESH_BINARY)[1]
    lbl, ncc = label(dt)

    lbl[img == 0] = lbl.max() + 1
    lbl = lbl.astype(numpy.int32)
    cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), lbl)
    lbl[lbl == -1] = 0

在這篇文章裡有幾個函數需要我們去理解:

cv.distanceTransform

OpenCV的distanceTransform是一個圖像處理功能,可以計算圖像中每個像素到最近的零值像素之間的歐幾里德距離。distanceTransform功能可以在圖像分割、形狀檢測、物體識別等應用中使用。
在OpenCV中,distanceTransform有三種不同的實現方式:cv2.DIST_L1、cv2.DIST_L2和cv2.DIST_C。cv2.DIST_L1使用曼哈頓距離,cv2.DIST_L2使用歐幾里德距離,而cv2.DIST_C使用切比雪夫距離。
使用distanceTransform功能需要先將圖像二值化,然後計算圖像中每個像素到最近的零值像素之間的距離。distanceTransform返回的結果是一個浮點型的圖像,每個像素值表示該像素到最近的零值像素之間的距離。

以下是distanceTransform的Python程式碼示例:

import cv2
import numpy as np

img = cv2.imread('image.jpg', 0)
ret,thresh = cv2.threshold(img,127,255,0)
dist_transform = cv2.distanceTransform(thresh,cv2.DIST_L2,5)

以下為對上圖做cv.distanceTransform的結果

cv.threshold

將距離變換圖像dt進行歸一化處理,將其轉換成0-255之間的整數型圖像,並使用threshold函數將其二值化,生成一個二值化圖像dt。這個二值化圖像dt中的像素值只有0和255兩種,用來表示圖像中物體和背景之間的分界線。
在這邊我使用了0.9*dist_transform.max()來做為閥值,確認兩個方形之間可以不相連

ret, sure_fg = cv.threshold(dist_transform,0.9*dist_transform.max(),255,0)

結果如下:

cv2.connectedComponents

在圖像處理和計算機視覺中,常常需要將圖像分割成多個不同的區域,然後對每個區域進行不同的分析和處理。圖像分割的第一步就是對圖像進行連通區域標記,將相連的像素點標記為同一個區域,以便後續處理。

程式碼使用OpenCV中的label函數對二值化圖像dt進行連通區域標記,生成一個標記圖像lbl和連通區域數量ncc。標記圖像lbl中的每個像素點都標記了其所屬的連通區域編號。

OpenCV中提供了幾個函數可以實現連通區域標記,其中最常用的是cv2.connectedComponents和cv2.connectedComponentsWithStats函數,這些函數會將每個連通區域分配一個唯一的標籤(編號),並返回每個區域的一些統計信息,如面積、重心等。

scipy.ndimage.labelcv2.connectedComponents都是對二值化圖像中的連通區域進行標記的函數,但在實現和用法上有所不同。

scipy.ndimage.label是Scipy中的一個函數,用於對二值化圖像進行連通區域標記,它的使用方式如下:

from scipy.ndimage import label
labels, num_features = label(binary_image)

其中,binary_image是二值化的圖像,labels是與原始圖像大小相同的數組,其中每個像素點都標記了其所屬的連通區域編號,num_features是圖像中連通區域的數量。

cv2.connectedComponents是OpenCV中的一個函數,用於對二值化圖像進行連通區域標記,它的使用方式如下:

num_labels, labels = cv2.connectedComponents(binary_image)

binary_image是二值化的圖像,num_labels是圖像中連通區域的數量,labels是與原始圖像大小相同的數組,其中每個像素點都標記了其所屬的連通區域編號。

兩個函數的返回值不同,scipy.ndimage.label返回的是每個像素點所屬的連通區域編號,而cv2.connectedComponents返回的是每個連通區域的編號。另外,cv2.connectedComponents還可以通過修改設置參數來指定標記的種類,例如指定為4或8連通等。

cv2.watershed

程式碼將標記圖像lbl轉換成整數型數組,並使用OpenCV中的watershed函數進行分水嶺分割,生成一個分割圖像lbl。分割圖像lbl中的像素點表示原始圖像中的每個像素點所屬的區域編號。

cv2.watershed() 是 OpenCV 中的一種分割算法,通常用於分割圖像中的目標物體。

在使用該函數時,需要先對輸入的圖像進行預處理,以便生成一組用於分割的初始標記。這通常可以通過在原始圖像中標記前景和背景像素來實現。然後,可以將這些標記傳遞給 cv2.watershed() 函數進行分割。該函數會根據標記和圖像的梯度信息來確定目標物體的邊界,將其分割為不同的區域。

函數的語法如下:

markers = cv2.watershed(img, markers)

img 是輸入圖像,markers 是與輸入圖像相同大小的標記矩陣。在函數執行完畢後,markers 矩陣中每個像素的值將被設置為其所屬的分割區域的標記值。
需要注意的是,cv2.watershed() 函數是一個原地操作,即它會修改傳遞給它的標記矩陣,而不是返回一個新的矩陣。因此,在調用該函數之前,最好複製一份原始標記矩陣以備份。
cv2.watershed() 函數是一種基於圖像分水嶺的分割算法,它可以對灰度圖像進行分割,將圖像中的前景和背景分開。該算法的分割結果是基於圖像梯度的變化來進行分割的,因此不能直接實現直線分割。

# 應用watershed算法進行圖像分割
    markers = cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), markers)
    # 根據標記將圖像分成不同的部分
    img[markers == -1] = 0

結果如下:

完整用法範例

import cv2
import numpy as np

# 創建一個黑白圖像
img = np.zeros((500, 500), dtype=np.uint8)
cv2.rectangle(img, (100, 100), (200, 200), 255, -1)
cv2.rectangle(img, (150, 150), (250, 250), 255, -1)

# 求出距離變換圖像
dt = cv2.distanceTransform(img, cv2.DIST_L2, 3)
dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(np.uint8)
# 閾值分割得到前景和背景
ret, thresh = cv2.threshold(dt, 0.8*dt.max(),255,0)
thresh = cv2.dilate(thresh, None, iterations=4)
unknown = cv2.subtract(img,thresh)
# 得到標記圖像,每個區域用不同的正整數標記
ret, markers = cv2.connectedComponents(thresh)
markers = markers+1
markers[unknown==255] = 0
# 應用watershed算法進行圖像分割
markers = cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), markers)
# 根據標記將圖像分成不同的部分
img[markers == -1] = 0

# 顯示結果
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
發佈日期: 1 則留言

如何檢測畫面中可能的正方形

最簡單-使用findContours

OpenCV 中有一個名為 findContours 的函數,可以用來查找圖像中的輪廓。一旦你找到了圖像中的輪廓,你可以使用 approxPolyDP 函數來近似地計算輪廓的形狀。如果你要查找正方形,你可以在這些形狀中尋找具有 4 個頂點的多邊形,這些多邊形應該有相近的邊長和角度。如果你要檢測的正方形不是水平的或垂直的,你可能需要使用角度信息來確定它的方向。

import cv2
# 读入图像
img = cv2.imread('square.jpg')

# 将图像转为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 使用 Canny 边缘检测
edges = cv2.Canny(gray, 50, 150)

# 寻找轮廓
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 遍历轮廓
for contour in contours:
    # 近似计算轮廓
    approx = cv2.approxPolyDP(contour, 0.01 * cv2.arcLength(contour, True), True)
    # 如果是 4 个顶点的多边形,且边长和角度相近,则认为是正方形
    if len(approx) == 4 and cv2.isContourConvex(app

當我們可以取得相黏物件邊緣時 – 分水嶺演算法

分水嶺演算法-偵測相連區域形狀

當形狀邊緣不清楚時-使用霍夫找線

使用霍夫變換檢測直線。因為正方形的四條邊是直線,因此可以通過檢測這四條直線來確定正方形。

具體來說,可以使用 OpenCV 中的 HoughLinesP 函數來檢測直線。該函數會返回一組檢測到的線段,這些線段可以是任意方向和長度的線段,因此我們需要對這些線段進行篩選,只保留長度、方向和相對位置都符合要求的線段。接著,我們可以將這些線段按照一定的規則組合成四條邊,從而確定正方形。

以下是一個示例代碼,演示如何使用霍夫變換檢測正方形:

import cv2
import numpy as np

# 读入图像
img = cv2.imread('square.jpg')

# 将图像转为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 使用 Canny 边缘检测
edges = cv2.Canny(gray, 50, 150)

# 使用霍夫变换检测直线
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength=100, maxLineGap=10)

# 筛选直线
filtered_lines = []
for line in lines:
    x1, y1, x2, y2 = line[0]
    length = np.sqrt((x2-x1)**2 + (y2-y1)**2)
    angle = np.arctan2(y2-y1, x2-x1)
    # 保留长度在一定范围内的直线
    if 100 < length < 200:
        filtered_lines.append((length, angle, line))

# 组合直线
sides = []
for i, (length1, angle1, line1) in enumerate(filtered_lines):
    for j, (length2, angle2, line2) in enumerate(filtered_lines[i+1:], i+1):
        angle_diff = np.abs(angle1 - angle2)
        # 保留角度相近的直线
        if angle_diff < np.pi/4:
            # 计算两条直线的中心点
            x1, y1, x2, y2 = line1
            cx1, cy1 = (x1+x2)/2, (y1+y2)/2
            x1, y1, x2, y2 = line2
            cx2, cy2 = (x1+x2)/2, (y1+y2)/2
            center_diff = np.sqrt((cx2-cx1)**2 + (cy2-cy1)**2)
            # 保留中心点距离相近的直线
            if center_diff < 20:
                # 将两条直线组合成一条边
                sides.append((length1+length2, angle1, angle2, line1, line2))

# 按照长度排序,取前四条直线作为正方形的四条边
sides = sorted(sides
取前四条直线作为正方形的四条边
sides = sorted(sides, reverse=True)[:4]

计算正方形的四个顶点
corners = []
for i, (length1, angle1, angle2, line1, line2) in enumerate(sides):
for j, (length3, angle3, angle4, line3, line4) in enumerate(sides[i+1:], i+1):
# 计算两条直线的交点
x1, y1, x2, y2 = line1
cx1, cy1 = (x1+x2)/2, (y1+y2)/2
x1, y1, x2, y2 = line3
cx2, cy2 = (x1+x2)/2, (y1+y2)/2
k1 = np.tan(angle1)
k2 = np.tan(angle3)
x = (cy1 - cy2 + k2cx2 - k1cx1) / (k2 - k1)
y = k1*(x - cx1) + cy1
corners.append((x, y))

绘制正方形
for i in range(4):
x1, y1 = corners[i]
x2, y2 = corners[(i+1)%4]
cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 3)

显示结果
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

基於角點的角點檢測

哈里斯角點檢測器簡介

Harris Corner Detector 是一種角點檢測算子,常用於計算機視覺算法中以提取角點並推斷圖像的特徵。它由 Chris Harris 和 Mike Stephens 於 1988 年在 Moravec 的角檢測器改進後首次提出。Harris 角點檢測器相比之前的角點檢測器,直接考慮了角點分數的差異,直接參考方向,而不是每 45 度角使用 shifting patches,並被證明在區分邊緣和邊緣方面更準確。角落。從那以後,它被改進並被許多算法採用,為後續應用預處理圖像。

發佈日期: 2 則留言

分水嶺演算法-偵測相連區域形狀

官方教學

Image Segmentation with Watershed Algorithm
官方的範例是一群黏在一起的硬幣

分割出黏在一起的長方形

這篇文章是在討論如何分割出一群黏在一起的長方形

給定一個二值圖像,我們可以應用距離變換 (DT) 並從中獲得分水嶺的標記。理想情況下,會有一個現成的函數來查找區域最小值/最大值,但由於它不存在,我們可以對如何設置 DT 閾值做出一個不錯的猜測。基於標記,我們可以使用 Watershed 進行分割,問題就解決了。現在您可以擔心區分矩形組件和非矩形組件了。

OpenCV的distanceTransform是一個圖像處理功能,可以計算圖像中每個像素到最近的零值像素之間的歐幾里德距離。distanceTransform功能可以在圖像分割、形狀檢測、物體識別等應用中使用。

在OpenCV中,distanceTransform有三種不同的實現方式:cv2.DIST_L1、cv2.DIST_L2和cv2.DIST_C。cv2.DIST_L1使用曼哈頓距離,cv2.DIST_L2使用歐幾里德距離,而cv2.DIST_C使用切比雪夫距離。

曼哈頓距離也稱為城市區塊距離或L1距離。它是兩點之間水平和垂直距離的總和。如果p1和p2是兩個二維坐標點,則曼哈頓距離可以通過以下公式計算:

d(p1, p2) = |p1.x – p2.x| + |p1.y – p2.y|

歐幾里德距離是兩個點之間的直線距離。如果p1和p2是兩個二維坐標點,則歐幾里德距離可以通過以下公式計算:

d(p1, p2) = sqrt((p1.x – p2.x)^2 + (p1.y – p2.y)^2)

切比雪夫距離是兩個點之間在所有方向上的最大距離。如果p1和p2是兩個二維坐標點,則切比雪夫距離可以通過以下公式計算:

d(p1, p2) = max(|p1.x – p2.x|, |p1.y – p2.y|)

import sys
import cv2
import numpy
import random
from scipy.ndimage import label

def segment_on_dt(img):
    dt = cv2.distanceTransform(img, 2, 3) # L2 norm, 3x3 mask
    dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(numpy.uint8)
    dt = cv2.threshold(dt, 100, 255, cv2.THRESH_BINARY)[1]
    lbl, ncc = label(dt)

    lbl[img == 0] = lbl.max() + 1
    lbl = lbl.astype(numpy.int32)
    cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), lbl)
    lbl[lbl == -1] = 0
    return lbl


img = cv2.cvtColor(cv2.imread(sys.argv[1]), cv2.COLOR_BGR2GRAY)
img = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)[1]
img = 255 - img # White: objects; Black: background

ws_result = segment_on_dt(img)
# Colorize
height, width = ws_result.shape
ws_color = numpy.zeros((height, width, 3), dtype=numpy.uint8)
lbl, ncc = label(ws_result)
for l in xrange(1, ncc + 1):
    a, b = numpy.nonzero(lbl == l)
    if img[a[0], b[0]] == 0: # Do not color background.
        continue
    rgb = [random.randint(0, 255) for _ in xrange(3)]
    ws_color[lbl == l] = tuple(rgb)

cv2.imwrite(sys.argv[2], ws_color)

從上圖中,您可以考慮在每個組件中擬合橢圓以確定矩形。然後您可以使用一些度量來定義組件是否為矩形。這種方法更有可能適用於完全可見的矩形,但對於部分可見的矩形可能會產生不良結果。下圖顯示了這種方法的結果,如果擬合橢圓的矩形在組件面積的 10% 以內,則組件是矩形。

# Fit ellipse to determine the rectangles.
wsbin = numpy.zeros((height, width), dtype=numpy.uint8)
wsbin[cv2.cvtColor(ws_color, cv2.COLOR_BGR2GRAY) != 0] = 255

ws_bincolor = cv2.cvtColor(255 – wsbin, cv2.COLOR_GRAY2BGR)
lbl, ncc = label(wsbin)
for l in xrange(1, ncc + 1):
yx = numpy.dstack(numpy.nonzero(lbl == l)).astype(numpy.int64)
xy = numpy.roll(numpy.swapaxes(yx, 0, 1), 1, 2)
if len(xy) < 100: # Too small. continue ellipse = cv2.fitEllipse(xy) center, axes, angle = ellipse rect_area = axes[0] * axes[1] if 0.9 < rect_area / float(len(xy)) < 1.1: rect = numpy.round(numpy.float64( cv2.cv.BoxPoints(ellipse))).astype(numpy.int64) color = [random.randint(60, 255) for _ in xrange(3)] cv2.drawContours(ws_bincolor, [rect], 0, color, 2) cv2.imwrite(sys.argv[3], ws_bincolor)[/code] 更多的資訊請參考: Advanced square detection (with connected region)

發佈日期:

The TechGiCS Forum

Experience Sharing

This morning, I attended a rare technology forum where all the speakers were women. Some were founders, and some were executives of internationally renowned companies, all of them outstanding and talented women. These women also have families and children and face struggles in balancing work and family life. Therefore, today’s speakers were different from the male-dominated technology forums, as they did not focus on the latest or most popular technology. They all emphasized that we should know ourselves, what we like, and what we want. They also mentioned the unique characteristics of women, who tend to feel that they are not good enough, not prepared enough, or not qualified enough.

What impressed me the most was during the final interview session, when a journalist from BUSINESS NEXT asked these women if they had planned to become outstanding women in the workplace from an early age. To our surprise, all the women answered that they did not have such a plan. They only took each step seriously and thoroughly understood themselves. 

As a woman who grew up under traditional thinking, but unexpectedly became the “Husband as homemaker, wife as breadwinner” family model because of my child, I understand why it’s important for women to first understand their passions. Being a career woman is often not supported by societal norms, and it’s only by finding happiness in our work that we can find the energy to persevere through the challenges of such a career.

Therefore, like Eunjoo Kim, a speaker who works at Google, who said that she did not intend to work in the United States originally. She only followed his husband, who needed to switch jobs. What she did was only to understand herself at every step, walk towards the direction she liked, work hard with passion, and then achieve her current success.

For me, my family role transformation was also a result of discovering that my child has special needs, which forced me to switch roles. Along the way, I deeply felt the impact of traditional thinking on our subconscious, which can greatly influence us. We may be pursuing a role that we don’t actually want to become, and sacrifice our careers. Or perhaps because we can’t find a good partner, even though we are in the workplace, our self-evaluation is low.

When we are not clear about what we are and what we want, We will feel like a candle burning at both ends, such as taking care of children, working outside, considering our husband’s feelings, and keeping ourselves beautiful. We can never do enough, and we cannot maintain a 25-year-old appearance and figure like celebrities, earn the same amount of money as our husbands and be economically independent, be a good mother, and also be a wise and virtuous wife.

It is precisely because women have too many choices now that we cannot have them all. We must recognize ourselves, what we are suitable for, what we want, what we can do, and what we like. Finding our own position is a prerequisite for being an outstanding woman.

Event Screenshot

CEO and Co-Founder at Women Who Code – Alaina Percival

Interview session by BUSINESS NEXT

發佈日期:

使用OBS來推流H265

使用OBS來推流H265

OBS也在v29板之後支持了HEVC推流,支持利用RTMP的封裝方式來推送H265編碼的串流格式,若電腦沒有可支持硬編碼的GPU,其CPU編碼所採取的編碼方案是QuickSync HEVC。

若是電腦有可支持硬編碼的GPU,則下拉選單會增加該硬件編碼的編碼選項

並且可以錄製SVT-AV1、AOM-AV1和HEVC的格式的影片

發佈日期:

高壓縮比編碼格式的介紹 – HEVC

HEVC(H265)介紹

HEVC(High Efficiency Video Coding),也稱為H.265,是一種先進的視頻編解碼標準,是H.264/MPEG-4 AVC的後繼者。相較於H.264,HEVC可以提供更高的視頻質量、更少的碼率和更高的壓縮效率。
HEVC採用更高級的壓縮算法,通過增加更多的預測模式、增加更多的參考幀以及使用更高級別的變換和量化技術,實現了更高的壓縮比。在同樣的視頻質量下,HEVC可以減少50%以上的比特率。
HEVC支持分辨率高達8192×4320的超高清視頻,可以處理各種類型的視頻內容,包括高速運動、低比特率和高動態範圍內容。 HEVC還可以支持多種顏色空間、高級色彩映射和多視點視頻。
由於HEVC壓縮效率高,可以在更低的比特率下提供更高的視頻質量,因此被廣泛用於高清和超高清視頻的傳輸和存儲,例如藍光光盤、在線視頻流媒體、視頻會議等。

瀏覽器對於HEVC的支持


上面若有不支持的瀏覽器,只要支持webassembly,就可以透過webassembly來實現解碼的部分
使用webassembly來達成全平台HEVC的播放器

支持推流端的HEVC透過RTMP推流

OBS在版本29版之後增加的新的編碼支持(H265及AV1)
現在可以透過OBS來推送HEVC格式的流了。

https://blog.csdn.net/karamos/article/details/103508790
FLV規範不支持HEVC(H.265)/AV1,FFmpeg社區對FLV的新視頻編碼算法CodecID也沒有新增定義支持,騰訊視頻雲T-FFmpeg(騰訊視頻雲的FFmpeg維護版本)正在推動社區支持封裝/解封H.265/AV1的FLV的補丁。目前,國內各直播CDN廠商基本支持H.265的封裝和解封,而騰訊視頻雲則針對FLV支持AV1進行了一系列優化。

go2rtc

go2rtc
因為WebRTC是一個非常低延遲的封裝格式,有人寫了這個工具,可以把所有封裝格式的串流轉為WebRTC格式,但是現在WEBRTC對H265的支持仍不高
以下為支持表

Safari 支持 WebRTC,如果您通過以下方式啟用它:

英文版:Develop > Experimental Features > WebRTC H265 codec
中文版:开发 > 实验性功能 > WebRTC H265 codec

發佈日期:

高壓縮比編碼格式的介紹 – AV1

高壓縮比編碼格式AV1介紹

AV1是一種免費、開源的視頻編解碼器,由Alliance for Open Media(AOMedia)聯盟開發。它是H.265(HEVC)的競爭對手,旨在提供更高的壓縮效率和更好的視頻質量。
AV1使用了許多新技術來提高壓縮效率,例如可變帶寬預測、可變塊大小和可變熵編碼等。與H.265相比,AV1在相同的比特率下可以提供更高的視頻質量。同時,AV1還具有更好的自適應流媒體性能,可以更好地適應網絡帶寬的變化。
AV1可以支持多種分辨率和色彩空間,包括8位、10位和12位色彩深度。此外,它還可以支持HDR(高動態範圍)和WCG(廣色域)等高級視頻格式,以提供更真實的圖像質量。
由於AV1是一種開放標準,並且沒有專利費用,因此它被廣泛用於在線視頻流媒體服務和其他應用,例如YouTube、Netflix、Amazon Prime Video等。同時,AV1還適用於各種類型的視頻內容,包括電影、電視節目、動畫和遊戲。

常見的編碼器

SVT-AV1和AOM-AV1

SVT-AV1和AOM-AV1都是AV1編碼器,不過它們有幾個不同之處:

  • 開發者:SVT-AV1是由英特爾開發的,而AOM-AV1是由Alliance for Open Media(AOM)開發的。
  • 編碼質量:SVT-AV1相對於AOM-AV1來說,可以提供更高的編碼質量,尤其是在高比特率下。
  • 編碼速度:AOM-AV1的編碼速度相對於SVT-AV1來說更快,這對於需要實時編碼的場景非常重要。
  • 適用範圍:SVT-AV1更適用於需要高質量視頻編碼的場景,如影片後期製作等;而AOM-AV1更適用於實時編碼的場景,如視頻通話、視頻會議等。

甚麼是AV1 SVC

AV1 SVC中的SVC是指可伸縮視頻編碼(Scalable Video Coding)。可伸縮視頻編碼是一種視頻編碼技術,可以將視頻數據分成多個層級,每個層級可以根據不同的要求進行編碼和解碼。這種編碼技術可以在不同的設備和網絡帶寬上提供不同的視頻質量和分辨率。在AV1 SVC中,視頻數據被分成多個空間和時間層級。空間層級是根據空間分辨率進行編碼的,例如低分辨率圖像和高分辨率圖像。時間層級是根據時間分辨率進行編碼的,例如低幀率和高幀率圖像。這些層級可以按需選擇和合併,以提供適合設備和網絡帶寬的最佳視頻質量。+

AV1 SVC可以提供更高的編碼效率和更好的視頻質量,同時可以適應不同的設備和網絡環境。這使得它非常適合用於實時視頻流傳輸、視頻會議和移動通信等應用場景。AV1是第一個支持SVC的編解碼器。對於那些對關於SVC是如何發揮作用的更多細節感興趣的人,Alex E.博士在2016年寫了一篇很好的解釋性博文。寫的是關於VP9,大多數點對AV1有效的內容。

SVT-AV1和AOM-AV1都支持可伸縮視頻編碼(SVC),但是它們支持的SVC規格略有不同。具體而言:

  • SVT-AV1支持SVC的規格是MPEG-5 Part 2 LCEVC(Low Complexity Enhancement Video Coding),這是一種用於增強現有編碼器的規格,可以提高編碼效率和視頻質量。
  • AOM-AV1支持的SVC規格是AV1 Scalable Video Technology(SVT),這是一種基於AV1的可伸縮視頻編碼技術,可以實現在不同的網絡帶寬和終端設備之間動態調整視頻質量和分辨率。

非常適合用於會議場合

AV1旨在與下一波WebRTC視頻創新集成:端到端加密,SVC和獨立於編解碼器的轉發。因此,這與視頻編解碼器無關,而與下一代架構有關。

1. 隨著WebRTC現在通過可插入流(和SFrame)合併了E2E加密,並且NSA現在推薦E2E安全性,由於有效負載可能是不透明的,因此會議系統需要RTP標頭擴展來轉發數據包。因此,如果瀏覽器和編解碼器不支持可插入流或與下一代編解碼器集成的轉發頭擴展名,則將無法滿足NSA的要求,並且會議供應商將無法提供完整的功能。

2. SVC支持對於會議很重要。AV1內置了SVC;在HEVC中,它是一個擴展。Dependency Descriptor(在AV1 RTP有效負載規範中定義)優於用於空間可伸縮性模式的Framemarking RTP標頭擴展。如果瀏覽器(和下一代編解碼器)不支持帶有轉發頭擴展名的SVC,那麼它就沒有競爭力。

3. AV1包含屏幕編碼工具作為基本功能,而不是像HEVC中的擴展。這是會議的主要競爭優勢。”

屏幕共享

對於文本內容以及超高動態內容,在對屏幕內容進行編碼時,AV1都非常高效。實際上,AV1實時的性能非常優越,以至於像Cisco在Webex中所做的那樣,AV1實時可能只部署在單個使用案例中。

在共享屏幕或應用程序時,如果選擇了“優化運動和視頻”,並且您所在的機器至少有四個內核,則支持傳輸AV1。任何至少有兩個內核的機器都支持接收AV1。只要會議的所有參與者都支持AV1,AV1就會自動用於共享此類屏幕內容,否則它將自動恢復為H.264。

有趣的是,這里分別提到了4和2個內核的約束條件。思科在2019年6月的大蘋果大會上進行現場演示時也發表了同樣的觀點。

我們將在下一個部分中繼續討論性能的問題,但是為了提供相關的背景信息,MacBook Air自2008年以來一直使用具有2個內核的Intel core-2芯片,並且自2011年以來在MacBook Pro中具有4個或更多內核的Intel i7或更高版本。就筆記本電腦和台式機而言,預計擁有4個內核並不是一個大問題。

端到端加密

E2EE是下一件值得關注的問題。也許是因為這是webrtc最初許下的承諾之一。又或許是因為它成為了一個過度使用的營銷術語,而Zoom已經變得遍體鱗傷。也許是因為大多數人仍然聲稱擁有它,實際上卻沒有。

關於E2EE,對這個問題最好的回應之一是Emil Ivov的這篇演講。

雖然許多人認為E2EE加密只是一種視頻會議或聊天應用程序功能,但它在整個媒體行業中都以縮寫“DRM”(數字版權管理)的形式使用。然而,傳統的DRM在瀏覽器和媒體播放器中的實現並不是真正的端到端的,只涵蓋了交付這一模塊。當人們上傳他們的內容到一個平台時仍然需要信任這個平台(以及任何可以合法訪問或不合法訪問該平台的人)與他們的原始內容。

真正的E2EE要求在對媒體進行編碼時在源處對媒體進行加密,並且僅在播放時對其進行解密。它允許內容提供商不信任該平台。

WebRTC可通過插入流API方案來得到了廣泛的應用,因為它可以用於很多方面。它是使您能夠訪問媒體的API,也是啟用E2EE的必要步驟。但是,它本身沒有加密功能或加密密鑰管理功能。

推流端支持AV1的相關資料

SRS在4.0.91開始支持經過WebRTC去推流AV1
RTC: Support av1 for Chrome M90 enabled it. 4.0.91
事實上,這已經是瀏覽器內建就可以做到的了。針對WEBRTC而言,Chrome已經在90版之後支持AV1編碼
Google Chrome 90 正式版發佈,支援 AV1 解碼

另外騰訊雲則可支持使用AV1格式推流
實現直播AV1 編碼

解碼端支持AV1的相關資料

騰訊雲有自己開發出可支援AV1透過FLV格式來解碼
播放AV1格式視頻

播放AV1視頻

通過支持AV1的播放器,按播放步驟3中生成的地址進行播放即可。在播放器的選擇上,可以選擇已支持AV1的播放器,也可以對自有播放器進行改造。

已支持AV1的播放器

App 客戶端

1. ExoPlayer已支持AV1,用的libgav1
2. ijkplayer FFmpeg 版本陳舊,可以升級FFmpeg 並集成dav1d

Web 端

1. dash.js已經支持(解碼取決於瀏覽器,Chrome 支持)
2. shaka-player已經支持(解碼取決於瀏覽器,Chrome 支持)

PC 端

VLC PC 版,支持AV1 in FLV、HEVC in FLV, 可按需下載Windowos 版& MacOS 版

發佈日期: 1 則留言

OBS在版本29版之後增加的新的編碼支持(H265及AV1)

OBS支持HEVC推流

OBS在v29版本之後支持了HEVC推流,支持利用RTMP的封裝方式來推送H265編碼的串流格式,若電腦沒有可支持硬編碼的GPU,其CPU編碼所採取的編碼方案是QuickSync HEVC。

若是電腦有可支持硬編碼的GPU,則下拉選單會增加該硬件編碼的編碼選項

支持AV1及HEVC的錄影格式

並且可以錄製SVT-AV1、AOM-AV1和HEVC的格式的影片