版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011808673/article/details/85106438
图形增强常用方法,记录
包含:
1.旋转 rotation ,3D旋转
2.随机填充
3. JPEG 压缩
4.高斯噪声
5.随机剪裁
6.对比度调整
7.各种图形模糊平滑处理
#pragma once
#ifndef _UTIL_IMG_
#define _UTIL_IMG_
#include <opencv2/opencv.hpp>
#include <time.h>
namespace util {
/*
Rotation img random .
theta range[0, 360]
landmarks size can be set 0
**/
cv::Mat get_rotation_img(cv::Mat img, std::vector<cv::Point2f> &landmarks, int theta = 20) {
cv::Mat mat = img;
cv::Mat dst;
cv::Point2f center(img.size().width / 2, img.size().height / 2);
double angle = theta;
cv::Mat rot = cv::getRotationMatrix2D(center, angle, 1);
cv::Rect bbox = cv::RotatedRect(center, mat.size(), angle).boundingRect();
cv::warpAffine(mat, dst, rot, bbox.size());
for (int j = 0; j < landmarks.size(); j++)
{
float theta = -3.1415926 / (180 / angle);
float x1 = landmarks[j].x - rot.at<double>(1, 2);
float y1 = landmarks[j].y - rot.at<double>(0, 2);
landmarks[j].x = x1 * cos(theta) - y1 * sin(theta);
landmarks[j].y = x1 * sin(theta) + y1 * cos(theta);
}
return dst;
}
cv::Mat get_rotation_img_random(cv::Mat img, std::vector<cv::Point2f> &landmarks, int base_theta = 20) {
srand(time(NULL));
cv::Mat mat = img;
cv::Mat dst;
cv::Point2f center(img.size().width / 2, img.size().height / 2);
double angle = (rand() % base_theta) * std::pow((-1), rand()%2);
cv::Mat rot = cv::getRotationMatrix2D(center, angle, 1);
cv::Rect bbox = cv::RotatedRect(center, mat.size(), angle).boundingRect();
cv::warpAffine(mat, dst, rot, bbox.size());
for (int j = 0; j < landmarks.size(); j++)
{
float theta = -3.1415926 / (180 / angle);
float x1 = landmarks[j].x - rot.at<double>(1, 2);
float y1 = landmarks[j].y - rot.at<double>(0, 2);
landmarks[j].x = x1 * cos(theta) - y1 * sin(theta);
landmarks[j].y = x1 * sin(theta) + y1 * cos(theta);
}
return dst;
}
/*
filled roi rect with value.
value range[0,255]
**/
template <typename type>
void mat_filled_value(cv::Mat &img, cv::Rect rect, int value = 0) {
int rows = img.rows;
int cols = img.cols;
if (rect.x < 0 || rect.y < 0 || rect.x + rect.width > cols || rect.y + rect.height > rows) {
std::cout << "rect value error." << std::endl;
return ;
}
int channel = img.channels();
int offsetx = rect.x * channel;
int offsetx_ = (rect.x + rect.width)*channel;
for (size_t i = 0; i < rows; i++)
{
type *ptr = img.ptr<type>(i);
for (size_t j = 0; j < cols*channel; j = j + channel)
{
if (i >= rect.y && i < rect.y + rect.height && j >= offsetx && j < offsetx_) {
if (channel == 1) {
ptr[j] = 0;
}
else if (channel == 3) {
ptr[j] = 0;
ptr[j + 1] = 0;
ptr[j + 2] = 0;
}
}
}
}
}
/*
value range [1, img_rows]
**/
static std::vector<cv::Rect> generate_random_rect(int widths, int heights, int value = 6) {
srand(time(NULL));
//Rect rect1, rect2, rect3, rect4;
std::vector<cv::Rect> rects;
int x1 = rand() % (heights / value);
int x2 = rand() % (heights / value);
int x3 = rand() % (widths / value);
int x4 = rand() % (widths / value);
cv::Rect rect[4];
rect[0] = cv::Rect(0, 0, widths, x1);
rect[1] = cv::Rect(0, heights - x2, widths, x2);
rect[2] = cv::Rect(0, 0, x3, heights);
rect[3] = cv::Rect(widths - x4, 0, x4, heights);
for (size_t i = 0; i < 4; i++)
{
int x5 = rand() % 20;
int x6 = rand() % 3;
if (x5 < x6) {
rects.push_back(rect[i]);
}
}
return rects;
}
template <typename type>
cv::Mat mat_filled_value_with_random(cv::Mat img, int value = 0) {
cv::Mat dst = img.clone();
std::vector<cv::Rect> rects = generate_random_rect(img.cols, img.rows);
for (size_t i = 0; i < rects.size(); i++)
{
std::cout << " rect" << rects[i].x << " " << rects[i].y << " " << rects[i].width << " " << rects[i].height << std::endl;
mat_filled_value<type>(dst, rects[i], 0);
}
return dst;
}
/*
Random JPEG Compression
**/
cv::Mat encoder_JPEG(cv::Mat img, int value = 40) {
srand(time(NULL));
int QF = 100;
// JPEG quality factor
QF = 100-value + (rand() % (value+1));
std::vector<int> compression_params = { 1, QF };
std::vector<unsigned char> img_jpeg;
cv::imencode(".jpg", img, img_jpeg, compression_params);
cv::Mat temp = cv::imdecode(img_jpeg, 1);
return temp;
}
static void composeExternalMatrix(float yaw, float pitch, float roll, float trans_x, float trans_y, float trans_z, cv::Mat& external_matrix)
{
external_matrix.release();
external_matrix.create(3, 4, CV_64FC1);
double sin_yaw = sin((double)yaw * CV_PI / 180);
double cos_yaw = cos((double)yaw * CV_PI / 180);
double sin_pitch = sin((double)pitch * CV_PI / 180);
double cos_pitch = cos((double)pitch * CV_PI / 180);
double sin_roll = sin((double)roll * CV_PI / 180);
double cos_roll = cos((double)roll * CV_PI / 180);
external_matrix.at<double>(0, 0) = cos_pitch * cos_yaw;
external_matrix.at<double>(0, 1) = -cos_pitch * sin_yaw;
external_matrix.at<double>(0, 2) = sin_pitch;
external_matrix.at<double>(1, 0) = cos_roll * sin_yaw + sin_roll * sin_pitch * cos_yaw;
external_matrix.at<double>(1, 1) = cos_roll * cos_yaw - sin_roll * sin_pitch * sin_yaw;
external_matrix.at<double>(1, 2) = -sin_roll * cos_pitch;
external_matrix.at<double>(2, 0) = sin_roll * sin_yaw - cos_roll * sin_pitch * cos_yaw;
external_matrix.at<double>(2, 1) = sin_roll * cos_yaw + cos_roll * sin_pitch * sin_yaw;
external_matrix.at<double>(2, 2) = cos_roll * cos_pitch;
external_matrix.at<double>(0, 3) = trans_x;
external_matrix.at<double>(1, 3) = trans_y;
external_matrix.at<double>(2, 3) = trans_z;
}
static cv::Mat Rect2Mat(const cv::Rect& img_rect)
{
// 画像プレートの四隅の座標
cv::Mat srcCoord(3, 4, CV_64FC1);
srcCoord.at<double>(0, 0) = img_rect.x;
srcCoord.at<double>(1, 0) = img_rect.y;
srcCoord.at<double>(2, 0) = 1;
srcCoord.at<double>(0, 1) = img_rect.x + img_rect.width;
srcCoord.at<double>(1, 1) = img_rect.y;
srcCoord.at<double>(2, 1) = 1;
srcCoord.at<double>(0, 2) = img_rect.x + img_rect.width;
srcCoord.at<double>(1, 2) = img_rect.y + img_rect.height;
srcCoord.at<double>(2, 2) = 1;
srcCoord.at<double>(0, 3) = img_rect.x;
srcCoord.at<double>(1, 3) = img_rect.y + img_rect.height;
srcCoord.at<double>(2, 3) = 1;
return srcCoord;
}
static void CircumTransImgRect(const cv::Size& img_size, const cv::Mat& transM, cv::Rect_<double>& CircumRect)
{
// 入力画像の四隅を斉次座標へ変換
cv::Mat cornersMat = Rect2Mat(cv::Rect(0, 0, img_size.width, img_size.height));
// 座標変換し、範囲を取得
cv::Mat dstCoord = transM * cornersMat;
double min_x = std::min(dstCoord.at<double>(0, 0) / dstCoord.at<double>(2, 0), dstCoord.at<double>(0, 3) / dstCoord.at<double>(2, 3));
double max_x = std::max(dstCoord.at<double>(0, 1) / dstCoord.at<double>(2, 1), dstCoord.at<double>(0, 2) / dstCoord.at<double>(2, 2));
double min_y = std::min(dstCoord.at<double>(1, 0) / dstCoord.at<double>(2, 0), dstCoord.at<double>(1, 1) / dstCoord.at<double>(2, 1));
double max_y = std::max(dstCoord.at<double>(1, 2) / dstCoord.at<double>(2, 2), dstCoord.at<double>(1, 3) / dstCoord.at<double>(2, 3));
CircumRect.x = min_x;
CircumRect.y = min_y;
CircumRect.width = max_x - min_x;
CircumRect.height = max_y - min_y;
}
static void CreateMap(const cv::Size& src_size, const cv::Rect_<double>& dst_rect, const cv::Mat& transMat, cv::Mat& map_x, cv::Mat& map_y)
{
map_x.create(dst_rect.size(), CV_32FC1);
map_y.create(dst_rect.size(), CV_32FC1);
double Z = transMat.at<double>(2, 3);
cv::Mat invTransMat = transMat.inv(); // 逆行列
cv::Mat dst_pos(3, 1, CV_64FC1); // 出力画像上の座標
dst_pos.at<double>(2, 0) = Z;
for (int dy = 0; dy<map_x.rows; dy++) {
dst_pos.at<double>(1, 0) = dst_rect.y + dy;
for (int dx = 0; dx<map_x.cols; dx++) {
dst_pos.at<double>(0, 0) = dst_rect.x + dx;
cv::Mat rMat = -invTransMat(cv::Rect(3, 2, 1, 1)) / (invTransMat(cv::Rect(0, 2, 3, 1)) * dst_pos);
cv::Mat src_pos = invTransMat(cv::Rect(0, 0, 3, 2)) * dst_pos * rMat + invTransMat(cv::Rect(3, 0, 1, 2));
map_x.at<float>(dy, dx) = src_pos.at<double>(0, 0) + (float)src_size.width / 2;
map_y.at<float>(dy, dx) = src_pos.at<double>(1, 0) + (float)src_size.height / 2;
}
}
}
cv::Mat rotate_image_3D(const cv::Mat& src, float yaw, float pitch, float roll,
float Z = 1000, int interpolation = cv::INTER_LINEAR, int boarder_mode = cv::BORDER_CONSTANT, const cv::Scalar& border_color = cv::Scalar(0, 0, 0))
{
cv::Mat dst;
// rotation matrix
cv::Mat rotMat_3x4;
composeExternalMatrix(yaw, pitch, roll, 0, 0, Z, rotMat_3x4);
cv::Mat rotMat = cv::Mat::eye(4, 4, rotMat_3x4.type());
rotMat_3x4.copyTo(rotMat(cv::Rect(0, 0, 4, 3)));
// From 2D coordinates to 3D coordinates
// The center of image is (0,0,0)
cv::Mat invPerspMat = cv::Mat::zeros(4, 3, CV_64FC1);
invPerspMat.at<double>(0, 0) = 1;
invPerspMat.at<double>(1, 1) = 1;
invPerspMat.at<double>(3, 2) = 1;
invPerspMat.at<double>(0, 2) = -(double)src.cols / 2;
invPerspMat.at<double>(1, 2) = -(double)src.rows / 2;
// 3次元座標から2次元座標へ透視変換
cv::Mat perspMat = cv::Mat::zeros(3, 4, CV_64FC1);
perspMat.at<double>(0, 0) = Z;
perspMat.at<double>(1, 1) = Z;
perspMat.at<double>(2, 2) = 1;
// 座標変換し、出力画像の座標範囲を取得
cv::Mat transMat = perspMat * rotMat * invPerspMat;
cv::Rect_<double> CircumRect;
CircumTransImgRect(src.size(), transMat, CircumRect);
// 出力画像と入力画像の対応マップを作成
cv::Mat map_x, map_y;
CreateMap(src.size(), CircumRect, rotMat, map_x, map_y);
cv::remap(src, dst, map_x, map_y, interpolation, boarder_mode, border_color);
return dst;
}
static double generateGaussianNoise()
{
#define TWO_PI 6.2831853071795864769252866
static bool hasSpare = false;
static double rand1, rand2;
if (hasSpare)
{
hasSpare = false;
return sqrt(rand1) * sin(rand2);
}
hasSpare = true;
rand1 = rand() / ((double)RAND_MAX);
if (rand1 < 1e-100) rand1 = 1e-100;
rand1 = -2 * log(rand1);
rand2 = (rand() / ((double)RAND_MAX)) * TWO_PI;
return sqrt(rand1) * cos(rand2);
}
/*
gaussian noise with value range[1, 255]
**/
cv::Mat get_gaussian_noise_img(cv::Mat img, int value = 10) {
srand(time(NULL));
value = rand() % value;
cv::Mat dst = img.clone();
int channels = dst.channels();
int nRows = dst.rows;
int nCols = dst.cols * channels;
if (dst.isContinuous()) {
nCols *= nRows;
nRows = 1;
}
int i, j;
uchar* p;
for (i = 0; i < nRows; ++i) {
p = dst.ptr<uchar>(i);
for (j = 0; j < nCols; ++j) {
double val = p[j] + generateGaussianNoise() * value;
if (val < 0)
val = 0;
if (val > 255)
val = 255;
p[j] = (uchar)val;
}
}
return dst;
}
/*
Random Cropping Img, And Resize Same With Input Image.
**/
cv::Mat get_crop_img_same(cv::Mat img, cv::Size crop_size) {
srand(time(NULL));
int h_off = 0;
int w_off = 0;
int img_height = img.rows;
int img_width = img.cols;
if (img_height < crop_size.height) {
crop_size.height = img_height;
}
if (img_width < crop_size.width) {
crop_size.width = img_width;
}
cv::Mat cv_cropped_img = img;
h_off = rand() % (img_height - crop_size.height + 1);
w_off = rand() % (img_width - crop_size.width + 1);
cv::Rect roi(w_off, h_off, crop_size.width, crop_size.height);
cv_cropped_img = img(roi);
cv::resize(cv_cropped_img, cv_cropped_img, cv::Size(img_width, img_height));
return cv_cropped_img;
}
/* Contrast and Brightness Adjuestment.
img = (i,j)*alpha + beta.
@param command value = 0.2 range[0, 1]
**/
cv::Mat contrast_adjustment(cv::Mat img, float value = 0.2) {
srand(time(NULL));
cv::Mat dst;
cv::RNG rng;
float alpha = 1, beta = 0;
float min_alpha = 1- value, max_alpha = 1+ value;
alpha = rng.uniform(min_alpha, max_alpha);
beta = (float)(rand() % 8);
// flip sign
if (rand() %2 != 0) beta = -beta;
img.convertTo(dst, -1, alpha, beta);
return dst;
}
/*
双边滤波,高通滤波,保边去噪。
param command value is 3 range[1,img_rows]
**/
cv::Mat bilateratl_filter(cv::Mat img, int value = 3) {
int g_nsigmaColorValue = 2 * value + 5;
int g_nsigmaSpaceValue = 2 * value + 5;
cv::Mat dst;
cv::bilateralFilter(img, dst, value, g_nsigmaColorValue, g_nsigmaSpaceValue);
return dst;
}
/*
value_quality command value is 3 range[0, img_rows]
**/
cv::Mat motion_blur(cv::Mat in, unsigned int steps_x =3, unsigned int steps_y =3)
{
// steps_x steps_y should >= 2.
int rows = in.rows;
int cols = in.cols;
int channel = in.channels();
cv::Mat out = in.clone();
int element = cols * channel;
if (steps_x != 0) {
for (size_t i = 0; i < rows; i++)
{
uchar * ptr_in = in.ptr<uchar>(i);
uchar * ptr_out = out.ptr<uchar>(i);
float sum_b = 0, sum_g = 0, sum_r = 0;
for (size_t j = 0; j < element; j += channel)
{
if ((j / channel) < steps_x) {
if (channel == 3) {
sum_b += ptr_in[j];
sum_g += ptr_in[j + 1];
sum_r += ptr_in[j + 2];
ptr_out[j] = static_cast<uchar>(sum_b / ((j / channel) + 1));
ptr_out[j + 1] = static_cast<uchar>(sum_g / ((j / channel) + 1));
ptr_out[j + 2] = static_cast<uchar>(sum_r / ((j / channel) + 1));
}
else if (channel == 1) {
sum_b += ptr_in[j];
ptr_out[j] = static_cast<uchar>(sum_b / (j + 1));
}
}
else {
if (channel == 3) {
sum_b = sum_b + ptr_in[j] - ptr_in[j - steps_x * channel];
sum_g = sum_g + ptr_in[j + 1] - ptr_in[j - steps_x * channel + 1];
sum_r = sum_r + ptr_in[j + 2] - ptr_in[j - steps_x * channel + 2];
ptr_out[j] = static_cast<uchar>(sum_b / steps_x);
ptr_out[j + 1] = static_cast<uchar>(sum_g / steps_x);
ptr_out[j + 2] = static_cast<uchar>(sum_r / steps_x);
}
else if (channel == 1) {
sum_b = sum_b + ptr_in[j] - ptr_in[j - steps_x];
ptr_out[j] = static_cast<uchar>(sum_b / steps_x);
}
}
}
}
}
if (steps_y != 0) {
for (size_t i = 0; i < cols; i++)
{
float sum_b = 0, sum_g = 0, sum_r = 0;
for (size_t j = 0; j < rows; j++)
{
if (j < steps_y) {
if (channel == 3) {
sum_b += in.at<cv::Vec3b>(j, i)[0];
sum_g += in.at<cv::Vec3b>(j, i)[1];
sum_r += in.at<cv::Vec3b>(j, i)[2];
out.at<cv::Vec3b>(j, i)[0] = static_cast<uchar>(sum_b / (j + 1));
out.at<cv::Vec3b>(j, i)[1] = static_cast<uchar>(sum_g / (j + 1));
out.at<cv::Vec3b>(j, i)[2] = static_cast<uchar>(sum_r / (j + 1));
}
else if (channel == 1) {
sum_b += in.at<uchar>(j, i);
out.at<uchar>(j, i) = static_cast<uchar>(sum_b / (j + 1));
}
}
else {
if (channel == 3) {
sum_b = sum_b + in.at<cv::Vec3b>(j, i)[0] - in.at<cv::Vec3b>(j - steps_y, i)[0];
sum_g = sum_g + in.at<cv::Vec3b>(j, i)[1] - in.at<cv::Vec3b>(j - steps_y, i)[1];
sum_r = sum_r + in.at<cv::Vec3b>(j, i)[2] - in.at<cv::Vec3b>(j - steps_y, i)[2];
out.at<cv::Vec3b>(j, i)[0] = static_cast<uchar>(sum_b / steps_y);
out.at<cv::Vec3b>(j, i)[1] = static_cast<uchar>(sum_g / steps_y);
out.at<cv::Vec3b>(j, i)[2] = static_cast<uchar>(sum_r / steps_y);
}
else if (channel == 1) {
sum_b = sum_b + in.at<uchar>(j, i) - in.at<uchar>(j - steps_y, i);
out.at<uchar>(j, i) = static_cast<uchar>(sum_b / steps_y);
}
}
}
}
}
return out;
}
/*
value_quality command value is 2 range[0, img_rows]
**/
cv::Mat dft_blur(cv::Mat inputs, const unsigned int value_quality = 2) {
std::vector<cv::Mat> mergeM;
std::vector<cv::Mat> splitM;
cv::split(inputs, splitM);
int size_m = splitM.size();
for (size_t j = 0; j < size_m; j++)
{
int w = cv::getOptimalDFTSize(splitM[j].cols);
int h = cv::getOptimalDFTSize(splitM[j].rows);//获取最佳尺寸,快速傅立叶变换要求尺寸为2的n次方
cv::Mat padded;
cv::copyMakeBorder(splitM[j], padded, 0, h - splitM[j].rows, 0, w - splitM[j].cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));//填充图像保存到padded中
cv::Mat plane[] = { cv::Mat_<float>(padded), cv::Mat_<float>::zeros(padded.size()) };//创建通道
cv::Mat complexIm;
cv::merge(plane, 2, complexIm);//合并通道
cv::dft(complexIm, complexIm);//进行傅立叶变换,结果保存在自身
int rows = complexIm.rows;
int cols = complexIm.cols;
int offsetX = rows / (value_quality + 1);
int offsetY = cols / (value_quality + 1);
for (size_t i = 0; i < rows; i++)
{
float *ptr = complexIm.ptr<float>(i);
for (size_t j = 0; j < cols * 2; j = j + 2)
{
if ((i > offsetX && i < (rows - offsetX)) && (j > 2 * offsetY && j < (2 * cols - 2 * offsetY))) {
ptr[j] = 0;
ptr[j + 1] = 0;
}
//if ((i < offsetX || i > (rows - offsetX)) || (j < 2 * offsetY || j > (2 * cols - 2 * offsetY))) {
// ptr[j] = 0;
// ptr[j + 1] = 0;
//}
}
}
cv::Mat _complexim;
complexIm.copyTo(_complexim);//把变换结果复制一份,进行逆变换,也就是恢复原图
cv::Mat iDft[] = { cv::Mat::zeros(plane[0].size(),CV_32F),cv::Mat::zeros(plane[0].size(),CV_32F) };//创建两个通道,类型为float,大小为填充后的尺寸
idft(_complexim, _complexim);//傅立叶逆变换
split(_complexim, iDft);//结果貌似也是复数
cv::magnitude(iDft[0], iDft[1], iDft[0]);
cv::normalize(iDft[0], iDft[0], 255, 0, CV_MINMAX);
//cv::imshow("idft", iDft[0]);//显示逆变换
//waitKey(0);
mergeM.push_back(iDft[0]);
}
cv::Mat dst;
cv::merge(mergeM, dst);
if (dst.channels() == 3) {
dst.convertTo(dst, CV_8UC3);
}
else if (dst.channels() == 1) {
dst.convertTo(dst, CV_8UC1);
}
return dst;
}
/* Smooth Filtering.
GaussianBlur
blur
medianBlur
boxFilter
**/
cv::Mat smooth_filter(cv::Mat img, int ksize = 3) {
srand(time(NULL));
cv::Mat dst;
int smooth_type = rand() % 7; // see opencv_util.hpp
ksize = ksize + 2 * (rand()%2);
switch (smooth_type) {
case 0:
cv::GaussianBlur(img, dst, cv::Size(ksize, ksize), 0);
break;
case 1:
cv::blur(img, dst, cv::Size(ksize, ksize));
break;
case 2:
cv::medianBlur(img, dst, ksize);
break;
case 3:
cv::boxFilter(img, dst, -1, cv::Size(ksize, ksize));
break;
case 4:
dst = motion_blur(img, ksize*(rand() % 2), ksize * (rand()%2));
break;
case 5:
dst = dft_blur(img, ksize);
break;
case 6:
dst = bilateratl_filter(img, ksize-1);
break;
default:
cv::GaussianBlur(img, dst, cv::Size(ksize, ksize), 0);
break;
}
return dst;
}
}
#endif // !1