图像处理比例缩放

2018-05-10 22:35:49 qq_37486501 阅读数 22544
  • Qt绘图机制

    Linux环境图形用户界面应用程序开发,面向对象程序设计,Linux/Windows多平台图形应用开发,嵌入式设备图形界面开发。Qt绘图,事件机制,网络,数据库,嵌入式移植。

    2880课时 0分钟 9121人学习 沈寒
    免费试看

以灰度图像circuit.tif为例,利用Matlab图像处理工具箱中的imresize函数对图像进行比例缩放变换。要求:创建4个figure窗口(不可以用subplot,显示不出来放大效果),分别用于显示原始图像、等比例放大1.5倍后的图像、等比例缩小0.5倍后的图像、缩放为高190宽400的图像(实现不等比例缩放)。并保存缩放后的所有图像文件到当前目录中。

I=imread('circuit.tif');
F=imresize(I,1.5,'nearest');
imwrite(F,'circuitFangda1_5.tif');
S=imresize(I,0.5,'nearest');
imwrite(S,'circuitSuoxiao0_5.tif');
J=imresize(I, [190,400],'nearest');
imwrite(J,'circuitbudengbi190_400.tif');
figure(1);
imshow(I);
figure(2);
imshow(F);
figure(3);
imshow(S);
figure(4);
imshow(J);


2019-04-25 19:59:52 lyd1995 阅读数 1476
  • Qt绘图机制

    Linux环境图形用户界面应用程序开发,面向对象程序设计,Linux/Windows多平台图形应用开发,嵌入式设备图形界面开发。Qt绘图,事件机制,网络,数据库,嵌入式移植。

    2880课时 0分钟 9121人学习 沈寒
    免费试看

一、实验内容与原理

1、空间变换:

空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:
在这里插入图片描述
所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:
在这里插入图片描述
以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:
在这里插入图片描述
因此,平移变换的变换矩阵及逆矩阵记为:
在这里插入图片描述

2、缩放变换:

将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:
在这里插入图片描述

3、选择变换:

图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针,特别说明,图像的坐标轴与一般数学意义的坐标轴不同)为:
在这里插入图片描述

二、实验代码

实验环境:
(1)OpenCV3.4.3
(2)Ubuntu16.04
(3)VS Code
(4)C++

// 图像的旋转 与 缩放
#include <stdio.h>  
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <string>

