H264解碼過濾花屏視頻幀

衆所周知視頻在各個領域佔有極爲重要的地位,安防領域,互聯網,醫藥,教育等等等等。扯淡我就儘量不多扯了,現主要扯安防領域吧,安防領域尤其是視頻分析領域,視頻質量要求比較苛刻。下面介紹一下場景比較苛刻的圖片情況:

1.這種

2.這種

花屏現象,在視頻接入解碼過程中尤爲常見,(比如28181接入,rtsp等等),解碼大家都考慮使用ffmpeg進行解碼,首先考慮的可能是解碼錯誤直接從解碼過程中就把這種錯誤的幀給幹掉,看了好多博客大概也就是這個思想。

1.如果解碼錯誤拋幀。2.如果是I幀從下一個IDR幀開始解碼。想法不錯當然我也在做了這一部分,具體部分代碼示例如下:

//僞代碼......	
int len = av_parser_parse2(m_h264Parser, m_ctx, &avpkt.data, &avpkt.size, in_buf, in_buf_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
                //重要: 如果有解碼錯誤,並且當前幀不是IDR就直接跳過
                //m_iErrorDeocde 表示是否有解碼錯誤 
                //m_h264Parser->pict_type != AV_PICTURE_TYPE_I 表示當前幀是否是I幀
		if (m_iErrorDeocde /*&& m_iLastFrame*/ && m_h264Parser->pict_type != AV_PICTURE_TYPE_I)
			return 0;
		in_buf += len;
		in_buf_size -= len;

		if(avpkt.size > 0)
		{
#if 0
			decode_video(m_ctx, m_picture, &avpkt,out_buf,pout,lineSize);
			m_iLastFrame = (m_h264Parser->pict_type == AV_PICTURE_TYPE_I)?1:0;
#endif	
#if 1
			{
				ret = avcodec_decode_video2(m_ctx, m_picture, &got_picture, &avpkt);
				m_iLastFrame = (m_h264Parser->pict_type == AV_PICTURE_TYPE_I) ? 1 : 0;
				if (ret < 0)
				{
					printf("damage!!!!!!!!!!");
					avcodec_flush_buffers(m_ctx);
					goto finish;
				}
                   }

到這種情況其實已經過濾掉了很多壞圖了,但是想上圖展示的兩種情況,就像是打不死的小強一下死了又來來了又死,怎麼辦?

當然前面的兩張圖你必須得把ffmpeg的錯誤隱藏給關掉,再就是另一個err_recognition這個東西,看解碼那塊的源碼找到了個這麼東西,具體幹什麼的,自己可以細細研究一下。

觀察上面的圖都有規律,是什麼?對,沒錯!就是都有灰圖,那灰圖是怎麼來的呢?於是乎我有看了看ffmpeg的h264解碼,注意到了一個0x80這麼數值,還是在alloc_pic的時候,難道這就是傳說中的賦初始值?看着像,具體也沒看太明白。。。。

那麼那些解碼錯誤的灰色的圖塊吧,確實的東西是不是就是這個默認值呢?答案差不多,那我是不是就可以把這些看似解碼正確的圖片其實是花了的圖片,直接判斷這些壞塊再做一遍過濾,剔除掉呢?

這裏我補充一些色彩空間的知識,不再贅述了,大概就是Ycbcr經過偏置處理默認值128即0x80,大概就是爲了和rgb的0~255在一個範圍吧:

https://blog.csdn.net/asahinokawa/article/details/80596655

好了,到這一步驟,基本上就是單純的過濾有灰塊的圖了,我的思想是判斷這個值,或者這個值範圍內的值,那麼選擇yuv哪個分量做過濾呢?當然是Y了,UV是色彩和飽和度,到了晚上這種值當然就是0x80了,看這張圖。

他的末尾全是這玩意,即0x80,這就是我選y的原因,因爲他是亮度。。。

我的過濾部分僞代碼實現如下:

//只是思想沒有做代碼的整潔及優化,可以根據自己情況去增加刪除代碼
bool  CheckY(int iwidth, int iHeight, unsigned char*Buf)
{
	
	unsigned char * pNewPoint = Buf;
	int iCountu = 0, iCountv = 0, iCountY = 0;
	int iValues = 0;
	unsigned char uBytes = 0;

	//定位最後一行Y
	pNewPoint = Buf;
	//uBytes = pNewPoint[0];

	//獲得新的像素位置
	pNewPoint = Buf + (iHeight - 8)*iwidth;

	//遍歷Y信息的所有高
	for (int i = 0; i < 8; i++)
	{
		unsigned char *pNewPoint2 = pNewPoint + i*iwidth;
		//遍歷Y信息的寬
		for (int j = 0; j < iwidth; j+=24)
		{
			int a = memcmp(pNewPoint2 + j, pNewPoint2 + j + 8, 8);
			int b = memcmp(pNewPoint2 + j + 8, pNewPoint2 + j + 16, 8);
			
	                
                        //判斷連續兩個8像素宏塊,是否相同,並且在這個值範圍內(0x7A~0x80)
			if (a == b && b == 0 && pNewPoint2[j] > 0x7A&& pNewPoint2[j] <= 0x80)
			{
				printf("%d%d%d%d%d%d", pNewPoint2[0], pNewPoint2[1], pNewPoint2[2], pNewPoint2[3], pNewPoint2[4], pNewPoint2[5]);
				iCountY++;
			}
			/*if (((unsigned char *)pNewPoint2)[j] == 0x80)
			{
				printf("uByte=%x,pNewPoint2=%x",uBytes,pNewPoint2[j]);
				
			}	*/
		}
	}
}

思想已經說完了,具體實現就看自己了,代碼爲商業代碼,就不能提供了。

發佈了23 篇原創文章 · 獲贊 23 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章