[图像处理] YUV图像处理入门4
9 yuv420图像截取
本程序中的函数主要是对YUV420P视频数据流的第一帧图像进行截取。类似opencv中的rect函数,函数的代码如下所示:
/**
* @file 9 yuv_clip.cpp
* @author luohen
* @brief yuv image clip
* @date 2018-12-08
*
*/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>
using namespace std;
/**
* @brief
*
* @param w width of input yuv420p file
* @param h height of input yuv420p file
* @param sx clipped initial position x of origin y image
* @param sy clipped initial position y of origin y image
* @param sw wdith of clipped image
* @param sh height of clipped image
* @param url location of input yuv420p file
* @return int
*/
int yuv420_clip(int w, int h, int sx, int sy, int sw, int sh, const char *url)
{
//reading yuv file
FILE *input_fp;
//writing yuv file
FILE *output_fp = fopen("video_result/output_clip.yuv", "wb+");
if ((input_fp = fopen(url, "rb")) == NULL)
{
printf("%s open error!\n", url);
return -1;
}
else
{
printf("%s open.\n", url);
}
//origin image
unsigned char *pic = new unsigned char[w * h * 3 / 2];
//clipped image
unsigned char *pic_clip = new unsigned char[sw * sh * 3 / 2];
// y length of origin image
int size_y = w * h;
// yu length of origin image
int size_yu = w * h + w * h / 4;
// y length of clipped image
int size_sy = sw * sh;
// yu length of clipped image
int size_syu = sw * sh + sw * sh / 4;
fread(pic, sizeof(unsigned char), w * h * 3 / 2, input_fp);
//y clip
for (int j = 0; j < sh; j++)
{
for (int k = 0; k < sw; k++)
{
pic_clip[j * sw + k] = pic[(sx + j) * w + (sy + k)];
}
}
//sw_uv,sh_uv
int sw_uv = sw / 2;
int sh_uv = sh / 2;
//u clip
for (int j = 0; j < sh_uv; j++)
{
for (int k = 0; k < sw_uv; k++)
{
pic_clip[size_sy + j * sw_uv + k] = pic[size_y + (sx / 2 + j) * w / 2 + (sy / 2 + k)];
}
}
//v clip
for (int j = 0; j < sh_uv; j++)
{
for (int k = 0; k < sw_uv; k++)
{
pic_clip[size_syu + j * sw_uv + k] = pic[size_yu + (sx / 2 + j) * w / 2 + (sy / 2 + k)];
}
}
fwrite(pic_clip, 1, sw * sh * 3 / 2, output_fp);
delete[] pic;
delete[] pic_clip;
fclose(input_fp);
fclose(output_fp);
return 0;
}
/**
* @brief main
*
* @return int
*/
int main()
{
int state = yuv420_clip(352, 288, 60, 50, 176, 144, "video/akiyo.yuv");
return 0;
}
调用函数为:
int yuv420_clip(int w, int h, int sx, int sy, int sw, int sh, const char *url);
这段代码指的是对于宽高为w,h的yuv图像,相对于y分量来说截取宽为sw,高为sh的部分,截取部分左上角顶点坐标为(sx,sy);对于uv分量来截取宽高分别为sw/2,sh/2,截取部分左上角顶点坐标为(sx/2,sy/2)。然后分别对y,u,v分量截图。
这段程序sx=60,sy=50,sw=176,sh=144。最终的结果如下图:
10 yuv420图像帧差法运动检测
本程序中的函数主要是截取YUV420P视频数据流的第1帧图像和第200图像,通过帧差法对两幅图像的y分量进行对比实现运动检测。类似opencv中的absdiff函数,函数的代码如下所示:
/**
* @file 10 yuv_framedifference.cpp
* @author luohen
* @brief Frame difference method of y
* @date 2018-12-10
*
*/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <string.h>
#include <iostream>
using namespace std;
/**
* @brief
*
* @param pFrame1 the first frame
* @param pFrame2 the second frame
* @param pResult the result image
* @param w width of input yuv420p file
* @param h height of input yuv420p file
* @param yThreshold threshold value
*/
void yuv420_Framedifference(unsigned char *pFrame1, unsigned char *pFrame2, unsigned char *pResult, int w, int h, int yThreshold)
{
//the first frame
unsigned char *Y1 = new unsigned char[w * h];
//the second frame
unsigned char *Y2 = new unsigned char[w * h];
//copy y
memcpy(Y1, pFrame1, w * h);
memcpy(Y2, pFrame2, w * h);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int i = y * w + x;
//diff
int temp = abs((int)Y1[i] - (int)Y2[i]);
if (temp > yThreshold)
{
pResult[i] = 255;
}
else
{
pResult[i] = 0;
}
}
}
delete[] Y1;
delete[] Y2;
}
/**
* @brief main function
*
* @return int
*/
int main()
{
//the yuv image size
int w = 352, h = 288;
//reading yuv file
FILE *input_fp;
//writing yuv file
//the first yuv image
FILE *fp1 = fopen("video_result/akiyo1.yuv", "wb+");
//the second yuv image
FILE *fp2 = fopen("video_result/akiyo2.yuv", "wb+");
//the binary image of frame difference
FILE *fp3 = fopen("video_result/output_diff.y", "wb+");
const char * url = "video/akiyo.yuv";
if ((input_fp = fopen(url, "rb")) == NULL)
{
printf("%s open error!\n", url);
return 0;
}
else
{
printf("%s open.\n", url);
}
//result
unsigned char *pResult = new unsigned char[w * h];
//the first image
unsigned char *pFrame1 = new unsigned char[w * h * 3 / 2];
//the second image
unsigned char *pFrame2 = new unsigned char[w * h * 3 / 2];
//used for read frames
unsigned char originalFrame[352 * 288 * 3 / 2];
//reading image for a loop
for (int i = 0; i < 200; i++)
{
//fread function automatically moves the pointer
//take the first frame
if (i == 0)
{
fread(pFrame1, w * h * 3 / 2, 1, input_fp);
}
//take the second frame
if (i == 199)
{
fread(pFrame2, w * h * 3 / 2, 1, input_fp);
}
//Skip intermediate frame
else
{
fread(originalFrame, w * h * 3 / 2, 1, input_fp);
}
}
/* another way to read images
fread(pFrame1, w * h * 3 / 2, 1, input_fp);
int p = 199 * w*h * 3 / 2;
//move the pointer
fseek(input_fp, p, SEEK_SET);
fread(pFrame2, w * h * 3 / 2, 1, input_fp);
*/
//the threshold is 30
yuv420_Framedifference(pFrame1, pFrame2, pResult, w, h, 30);
fwrite(pFrame1, 1, w * h * 3 / 2, fp1);
fwrite(pFrame2, 1, w * h * 3 / 2, fp2);
fwrite(pResult, 1, w * h, fp3);
delete[] pResult;
delete[] pFrame1;
delete[] pFrame2;
fclose(input_fp);
fclose(fp1);
fclose(fp2);
fclose(fp3);
}
调用函数为:
void yuv420_Framedifference(unsigned char *pFrame1, unsigned char *pFrame2, unsigned char *pResult, int w, int h, int yThreshold);
这段代码中忽略了uv分量的影响。计算第一帧y分量与第二帧y分量的差。然后通过类似opencv中的threshold函数,对于帧差得到的图像中像素值大于给定阈值yThreshold的点,其像素值置为255,小于阈值的点像素值置为0。也就是说像素值为255的区域就是运动区域,为0的区域就是背景区域。这是最基本的运动检测算法,当然这段代码改进的地方有:通过uv分量的差综合得到背景区域;或者引入三帧差法。
对于主函数中,由于akiyo.yuv是一个视频流,通过fread函数每读完一张图像,fread中的文件指针会直接移动到当前文件读取位置,这样就可以用fread函数读完视频中所有的图像。
当然这种方式比较傻,可以直接通过下列代码,先读取第一帧图像,然后通过fseek函数,将fread的文件指针,从SEEK_SET(文件开始处)移动到第199帧图像的结尾处,再用fread函数读取第200帧图像。
fseek(input_fp, p, SEEK_SET);
fread(pFrame1, w * h * 3 / 2, 1, input_fp);
int p = 199 * w*h * 3 / 2;
fseek(input_fp, p, SEEK_SET);
fread(pFrame2, w * h * 3 / 2, 1, input_fp);
所提取的第1帧图像、第200帧图像以及帧差结果的二值图如图所示:
11 二值图像膨胀和腐蚀
本程序中的函数主要是对帧差法提取的运动区域二值图像进行膨胀和腐蚀操作,函数的代码如下所示:
/**
* @file 11 yuv_dilate_erode.cpp
* @author luohen
* @brief dilate and erode of yuv image
* @date 2018-12-10
*
*/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <string.h>
#include <iostream>
using namespace std;
/**
* @brief erode
*
* @param pFrame the input binary image
* @param pdilateResult the output binary image
* @param kernel kernel of erode
* @param w width of image
* @param h height of image
*/
void yuv420_erode(unsigned char *pFrame, unsigned char *pErodeResult, int kernel, int w, int h)
{
//copy data
unsigned char *srcImg = new unsigned char[w * h];
memcpy((char *)srcImg, (char *)pFrame, w * h);
//kernel除中心之外像素点个数
int nErodeThreshold = kernel * kernel - 1;
//对于中点要erode像素区域步长
int erodeDist = (kernel - 1) / 2;
//ignore edge point
for (int i = erodeDist; i < h - erodeDist; i++)
{
for (int j = erodeDist; j < w - erodeDist; j++)
{
// 如果值不为0才进行处理
if (srcImg[i * w + j] != 0)
{
int iPointCount = 0;
// 根据此点的邻域判断此点是否需要删除
for (int r = -erodeDist; r <= erodeDist; r++)
{
for (int c = -erodeDist; c <= erodeDist; c++)
{
//统计不为0的个数
if (srcImg[(i + r) * w + j + c] != 0)
{
iPointCount++;
}
}
}
// 如果邻域中不为0的个数小于阈值,则中心点像素值设置为0
if (iPointCount < nErodeThreshold)
{
pErodeResult[i * w + j] = 0;
}
else
{
pErodeResult[i * w + j] = 255;
}
}
else
{
pErodeResult[i * w + j] = 0;
}
}
}
delete[] srcImg;
return;
}
/**
* @brief dilate
*
* @param pFrame the input binary image
* @param pdilateResult the output binary image
* @param kernel kernel of dilate
* @param w width of image
* @param h height of image
*/
void yuv420_dilate(unsigned char *pFrame, unsigned char *pdilateResult, int kernel, int w, int h)
{
//copy data
unsigned char *srcImg = new unsigned char[w * h];
memcpy((char *)srcImg, (char *)pFrame, w * h);
//对于中点要erode像素区域步长
int erodeDist = (kernel - 1) / 2;
//ignore edge point
for (int i = erodeDist; i < h - erodeDist; i++)
{
for (int j = erodeDist; j < w - erodeDist; j++)
{
//对所有点进行判断
int iPointCount = 0;
// 根据此点的邻域判断此点是否需要删除
for (int r = -erodeDist; r <= erodeDist; r++)
{
for (int c = -erodeDist; c <= erodeDist; c++)
{
//统计不为0的个数
if (srcImg[(i + r) * w + j + c] != 0)
{
iPointCount++;
}
}
}
// 如果邻域中各像素点值都为0,则中心点像素值设置为0
if (iPointCount == 0)
{
pdilateResult[i * w + j] = 0;
}
else
{
pdilateResult[i * w + j] = 255;
}
}
}
delete[] srcImg;
return;
}
/**
* @brief
*
* @param pFrame1 the first frame
* @param pFrame2 the second frame
* @param pResult the result image
* @param w width of input yuv420p file
* @param h height of input yuv420p file
* @param yThreshold threshold value
*/
void yuv420_Framedifference(unsigned char *pFrame1, unsigned char *pFrame2, unsigned char *pResult, int w, int h, int yThreshold)
{
//the first frame
unsigned char *Y1 = new unsigned char[w * h];
//the second frame
unsigned char *Y2 = new unsigned char[w * h];
;
//copy y
memcpy(Y1, pFrame1, w * h);
memcpy(Y2, pFrame2, w * h);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int i = y * w + x;
//diff
int temp = abs((int)Y1[i] - (int)Y2[i]);
if (temp > yThreshold)
{
pResult[i] = 255;
}
else
{
pResult[i] = 0;
}
}
}
delete[] Y1;
delete[] Y2;
}
/**
* @brief main function
*
* @return int
*/
int main()
{
//the yuv image size
int w = 352, h = 288;
//reading yuv file
FILE *input_fp;
//writing yuv file
//the first yuv image
FILE *fp1 = fopen("video_result/akiyo1.yuv", "wb+");
//the second yuv image
FILE *fp2 = fopen("video_result/akiyo2.yuv", "wb+");
//the binary image of frame difference
FILE *fp3 = fopen("video_result/akiyo_erode.y", "wb+");
const char *url = "video/akiyo.yuv";
if ((input_fp = fopen(url, "rb")) == NULL)
{
printf("%s open error!\n", url);
return 0;
}
else
{
printf("%s open.\n", url);
}
//result
unsigned char *pResult = new unsigned char[w * h];
//the first image
unsigned char *pFrame1 = new unsigned char[w * h * 3 / 2];
//the second image
unsigned char *pFrame2 = new unsigned char[w * h * 3 / 2];
//used for read frames
unsigned char originalFrame[352 * 288 * 3 / 2];
//reading image for a loop
for (int i = 0; i < 200; i++)
{
//fread function automatically moves the pointer
//take the first frame
if (i == 0)
{
fread(pFrame1, w * h * 3 / 2, 1, input_fp);
}
//take the second frame
if (i == 199)
{
fread(pFrame2, w * h * 3 / 2, 1, input_fp);
}
//Skip intermediate frame
else
{
fread(originalFrame, w * h * 3 / 2, 1, input_fp);
}
}
/* another way to read images
fread(pFrame1, w * h * 3 / 2, 1, input_fp);
int p = 199 * w*h * 3 / 2;
//move the pointer
fseek(input_fp, p, SEEK_SET);
fread(pFrame2, w * h * 3 / 2, 1, input_fp);
*/
//the threshold is 30
yuv420_Framedifference(pFrame1, pFrame2, pResult, w, h, 30);
//kernel size is 3 X 3.
yuv420_erode(pResult, pResult, 3, w, h);
//yuv420_dilate(pResult, pResult, 3, w, h);
fwrite(pFrame1, 1, w * h * 3 / 2, fp1);
fwrite(pFrame2, 1, w * h * 3 / 2, fp2);
fwrite(pResult, 1, w * h, fp3);
delete[] pResult;
delete[] pFrame1;
delete[] pFrame2;
fclose(input_fp);
fclose(fp1);
fclose(fp2);
fclose(fp3);
}
函数调用代码为:
void yuv420_dilate(unsigned char *pFrame, unsigned char *pdilateResult, int kernel, int w, int h);
void yuv420_erode(unsigned char *pFrame, unsigned char *pErodeResult, int kernel, int w, int h);
腐蚀膨胀的原理和作用见文章;
https://blog.csdn.net/qq_25847123/article/details/73744575
简单。这段代码是opencv中dilate和erode函数中的简化版。不同之处是,这段代码所用处理核只能是矩形的。此外对于边缘点,opencv中会用类似深度学习卷积神经网络中的padding操作,在周围填充点;这段代码就忽略边缘的点,直接处理中间的像素点。
代码很简单,通常对yuv图像处理就是读图,分离yuv分量,然后分别对像素点进行处理。
膨胀腐蚀的结果如图所示:
[图像处理] YUV图像处理入门4的更多相关文章
- [图像处理] YUV图像处理入门1
目前数字图像处理技术已经应用生活各个方面,但是大部分教程都是利用第三方库(如opencv)对RGB图像格式进行处理.对于YUV图像格式的图像处理教程较少.于是博主搬运总结了多个大牛的文章,总结出来这个 ...
- [图像处理] YUV图像处理入门2
1 分离YUV420中YUV分量 本程序中的函数主要是将YUV420P视频数据流的第一帧图像中的Y.U.V三个分量分离开并保存成三个文件.函数的代码如下所示: /** * @file 1 yuv_sp ...
- [图像处理] YUV图像处理入门5
12 yuv420转换为rgb(opencv mat) yuv格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式,而且自己造轮子工作量太大.因此通常都会将yuv转换为rgb, ...
- [图像处理] YUV图像处理入门3
5 yuv420格式的灰阶测试图 本程序中的函数主要是为YUV420P视频数据流的第一帧图像添加边框.函数的代码如下所示: /** * @file 5 yuv_graybar.cpp * @autho ...
- Python图像处理库Pillow入门
http://python.jobbole.com/84956/ Pillow是Python里的图像处理库(PIL:Python Image Library),提供了了广泛的文件格式支持,强大的图像处 ...
- MATLAB图像处理_Bayer图像处理 & RGB Bayer Color分析
Bayer图像处理 Bayer是相机内部的原始图片, 一般后缀名为.raw. 很多软件都可以查看, 比如PS. 我们相机拍照下来存储在存储卡上的.jpeg或其它格式的图片, 都是从.raw格式转化 ...
- 打基础丨Python图像处理入门知识详解
摘要:本文讲解图像处理基础知识和OpenCV入门函数. 本文分享自华为云社区<[Python图像处理] 一.图像处理基础知识及OpenCV入门函数>,作者: eastmount. 一.图像 ...
- Atitit 图像处理知识点 知识体系 知识图谱v2
Atitit 图像处理知识点 知识体系 知识图谱v2 霍夫变换(Hough Transform) 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法.主要用来从图像 ...
- Atitit 图像处理知识点 知识体系 知识图谱
Atitit 图像处理知识点 知识体系 知识图谱 图像处理知识点 图像处理知识点体系 v2 qb24.xlsx 基本知识图像金字塔op膨胀叠加混合变暗识别与检测分类肤色检测other验证码生成 基本 ...
随机推荐
- JuiceFS 元数据引擎选型指南
文件系统是我们常见的存储形式,内部主要由数据和元数据两部分组成.其中数据是文件的具体内容,通常会直接展现给用户:而元数据是描述数据的数据,用来记录文件属性.目录结构.数据存储位置等.一般来说,元数据有 ...
- 【python】Ubuntu中多条命令的运行
在模型训练时,往往需要消融实验,通常都是在一个程序运行结束后才再次手动运行下一个,不及时在电脑前可能无端浪费时间,因此需要让程序自动一个接一个去执行.受此启发,特了解了Ubuntu中多命令运行的内容. ...
- 开源动态可监控线程池DynamicTp介绍
前言 使用线程池 ThreadPoolExecutor 过程中你是否有以下痛点呢? 代码中创建了一个 ThreadPoolExecutor,但是不知道那几个核心参数设置多少比较合适 凭经验设置参数值, ...
- SpringBoot(七) - Redis 缓存
1.五大基本数据类型和操作 1.1 字符串-string 命令 说明 set key value 如果key还没有,那就可以添加,如果key已经存在了,那会覆盖原有key的值 get key 如果ke ...
- 简单将Springboot项目部署到linux服务器上
1.使用springboot的jar包方式 直接使用maven工具按照步骤点击就可以直接打包 2.到target目录下找到 jar包 3.将jar包放到linux的任意文件夹下(此项目是之前的kafk ...
- 2022最新最详细必成功的在Vscode中设置背景图、同时解决不受支持的问题
文章目录 1.效果展示 2.设置背景图的详细步骤 2.1 .下载background插件 2.2 .选择扩展设置 2.3 .在setting.json中编辑 2.4.对应的配置文件 2.5 .重启电脑 ...
- ElasticSearch之Quick.ElasticSearch.Furion组件的使用
ElasticSearch 使用说明 本章,我们主要讲解在.Net 中对Quick.ElasticSearch.Furion的使用进行介绍! ElasticSearch 的官方客户端 API 文档地址 ...
- 中小型企业综合项目(Nginx+LVS+Tomcat+MGR+Nexus+NFS)
Nginx+Tomcat+Mysql综合实验 1.环境准备 服务器 IP地址 作用 系统版本 数据库服务器1 192.168.100.111 MGR集群数据库master节点 Rocky8.6 数据库 ...
- 几个实用 shell 脚本
1. Dos攻击防范(自动屏蔽攻击 IP) #!/bin/bash DATE=$(date +%d/%b/%Y:%H:%M) LOG_FILE=/usr/local/nginx/logs/demo2. ...
- 部署redis
1. 下载redis 下载地址:https://redis.io/download/ 下载版本:6.2.7 应用包:redis-6.2.7.tar.gz 2. 上传服务器并解压 将redis安装包上传 ...