class Extra1{
public:
    Extra1(std::vector<std::string> path){
        for(int i = 0; i < path.size(); i++){
            // 读取彩色图片、灰度图片
            original_color_image.push_back(cv::imread(path[i]));
            original_gray_image.push_back(color2Gray(original_color_image[i]));
        }
    }
    // 0、彩色图像转灰度图像
    cv::Mat color2Gray(cv::Mat src_image){
        //创建与原图同类型和同大小的矩阵
	    cv::Mat gray_image(src_image.rows, src_image.cols, CV_8UC1);
        if(src_image.channels()!=1){
            for(int i = 0; i < src_image.rows; i++)
                for(int j = 0; j < src_image.cols; j++)
                    gray_image.at<uchar>(i, j) = (src_image.at<cv::Vec3b>(i, j)[0] + src_image.at<cv::Vec3b>(i, j)[1] + src_image.at<cv::Vec3b>(i, j)[2]) / 3;
        }
        else
            gray_image = src_image.clone();
        return gray_image;
    }
    // 2 旋转图像
    void RotateScalingImage(cv::Mat& src_image, int opt=1){
        int angle = 0, delta = 1;
        std::string s[] = {"仅仅旋转","旋转加缩放"}; 
        while(true){
            double factor;
            if(opt)
                factor = (cos(angle*CV_PI/180.) + 1.05)*2;
            else
                factor = 1;

            int w = src_image.cols, h = src_image.rows;
            cv::Mat rot_mat = cv::Mat(2, 2, CV_32F);
            //             
            // [ m0 m1 m2 ]  ===> [ A11 A12   b1 ] ===> [cos(a)  sin(a)  b1]            
            // [ m3 m4 m5 ]  ===> [ A21 A22   b2 ] ===> [-sin(a) cos(a)  b2]
            //  
            rot_mat.at<float>(0, 0) = (float)(factor*cos(-angle*2*CV_PI/180.));     
            rot_mat.at<float>(0, 1) = (float)(factor*sin(-angle*2*CV_PI/180.));     
            rot_mat.at<float>(1, 0) = -rot_mat.at<float>(0, 1);     
            rot_mat.at<float>(1, 1) = rot_mat.at<float>(0, 0);                    
            // dst(x,y) = A * src(x,y) + b
            cv::Mat rotate_image = cv::Mat::zeros(src_image.rows, src_image.cols, src_image.type());
            realizeRotateScaling(src_image, rot_mat, rotate_image);
            //cv::Mat middle_filter_image = cv::Mat::zeros(rotate_image.rows, rotate_image.cols, rotate_image.type());
            grayFiltering(rotate_image);
            
            cv::imshow(s[opt], rotate_image);
            if(cv::waitKey(5) == 27)
                break;
            angle =(int)(angle + delta) % 360;   
        }
        return; 
    }
    // 2.1 实现旋转 与 缩放 操作
    void realizeRotateScaling(cv::Mat& src, cv::Mat& rotmat, cv::Mat& dst){
        int i, j, i1, j1;
        int centeri = src.rows/2, centerj = src.cols/2;
        for(i = 0; i < src.cols; i++)
            for(j = 0; j < src.rows; j++){
                i1 = (i - centeri)*rotmat.at<float>(0, 0) + (j - centerj)*rotmat.at<float>(1, 0) + centeri;
                j1 = (i - centeri)*rotmat.at<float>(0, 1) + (j - centerj)*rotmat.at<float>(1, 1) + centerj;
                if(0 <= i1 && i1 < dst.cols && 0 <= j1 && j1 < dst.rows)
                    dst.at<uchar>(i1, j1) = src.at<uchar>(i, j);
            }
    }
    // 3 灰度图像滤波
    void grayFiltering(cv::Mat& src, int select=0, int filter_size=9, double Q=1){
        int m = filter_size/2;
        int n = filter_size*filter_size;
        cv::Mat src_clone = src.clone();
        for(int i=m; i < src.rows - m; i++)
            for(int j=m; j < src.cols - m; j++){ 
                cv::Mat sub_matrix = src_clone(cv::Rect(j - m, i - m, filter_size, filter_size));
                if(src.at<uchar>(i,j) == 0)
                    src.at<uchar>(i,j) = maxValueConvolution(sub_matrix, filter_size);
            }
    }
    // 3.0 最大值滤波
    int maxValueConvolution(cv::Mat& image_block, int size){
        int max = 0;
        for(int i=0; i < image_block.rows; i++)
            for(int j=0; j < image_block.cols; j++){
                if(image_block.at<uchar>(i, j) > max)
                    max = image_block.at<uchar>(i, j);
            }
        return max;
    }
    // 3.1 中值滤波
    int middleValueConvolution(cv::Mat& image_block, int size=5){
        int min = 0, k = 0, pos = size*size/2;
        std::vector<int> nums;
        for(int k1 = 0; k1 < size; k1++)
            for(int k2 = 0; k2 < size; k2++)
                nums.push_back(image_block.at<uchar>(k1,k2));
        int middle = findMiddleNum(nums, 0, size*size - 1, pos);
        return middle;
    }
    // 3.2.1 快速查找中位数
    int findMiddleNum(std::vector<int>& nums, int begin, int end, int n){
        int i = partition(nums, begin, end);
        if(i == n)
            return nums[i];
        else if(i > n)
            return findMiddleNum(nums, begin, i-1, n);
        else
            return findMiddleNum(nums, i+1, end, n);
    }
    // 3.2.2 交换
    void exchange(std::vector<int>& nums, int a,int b){
        int c = nums[a];
        nums[a] = nums[b];
        nums[b] = c;
        return;
    }
    // 3.2.2 快速查找中位数
    int partition(std::vector<int>& nums, int begin, int end){
        int i = begin, j = end + 1;
        int x = nums[begin];
        while (true) {
            while (nums[++i] < x) {// 向右扫描
                if (i == end)
                    break;
            }
            while (nums[--j] > x) {// 向左扫描
                if (j == begin)
                    break;
            }
            if (i >= j) // 指针相遇,切分位置确定
                break;
            exchange(nums, i, j);// 交换左右逆序元素
        }
        // 运行到最后:i指向从左往右第一个大于x的元素,j指向从右往左第一个小于x的元素。
        exchange(nums, begin, j);// 将切分元素放在切分位置
        return j;
    }
    //  运行
    void run(){
        RotateScalingImage(original_gray_image[0]);
    }


private:
    std::vector<cv::Mat> original_color_image;
    std::vector<cv::Mat> original_gray_image;
};

int main(){
    std::vector<std::string> path;
    path.push_back("/home/lyd/image_process/pic/lena.jpg");
    Extra1 ex6(path);
    ex6.run();

    return 1;
}

三、实验结果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2015-12-30 19:00:08 whuizhou 阅读数 16598
  • Qt绘图机制

    Linux环境图形用户界面应用程序开发,面向对象程序设计,Linux/Windows多平台图形应用开发,嵌入式设备图形界面开发。Qt绘图,事件机制,网络,数据库,嵌入式移植。

    2880课时 0分钟 9121人学习 沈寒
    免费试看

本文来源地址:http://www.osjoin.com        


今天遇见的处理图片的问题,一张图片上下两个部分都有一个空白区域,就中间是图片。要求是不能让他显示上下    有空白问间距。这是测试提出来的问题,但是图片本身就是这个毛病。无奈,哥哥改。谁有好的方法推荐推荐推荐      啊!!!


   两个方法如下

   建议让这两个放在UIImage的延展里,方便以后使用。

    1:按比例缩放

    在这里你传入的cgsize就是你要放大的区域,或者就是说设置放大的区域部分

     

//按比例缩放,size 是你要把图显示到 多大区域 
+ (UIImage *) imageCompressFitSizeScale:(UIImage *)sourceImage targetSize:(CGSize)size{
    UIImage *newImage = nil;
    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    CGFloat targetWidth = size.width;
    CGFloat targetHeight = size.height;
    CGFloat scaleFactor = 0.0;
    CGFloat scaledWidth = targetWidth;
    CGFloat scaledHeight = targetHeight;
    CGPoint thumbnailPoint = CGPointMake(0.0, 0.0);
    
    if(CGSizeEqualToSize(imageSize, size) == NO){
        
        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;
        
        if(widthFactor > heightFactor){
            scaleFactor = widthFactor;
            
        }
        else{
            
            scaleFactor = heightFactor;
        }
        scaledWidth = width * scaleFactor;
        scaledHeight = height * scaleFactor;
        
        if(widthFactor > heightFactor){
            
            thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
        }else if(widthFactor < heightFactor){
            
            thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
        }
    }
    
    UIGraphicsBeginImageContext(size);
    
    CGRect thumbnailRect = CGRectZero;
    thumbnailRect.origin = thumbnailPoint;
    thumbnailRect.size.width = scaledWidth;
    thumbnailRect.size.height = scaledHeight;
    
    [sourceImage drawInRect:thumbnailRect];
    
    newImage = UIGraphicsGetImageFromCurrentImageContext();
    if(newImage == nil){
        NSLog(@"scale image fail");
    }
    
    UIGraphicsEndImageContext();
    return newImage;
}


   2:按比例缩放

 这个代码很有意思

//指定宽度按比例缩放
-(UIImage *) imageCompressForWidthScale:(UIImage *)sourceImage targetWidth:(CGFloat)defineWidth{

    UIImage *newImage = nil;
    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    CGFloat targetWidth = defineWidth;
    CGFloat targetHeight = height / (width / targetWidth);
    CGSize size = CGSizeMake(targetWidth, targetHeight);
    CGFloat scaleFactor = 0.0;
    CGFloat scaledWidth = targetWidth;
    CGFloat scaledHeight = targetHeight;
    CGPoint thumbnailPoint = CGPointMake(0.0, 0.0);

    if(CGSizeEqualToSize(imageSize, size) == NO){

        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;

        if(widthFactor > heightFactor){
            scaleFactor = widthFactor;
        }
        else{
            scaleFactor = heightFactor;
        }
        scaledWidth = width * scaleFactor;
        scaledHeight = height * scaleFactor;

        if(widthFactor > heightFactor){

            thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;

        }else if(widthFactor < heightFactor){

            thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
        }
    }

    UIGraphicsBeginImageContext(size);

    CGRect thumbnailRect = CGRectZero;
    thumbnailRect.origin = thumbnailPoint;
    thumbnailRect.size.width = scaledWidth;
    thumbnailRect.size.height = scaledHeight;

    [sourceImage drawInRect:thumbnailRect];

    newImage = UIGraphicsGetImageFromCurrentImageContext();

    if(newImage == nil){

        NSLog(@"scale image fail");
    }
    UIGraphicsEndImageContext();
    return newImage;
}


 

调用

UIImage *headImage = [UIImage imageWithContentsOfFile:imagePath];

        

        UIImage * newHeadImage = [UIImage imageCompressForSize:headImage targetSize:CGSizeMake(200, 140)];

        

        if (newHeadImage==nil) {

            newHeadImage = [UIImage imageNamed:@"headIcon"];

        }

        self.headerImageView.image = newHeadImage;

2012-10-29 19:03:13 guoyk1990 阅读数 0
  • Qt绘图机制

    Linux环境图形用户界面应用程序开发,面向对象程序设计,Linux/Windows多平台图形应用开发,嵌入式设备图形界面开发。Qt绘图,事件机制,网络,数据库,嵌入式移植。

    2880课时 0分钟 9121人学习 沈寒
    免费试看

图像缩放的定义为:将图像中的某点(x,y)经缩放后其位置变为(x’,y’),则两者之间的关系为:

                                                        X’= ax   y’ = by

a、b分别是x、y方向上的缩放比例。当a、b大于1时图像放大,小于1时,图像缩小。当a = -1,b = 1时会产生一个关于y轴对称的镜像;当a = 1, b=-1时,会产生一个关于x 轴对称的镜像。其矩阵表示为:

                  

最近邻插值法,是最简单的插值法。其做法是令输出像素的灰度值等于离它所映射到的位置最近的输入图像像素的灰度值。此方法会产生锯齿,放大倍数过大会出现马赛克。

双线性插值也称为一阶差值,此方法是求得相邻的四个相邻的点的距离之比,用这个比率和四个相邻像素点的灰度值进行差值。具体方法如下:对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为f(i+u,j+v),其中i, j均为非负整数,p、q为区间[0,1)的浮点数,则这个像素f(i+p,j+q) 的值可由原图像坐标为( i , j ), (i+1, j), (i, j+1), (i+1 ,j+1) 所对应的四个像素的值决定:

                                        

双线性插值算法计算量比较大,但缩放后图像质量高。由于双线性差值具有低通滤波的性质,使高频分量受损,故会使图像丢失细节变的模糊。

具体算法实现:

  /// <summary>
    /// 图像缩放
    /// </summary>
    /// <param name="srcBmp">原始图像</param>
    /// <param name="width">目标图像宽度</param>
    /// <param name="height">目标图像高度</param>
    /// <param name="dstBmp">目标图像</param>
    /// <param name="zt">缩放选用的算法</param>
    /// <returns>处理成功 true 失败 false</returns>
    public static bool Zoom(Bitmap srcBmp, double width, double height, out Bitmap dstBmp, ZoomType zt) {//ZoomType为自定义的枚举类型
        if (srcBmp == null) {
            dstBmp = null;
            return false;
        }
        //若缩放大小与原图一样,则返回原图不做处理
        if (srcBmp.Width == width && srcBmp.Height == height) {
            dstBmp = new Bitmap(srcBmp);
            return true;
        }
        //计算缩放比例
        double ratioH = height / (double)srcBmp.Height;
        double ratioW = width / (double)srcBmp.Width;
        dstBmp = new Bitmap((int)width, (int)height);

        BitmapData srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        BitmapData dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        unsafe {
            byte* srcPtr = null;
            byte* dstPtr = null;
            int srcI = 0;
            int srcJ = 0;
            double srcdI = 0;
            double srcdJ = 0;
            double a = 0;
            double b = 0;
            double F1 = 0;//横向插值所得数值
            double F2 = 0;//纵向插值所得数值
            if (zt == ZoomType.NearestNeighborInterpolation) {//邻近插值法

                for (int i = 0; i < dstBmp.Height; i++) {
                    srcI = (int)(i / ratioH);
                    srcPtr = (byte*)srcBmpData.Scan0 + srcI * srcBmpData.Stride;
                    dstPtr = (byte*)dstBmpData.Scan0 + i * dstBmpData.Stride;
                    for (int j = 0; j < dstBmp.Width; j++) {
                        dstPtr[j * 3] = srcPtr[(int)(j / ratioW) * 3];
                        dstPtr[j * 3 + 1] = srcPtr[(int)(j / ratioW) * 3 + 1];
                        dstPtr[j * 3 + 2] = srcPtr[(int)(j / ratioW) * 3 + 2];
                    }
                }
            }
            if (zt == ZoomType.BilinearInterpolation) {//双线性插值法
                byte* srcPtrNext = null;
                for (int i = 0; i < dstBmp.Height; i++) {
                    srcdI = i / ratioH;
                    srcI = (int)srcdI;//当前行对应原始图像的行数
                    srcPtr = (byte*)srcBmpData.Scan0 + srcI * srcBmpData.Stride;//指原始图像的当前行
                    srcPtrNext = (byte*)srcBmpData.Scan0 + (srcI + 1) * srcBmpData.Stride;//指向原始图像的下一行
                    dstPtr = (byte*)dstBmpData.Scan0 + i * dstBmpData.Stride;//指向当前图像的当前行
                    for (int j = 0; j < dstBmp.Width; j++) {
                        srcdJ = j / ratioW;
                        srcJ = (int)srcdJ;//指向原始图像的列
                        if (srcdJ < 1 || srcdJ > srcBmp.Width - 1 || srcdI < 1 || srcdI > srcBmp.Height - 1) {//避免溢出(也可使用循环延拓)
                            dstPtr[j * 3] = 255;
                            dstPtr[j * 3 + 1] = 255;
                            dstPtr[j * 3 + 2] = 255;
                            continue;
                        }
                        a = srcdI - srcI;//计算插入的像素与原始像素距离(决定相邻像素的灰度所占的比例)
                        b = srcdJ - srcJ;
                        for (int k = 0; k < 3; k++) {//插值
                            F1 = (1 - b) * srcPtr[srcJ * 3 + k] + b * srcPtr[(srcJ + 1) * 3 + k];
                            F2 = (1 - b) * srcPtrNext[srcJ * 3 + k] + b * srcPtrNext[(srcJ + 1) * 3 + k];
                            dstPtr[j * 3 + k] = (byte)((1 - a) * F1 + a * F2);
                        }
                    }
                }
            }
        }
        srcBmp.UnlockBits(srcBmpData);
        dstBmp.UnlockBits(dstBmpData);
        return true;
    }
效果图不再展示~

2019-04-03 22:58:58 baidu_38172402 阅读数 779
  • Qt绘图机制

    Linux环境图形用户界面应用程序开发,面向对象程序设计,Linux/Windows多平台图形应用开发,嵌入式设备图形界面开发。Qt绘图,事件机制,网络,数据库,嵌入式移植。

    2880课时 0分钟 9121人学习 沈寒
    免费试看

在日常工作中,我们经常需要对图像进行缩放(放大、缩小),旋转平移等各种操作,这类操作统称为图像的几何变换。相对于前面提到的灰度变换,几何变换是改变了原图像像素点在新图像中的空间位置。

我们首先来看看图像缩放操作。假设一幅图像是100×100像素大小,放大一倍后是200×200大小。图像中的每一个像素点位置可以看作是一个点,也可以看作是二维平面上的一个矢量。图像缩放,本质上就是将每个像素点的矢量进行缩放,也就是将矢量x方向和y方向的坐标值缩放。也就是[x,y][x, y]变成了[kxx,kyy][k_x \cdot x,k_y\cdot y],一般情况下kx=kyk_x=k_y,但是很多时候也不相同,例如将100×100的图像变成400×300的图像。学过线性代数的同学很快就能知道,这可以表示成矩阵乘法的形式:


[uv]=[kx00ky][xy] \begin{aligned} {\left[ \begin{array}{ccc} u\\ v\\ \end{array} \right ]}={ \left[ \begin{array}{ccc} k_x &amp; 0 \\ 0 &amp; k_y\\ \end{array} \right ]}{ \left[ \begin{array}{ccc} x\\ y\\ \end{array} \right ]} \end{aligned}

通过上述矩阵乘法的形式,我们就把原图像上的每一个像素点映射到新图像上相应的像素点了,这称为前向映射

但是,我们很快注意到,原始图像有100×100=10000个像素点,而变换后的图像是200×200=40000个像素点。纵使把原始图像的10000个像素点全部映射到新图像上的对应点上,新图像上仍然有40000-10000=30000个点没有与原图像进行对应,那这30000个像素点的灰度值从何而来呢?

我们把上面的矩阵表达式稍微转换下,两边乘以放大矩阵的逆矩阵:

[kx00ky]1[uv]=[xy] \begin{aligned} { \left[ \begin{array}{ccc} k_x &amp; 0 \\ 0 &amp; k_y\\ \end{array} \right ] }^{-1}{\left[ \begin{array}{ccc} u\\ v\\ \end{array} \right ]}={ \left[ \begin{array}{ccc} x \\ y\\ \end{array} \right ]} \end{aligned}

通过上面的式子,我们可以将新图像中的每一个像素点[u,v][u, v]与原图像中的一个像素点[x,y][x, y]对应起来了。这称为后向映射。显然后向映射比前向映射更有效。

import cv2
import numpy as np

lenna100 = cv2.imread("lenna100.png", 0)
row, col = lenna100.shape
kx, ky = 2, 2
A = np.mat([[kx, 0], [0, ky]])
lenna200 = np.zeros((kx * row, ky * col))

for r in range(kx * row):
    for l in range(ky * col):
        v = np.dot(A.I, np.array([r, l]).T)
        lenna200[r, l] = lenna100[int(v[0, 0]), int(v[0, 1])]

cv2.imshow("lenna100", lenna100)
cv2.imshow("lenna200", lenna200.astype("uint8"))
cv2.waitKey()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这里写图片描述

转载自:https://blog.csdn.net/saltriver/article/details/79680067