2016-08-12 15:12:59 scimence 阅读数 3899
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4434 人正在学习 去看看 贾志刚

示例:

Image screen = getScreen();                       // 截取屏幕
saveImage(screen, screen.Size, @"d:\截屏.jpg");   // 保存截屏

# region 图像处理功能函数

/// <summary>
/// 按指定尺寸对图像pic进行非拉伸缩放
/// </summary>
public static Bitmap shrinkTo(Image pic, Size S, Boolean cutting)
{
    //创建图像
    Bitmap tmp = new Bitmap(S.Width, S.Height);     //按指定大小创建位图

    //绘制
    Graphics g = Graphics.FromImage(tmp);           //从位图创建Graphics对象
    g.Clear(Color.FromArgb(0, 0, 0, 0));            //清空

    Boolean mode = (float)pic.Width / S.Width > (float)pic.Height / S.Height;   //zoom缩放
    if (cutting) mode = !mode;                      //裁切缩放

    //计算Zoom绘制区域             
    if (mode)
        S.Height = (int)((float)pic.Height * S.Width / pic.Width);
    else
        S.Width = (int)((float)pic.Width * S.Height / pic.Height);
    Point P = new Point((tmp.Width - S.Width) / 2, (tmp.Height - S.Height) / 2);

    g.DrawImage(pic, new Rectangle(P, S));

    return tmp;     //返回构建的新图像
}


//保存图像pic到文件fileName中,指定图像保存格式
public static void SaveToFile(Image pic, string fileName, bool replace, ImageFormat format)    //ImageFormat.Jpeg
{
    //若图像已存在,则删除
    if (System.IO.File.Exists(fileName) && replace)
        System.IO.File.Delete(fileName);

    //若不存在则创建
    if (!System.IO.File.Exists(fileName))
    {
        if (format == null) format = getFormat(fileName);   //根据拓展名获取图像的对应存储类型

        if (format == ImageFormat.MemoryBmp) pic.Save(fileName);
        else pic.Save(fileName, format);                    //按给定格式保存图像
    }
}

//根据文件拓展名,获取对应的存储类型
public static ImageFormat getFormat(string filePath)
{
    ImageFormat format = ImageFormat.MemoryBmp;
    String Ext = System.IO.Path.GetExtension(filePath).ToLower();

    if (Ext.Equals(".png")) format = ImageFormat.Png;
    else if (Ext.Equals(".jpg") || Ext.Equals(".jpeg")) format = ImageFormat.Jpeg;
    else if (Ext.Equals(".bmp")) format = ImageFormat.Bmp;
    else if (Ext.Equals(".gif")) format = ImageFormat.Gif;
    else if (Ext.Equals(".ico")) format = ImageFormat.Icon;
    else if (Ext.Equals(".emf")) format = ImageFormat.Emf;
    else if (Ext.Equals(".exif")) format = ImageFormat.Exif;
    else if (Ext.Equals(".tiff")) format = ImageFormat.Tiff;
    else if (Ext.Equals(".wmf")) format = ImageFormat.Wmf;
    else if (Ext.Equals(".memorybmp")) format = ImageFormat.MemoryBmp;

    return format;
}

[DllImport("user32.dll")]
static extern bool GetCursorInfo(out CURSORINFO pci);

private const Int32 CURSOR_SHOWING = 0x00000001;
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
    public Int32 x;
    public Int32 y;
}

[StructLayout(LayoutKind.Sequential)]
struct CURSORINFO
{
    public Int32 cbSize;
    public Int32 flags;
    public IntPtr hCursor;
    public POINT ptScreenPos;
}

/// <summary>
/// 截取屏幕指定区域为Image,保存到路径savePath下,haveCursor是否包含鼠标
/// </summary>
public static Image getScreen(int x = 0, int y = 0, int width = -1, int height = -1, String savePath = "", bool haveCursor = true)
{
    if (width == -1) width = SystemInformation.VirtualScreen.Width;
    if (height == -1) height = SystemInformation.VirtualScreen.Height;

    Bitmap tmp = new Bitmap(width, height);                 //按指定大小创建位图
    Graphics g = Graphics.FromImage(tmp);                   //从位图创建Graphics对象
    g.CopyFromScreen(x, y, 0, 0, new Size(width, height));  //绘制

    // 绘制鼠标
    if (haveCursor)
    {
        try
        {
            CURSORINFO pci;
            pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
            GetCursorInfo(out pci);
            System.Windows.Forms.Cursor cur = new System.Windows.Forms.Cursor(pci.hCursor);
            cur.Draw(g, new Rectangle(pci.ptScreenPos.x, pci.ptScreenPos.y, cur.Size.Width, cur.Size.Height));
        }
        catch (Exception ex) { }    // 若获取鼠标异常则不显示
    }

    //Size halfSize = new Size((int)(tmp.Size.Width * 0.8), (int)(tmp.Size.Height * 0.8));  // 按一半尺寸存储图像
    if (!savePath.Equals("")) saveImage(tmp, tmp.Size, savePath);       // 保存到指定的路径下

    return tmp;     //返回构建的新图像
}

/// <summary>
/// 缩放icon为指定的尺寸,并保存到路径PathName
/// </summary>
public static void saveImage(Image image, Size size, String PathName)
{
    Image tmp = shrinkTo(image, size, false);
    SaveToFile(tmp, PathName, true, null);
}

# endregion



2019-08-16 12:34:50 qq_37394634 阅读数 616
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4434 人正在学习 去看看 贾志刚

个人博客:http://www.chenjianqu.com/

原文链接:http://www.chenjianqu.com/show-7.html

在计算机图像处理和计算机图形学中,图像缩放(image scaling)是指对数字图像的大小进行调整的过程。图像缩放是一种非平凡的过程,需要在处理效率以及结果的平滑度(smoothness)和清晰度(sharpness)上做一个权衡。当一个图像的大小增加之后,组成图像的像素的可见度将会变得更高,从而使得图像表现得“软”。相反地,缩小一个图像将会增强它的平滑度和清晰度。

 

图像缩小

  图像缩小是通过减少像素个数来实现的,因此要根据缩小的尺寸,从原图像中选择合适的像素点,使图像缩小后可以保持原有图像的特征。图像缩小的算法有很多,这里主要介绍和实现两种算法:等间隔采样、局部均值。

 

基于等间隔采样的图像缩小算法

  等间隔采样其实就是在原图中每间隔一定的距离取像素点放到输出图像上。设原图的大小为W*H,宽度和长度的缩小因子分别为看k1和k2,那么采样间隔为:W/k1,W/k2.也就是说在原图的水平方向每隔W/k1,在垂直方向每隔W/k2取一个像素。长和宽的缩小因子k1和k2相等时,图像时等比例缩小,不等时是不等比例缩小,缩小图像的长和宽的比例会发生变化。

算法的实现步骤:

(1)计算采样间隔

    设原图的大小为W*H,将其放大(缩小)为(k1*W)*(K2*H),则采样区间为

    ii=1/k1;      

    jj=1/k2;

    当k1==k2时为等比例缩小;当k1!=k2时为不等比例放大(缩小);当k1<1 && k2<1时为图片缩小,k1>1 && k2>1时图片放大。

(2)求出放大(缩小)后的图像

    设原图为F(x,y)(i=1,2, ……W; j=1,2,……H),放大(缩小)的图像为G(x,y)(x=1,2, ……M; y=1,2,……N,其中M=W*k1,N=H*k2),则有 G(x,y) = f(ii*x, jj*y)

 

代码实现:

//图像缩小—等间隔采样 参数:原图 x轴缩放比例 y轴缩放比例
Mat ZoomOutEqualInterval(Mat& src, double x_k,double y_k)
{
 
    Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));
    x_k = 1 / x_k;//将缩小率转换为采样间隔
    y_k = 1 / y_k;
    cout << "dst x y:" << dst.cols << " " << dst.rows << endl;
    cout << "src x y:" <<src.cols << " " << src.rows << endl;
    if (src.channels() == 3)
    {
        for (int i = 0; i < dst.rows; i++)
        {
            for (int j = 0; j < dst.cols; j++)
            {
 
                int x = j * x_k + 0.5;
                int y = i * y_k + 0.5;
                if (x >= src.cols)x = src.cols - 1;
                if (y >= src.rows)y = src.rows - 1;
                dst.at<Vec3b>(i, j) = src.at<Vec3b>(y, x);
            }
        }
    }
    return dst;
}

缩小一倍的效果如下:

1_20190110230138_288_20190118141407_577.png1_20190110230138_288.png

 

基于局部均值的图像缩小算法

算法描述

  等间隔采样的缩小方法实现简单,但是原图像中未被选中的像素信息会在缩小后的图像中丢失。局部均值的图像缩小方法对其进行了改进。在求缩小图像的像素时,不仅仅单纯的取在原图像中的采样点像素,而是以相邻的两个采样点为分割,将原图像分成一个个的子块。缩小图像的像素取相应子块像素的均值。

tt_20190110230350_946.png

根据局部均值缩小的原理:g11 = (f11 + f12 + f21 + f22 ) / 4

代码实现: 

//图像缩小—局部均值采样 参数:原图 x轴缩放比例 y轴缩放比例
Mat ZoomOutLocalMean(Mat& src, double x_k, double y_k)
{
 
    Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));
    x_k = 1 / x_k;//将缩小率转换为采样间隔
    y_k = 1 / y_k;
    cout << "dst x y:" << dst.cols << " " << dst.rows << endl;
    cout << "src x y:" << src.cols << " " << src.rows << endl;
    if (src.channels() == 3)
    {
        for (int i = 0; i < dst.rows; i++)
        {
            for (int j = 0; j < dst.cols; j++)
            {
                int j_start = (j-1) * x_k+1;
                if (j_start < 0)j_start = 0;
                int j_end = j* x_k;
                if (j_end >=src.cols )j_end = src.cols;
 
                int i_start= (i-1) * y_k +1;
                if (i_start < 0)i_start = 0;
                int i_end = i * y_k;
                if (i_end >= src.rows)i_end = src.rows;
 
                int pix[3] = { 0,0,0 };
                
                int count = (j_end - j_start)*(i_end - i_start);
                for (int n = i_start; n < i_end; n++)
                    for (int m = j_start; m < j_end; m++) {
                        pix[0] = src.at<Vec3b>(n, m)[0];
                        pix[1] = src.at<Vec3b>(n, m)[1];
                        pix[2] = src.at<Vec3b>(n, m)[2];
                    }
                if (count != 0) {
                    Vec3b v(pix[0] / count, pix[1] / count, pix[2] / count);
                    dst.at<Vec3b>(i, j) = v;
 
                }
                else
                    dst.at<Vec3b>(i, j) = src.at<Vec3b>(i,j);
            }
        }
    }
    return dst;
}

效果图:

2_20190110230200_741.png

 

 

图像放大

  要将该图像放大两倍,可以有很多种算法,最简单的方法为邻域插值,即将每一个原像素原封不动地复制映射到扩展后对应四个像素中:

      这种方法在放大图像的同时保留了所有的原图像的所有信息,但是会产生锯齿现象。

双线性插值的效果对于放大的图像而言较领域插值来得平滑,但是却使得图像变得模糊而且仍然会有一部分锯齿现象。双三次插值更好比双线性插值更好。这里只实现最近邻插值和双线性插值。

 

最邻近插值

原理

    原始图像:I(x,y), 输出图像:F(x,y),则放大过程可转换为I(x,y)=F(int(c1*i),int(c2*j)),其中c1=1/y轴放大倍数,c2=1/x轴放大倍数系数乘以原坐标值得到的坐标值可能含有小数,所以,我们必须采取一定方法(如:四舍五入,我们这里直接去掉小数部分)来舍弃小数部分,从而取得整数表示的像素点坐标, 该过程即为最邻近插值方法。

 

优缺点

  最邻近插值简单且直观,但得到的图像质量不高,特别在图像放大后可能产生明显锯齿。

代码实现://图像放大-最近邻插值法

Mat ZoomInNearestNeighborInterpolation(Mat& src, double x_k, double y_k)
{
         Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));
         x_k = 1 / x_k;
         y_k = 1 / y_k;
         if (src.channels() == 3)
         {
                   for (int i = 0; i < dst.rows; i++)
                   {
                            for (int j = 0; j < dst.cols; j++)
                                     dst.at<Vec3b>(i, j) = src.at<Vec3b>(y_k*i, x_k*j);
                   }
         }
         return dst;
}

当x轴和y轴的放大系数均为2时,运行的效果为:

3_20190110230222_120.png

  

双线性插值

原理

      如图所示,最邻近插值是当求得p0后,直接找其邻近的点p1, p2, p3, p4中的一个的像素值作为目标点的像素;而双线性插值,则是根据p0点与周围4点(p1, p2, p3, p4)距离关系计算目标点的像素值。

4_20190110230651_777.png

通过计算得到的原始点为p0(x0, y0),则其4周的点分别为:

x0的可能取值为:sx1 = (int)x0, sx2 = sx1 + 1

y0的可能取值为:sy1 = (int)y0, sy2 = sy1 + 1

设:

s1 = y0 – sy1

s2 = sx2 – x0

s3 = 1.0 – s1

s4 = 1.0 – s2

假设p1, p2, p3, p4的像素值分别为v1, v2, v3, v4,

则双线性插值计算p0点像素值v0公式为:

v0 = v1*s1*s4 + v2*s1*s2 + v3*s2*s3 + v4*s3*s4

 

优缺点

  双线性内插值法计算量大,但缩放后图像质量高,不会出现像素值不连续的的情况。

  双线性插值具有低通滤波器的性质,使高频分量受损,所以可能会使图像轮廓在一定程度上变得模糊。

 

代码实现:

//图像放大-双线性插值法
Mat ZoomInBilinearInterpolation(Mat& src, double x_k, double y_k)
{
    Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));
    x_k = 1 / x_k;
    y_k = 1 / y_k;
    if (src.channels() == 3)
    {
        for (int i = 0; i < dst.rows; i++){
            for (int j = 0; j < dst.cols; j++)
            {
                double x0 = x_k * j;
                double y0 = y_k * i;
                int x1 = int(x0);
                int y1 = int(y0);
                
                
                double s1 = y0 - y1;
                double s4 = x0 - x1;
                double s2 = 1 - s4;
                double s3 = 1 - s1;
                if (x1 >= src.cols - 1)x1 = src.cols - 2;
                if (y1 >= src.rows - 1)y1 = src.rows - 2;
                dst.at<Vec3b>(i, j) = src.at<Vec3b>(y1, x1)*s1*s4 + src.at<Vec3b>(y1, x1 + 1)*s1*s2 + src.at<Vec3b>(y1 + 1, x1 + 1)*s2*s3 + src.at<Vec3b>(y1 + 1, x1)*s3*s4;
            }
        }
    }
    return dst;
}

效果图:

5_20190110230729_462.png

 

三次卷积法

原理

  双立方插值算法与双线性插值算法类似,对于放大后未知的像素点P,将对其影响的范围扩大到邻近的16个像素点,依据对P点的远近影响进行插值计算,因P点的像素值信息来自16个邻近点,所以可得到较细致的影像,不过速度比较慢。

6_20190110230750_486.png

  不过双立方插值算法与双线性插值算法的本质区别不仅在于扩大了影响点的范围,还采用高级的插值算法,如图所示:

7_20190110230812_167.png

     要求A,B两点之间e点的值,需要利用A,B周围A-1,A,B,B 1四个点的像素值,通过某种非线性的计算,得到光滑的曲线,从而算出e点的值来。

    所谓“双”或者叫“二次”的意思就是在计算了横向插值影响的基础上,把上述运算拓展到二维空间,再计算纵向插值影响的意思。

      双立方插值算法能够得到相对清晰的画面质量,不过计算量也变大。该算法在现在的众多图像处理软件中最为常用,比如Photoshop,After Effects,Avid,Final Cut Pro等。

      为了得到更好的图像质量,在以上的基础上,许多新的算法不断涌现,它们使用了更加复杂的改进的插值方式。譬如B样条(B-SPline), 米切尔(Mitchell)等插值算法,它们的目的是使插值的曲线显得更平滑,图像边缘的表现更加完美。

 

自适应样条插值极其增强技术( S-Spline & S-Spline XL)

  与上述经典的插值方法最大的区别在于, S-Spline 采用了一种自适应技术,那些传统的方法总是依据周围的像素点来求未知点的色彩值,也就是说需要求解的色彩值仅仅依靠该像素点在图像中的位置,而非实际的图像的像素信息,而自适应样条算法还会考虑实际图像的像素信息。 实验表明,经过 S-Spline 算法得到的图像效果要优于双立方插值算法。

      现在 S-Spline 算法又出现了增强版 S-Spline XL,新版本的 S-Spline XL 算法较 S-Spline 而言画面的锐度得到进一步增强,物体的轮廓更加清晰,边缘的锯齿现象大大减弱,图像感受更加自然。

 

 

参考文献

[1]博客园:淑月尘缘. 图像缩放算法 .

https://www.cnblogs.com/sycy/p/4743620.html . 2015-08-19

[2]CSDN博客:rainbowbirds_aes.图像放大算法.

https://blog.csdn.net/rainbowbirds_aes/article/details/83114266. 2018.10.17

[3]博客园:Brook_icv. OpenCV2:等间隔采样和局部均值的图像缩小.

http://www.cnblogs.com/wangguchangqing/p/4011892.html. 2014.10.08

[3]韩九强,杨磊.数据图像处理-基于XAVIS组态软件.西安交通大学出版社.2018.8

 

 

 

 

 

2019-05-19 10:32:28 majianxiong_lzu 阅读数 144
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4434 人正在学习 去看看 贾志刚

使用Java实现一个简单的图像处理工具类,可完成图像安装一定比例缩放,代码如下:

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 图像缩放工具类
 */
public class ImageUtils {
    /**
     * 缩放图像
     *
     * @param src   原始图像
     * @param scale 缩放比例
     * @return
     */
    public static BufferedImage zoomByScale(BufferedImage src, double scale) {
        //获取缩放后的长和宽
        int width = (int) (scale * src.getWidth());
        int height = (int) (scale * src.getHeight());
        Image instance = src.getScaledInstance(width, height, Image.SCALE_DEFAULT);
        //新建一个和Image对象相同大小的画布
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //获取画笔
        Graphics2D graphics = image.createGraphics();
        //将Image对象画在画布上,最后一个参数,ImageObserver:接收有关 Image 信息通知的异步更新接口,没用到直接传空
        graphics.drawImage(instance, 0, 0, null);
        //释放资源
        graphics.dispose();
        return image;
    }

    /**
     * 缩放图像,并保存至特定路径
     *
     * @param src    原始图像路径
     * @param dest   缩放图像路径
     * @param format 缩放图像格式
     * @param scale  缩放比例
     * @throws IOException
     */
    public static void zoomByScale(String src, String dest, String format, double scale) throws IOException {
        BufferedImage srcImg = ImageIO.read(new File(src));
        int width = (int) (scale * srcImg.getWidth());
        int height = (int) (scale * srcImg.getHeight());
        Image instance = srcImg.getScaledInstance(width, height, Image.SCALE_DEFAULT);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = image.createGraphics();
        graphics.drawImage(instance, 0, 0, null);
        graphics.dispose();
        ImageIO.write(image, format, new FileOutputStream(new File(dest)));
    }

    public static void main(String[] args) throws IOException {
        //缩小
        BufferedImage src = ImageIO.read(new File("E:/image/1.jpg"));
        BufferedImage image = zoomByScale(src, 0.6);
        //图像处理
//        image.getHeight();
        //另存
        ImageIO.write(image, "jpg", new FileOutputStream(new File("E:/image/1-1.jpg")));

        //放大图片
        zoomByScale("E:/image/1.jpg", "E:/image/1-2.jpg", "jpg", 1.2);
    }
}

测试效果
在这里插入图片描述
在这里插入图片描述

2016-01-15 16:36:34 damys 阅读数 923
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4434 人正在学习 去看看 贾志刚

特点:图像处理类,可以完成对各种类型的图像进行缩放、加图片水印和剪裁的操作

<?php
/**
 file: image.class.php 类名为Image
 图像处理类,可以完成对各种类型的图像进行缩放、加图片水印和剪裁的操作。
 */
class Image {
  /* 图片保存的路径 */
  private $path;
   
  /**
   * 实例图像对象时传递图像的一个路径,默认值是当前目录
   * @param  string $path  可以指定处理图片的路径
   */
  function __construct($path="./"){
    $this->path = rtrim($path,"/")."/";
  }
   
  /**
   * 对指定的图像进行缩放
   * @param  string $name  是需要处理的图片名称
   * @param  int $width   缩放后的宽度
   * @param  int $height   缩放后的高度
   * @param  string $qz   是新图片的前缀
   * @return mixed      是缩放后的图片名称,失败返回false;
   */
  function thumb($name, $width, $height,$qz="th_"){
    /* 获取图片宽度、高度、及类型信息 */
    $imgInfo = $this->getInfo($name);
    /* 获取背景图片的资源 */
    $srcImg = $this->getImg($name, $imgInfo);
    /* 获取新图片尺寸 */
    $size = $this->getNewSize($name,$width, $height,$imgInfo);
    /* 获取新的图片资源 */
    $newImg = $this->kidOfImage($srcImg, $size,$imgInfo);
    /* 通过本类的私有方法,保存缩略图并返回新缩略图的名称,以"th_"为前缀 */
    return $this->createNewImage($newImg, $qz.$name,$imgInfo);
  }
   
  /**
   * 为图片添加水印
   * @param  string $groundName 背景图片,即需要加水印的图片,暂只支持GIF,JPG,PNG格式
   * @param  string $waterName 图片水印,即作为水印的图片,暂只支持GIF,JPG,PNG格式
   * @param  int $waterPos    水印位置,有10种状态,0为随机位置;
   *                1为顶端居左,2为顶端居中,3为顶端居右;
   *                4为中部居左,5为中部居中,6为中部居右;
   *                7为底端居左,8为底端居中,9为底端居右;
   * @param  string $qz     加水印后的图片的文件名在原文件名前面加上这个前缀
   * @return  mixed        是生成水印后的图片名称,失败返回false
   */
  function waterMark($groundName, $waterName, $waterPos=0, $qz="wa_"){
    /*获取水印图片是当前路径,还是指定了路径*/
    $curpath = rtrim($this->path,"/")."/";
    $dir = dirname($waterName);
    if($dir == "."){
      $wpath = $curpath;
    }else{
      $wpath = $dir."/";
      $waterName = basename($waterName);
    }
   
    /*水印图片和背景图片必须都要存在*/
    if(file_exists($curpath.$groundName) && file_exists($wpath.$waterName)){
      $groundInfo = $this->getInfo($groundName);        //获取背景信息
      $waterInfo = $this->getInfo($waterName, $dir);      //获取水印图片信息
      /*如果背景比水印图片还小,就会被水印全部盖住*/
      if(!$pos = $this->position($groundInfo, $waterInfo, $waterPos)){
        echo '水印不应该比背景图片小!';
        return false;
      }
   
      $groundImg = $this->getImg($groundName, $groundInfo);  //获取背景图像资源
      $waterImg = $this->getImg($waterName, $waterInfo, $dir); //获取水印图片资源
   
      /* 调用私有方法将水印图像按指定位置复制到背景图片中 */
      $groundImg = $this->copyImage($groundImg, $waterImg, $pos, $waterInfo);
      /* 通过本类的私有方法,保存加水图片并返回新图片的名称,默认以"wa_"为前缀 */
      return $this->createNewImage($groundImg, $qz.$groundName, $groundInfo);
   
    }else{
      echo '图片或水印图片不存在!';
      return false;
    }
  }
   
  /**
   * 在一个大的背景图片中剪裁出指定区域的图片
   * @param  string $name  需要剪切的背景图片
   * @param  int $x     剪切图片左边开始的位置
   * @param  int $y     剪切图片顶部开始的位置
   * @param  int $width   图片剪裁的宽度
   * @param  int $height   图片剪裁的高度
   * @param  string $qz   新图片的名称前缀
   * @return  mixed      裁剪后的图片名称,失败返回false;
   */
  function cut($name, $x, $y, $width, $height, $qz="cu_"){
    $imgInfo=$this->getInfo($name);         //获取图片信息
    /* 裁剪的位置不能超出背景图片范围 */
    if( (($x+$width) > $imgInfo['width']) || (($y+$height) > $imgInfo['height'])){
      echo "裁剪的位置超出了背景图片范围!";
      return false;
    }
   
    $back = $this->getImg($name, $imgInfo);     //获取图片资源
    /* 创建一个可以保存裁剪后图片的资源 */
    $cutimg = imagecreatetruecolor($width, $height);
    /* 使用imagecopyresampled()函数对图片进行裁剪 */
    imagecopyresampled($cutimg, $back, 0, 0, $x, $y, $width, $height, $width, $height);
    imagedestroy($back);
    /* 通过本类的私有方法,保存剪切图并返回新图片的名称,默认以"cu_"为前缀 */
    return $this->createNewImage($cutimg, $qz.$name,$imgInfo);
  }
   
  /* 内部使用的私有方法,用来确定水印图片的位置 */
  private function position($groundInfo, $waterInfo, $waterPos){
    /* 需要加水印的图片的长度或宽度比水印还小,无法生成水印 */
    if( ($groundInfo["width"]<$waterInfo["width"]) || ($groundInfo["height"]<$waterInfo["height"]) ) {
      return false;
    }
    switch($waterPos) {
      case 1:     //1为顶端居左
        $posX = 0;
        $posY = 0;
        break;
      case 2:     //2为顶端居中
        $posX = ($groundInfo["width"] - $waterInfo["width"]) / 2;
        $posY = 0;
        break;
      case 3:     //3为顶端居右
        $posX = $groundInfo["width"] - $waterInfo["width"];
        $posY = 0;
        break;
      case 4:     //4为中部居左
        $posX = 0;
        $posY = ($groundInfo["height"] - $waterInfo["height"]) / 2;
        break;
      case 5:     //5为中部居中
        $posX = ($groundInfo["width"] - $waterInfo["width"]) / 2;
        $posY = ($groundInfo["height"] - $waterInfo["height"]) / 2;
        break;
      case 6:     //6为中部居右
        $posX = $groundInfo["width"] - $waterInfo["width"];
        $posY = ($groundInfo["height"] - $waterInfo["height"]) / 2;
        break;
      case 7:     //7为底端居左
        $posX = 0;
        $posY = $groundInfo["height"] - $waterInfo["height"];
        break;
      case 8:     //8为底端居中
        $posX = ($groundInfo["width"] - $waterInfo["width"]) / 2;
        $posY = $groundInfo["height"] - $waterInfo["height"];
        break;
      case 9:     //9为底端居右
        $posX = $groundInfo["width"] - $waterInfo["width"];
        $posY = $groundInfo["height"] - $waterInfo["height"];
        break;
      case 0:
      default:    //随机
        $posX = rand(0,($groundInfo["width"] - $waterInfo["width"]));
        $posY = rand(0,($groundInfo["height"] - $waterInfo["height"]));
        break;
    }
    return array("posX"=>$posX, "posY"=>$posY);
  }
   
  /* 内部使用的私有方法,用于获取图片的属性信息(宽度、高度和类型) */
  private function getInfo($name, $path=".") {
    $spath = $path=="." ? rtrim($this->path,"/")."/" : $path.'/';
   
    $data = getimagesize($spath.$name);
    $imgInfo["width"]  = $data[0];
    $imgInfo["height"] = $data[1];
    $imgInfo["type"]  = $data[2];
   
    return $imgInfo;
  }
   
  /*内部使用的私有方法, 用于创建支持各种图片格式(jpg,gif,png三种)资源 */
  private function getImg($name, $imgInfo, $path='.'){
   
    $spath = $path=="." ? rtrim($this->path,"/")."/" : $path.'/';
    $srcPic = $spath.$name;
   
    switch ($imgInfo["type"]) {
      case 1:         //gif
        $img = imagecreatefromgif($srcPic);
        break;
      case 2:         //jpg
        $img = imagecreatefromjpeg($srcPic);
        break;
      case 3:         //png
        $img = imagecreatefrompng($srcPic);
        break;
      default:
        return false;
        break;
    }
    return $img;
  }
   
  /* 内部使用的私有方法,返回等比例缩放的图片宽度和高度,如果原图比缩放后的还小保持不变 */
  private function getNewSize($name, $width, $height, $imgInfo){
    $size["width"] = $imgInfo["width"];     //原图片的宽度
    $size["height"] = $imgInfo["height"];    //原图片的高度
   
    if($width < $imgInfo["width"]){
      $size["width"]=$width;          //缩放的宽度如果比原图小才重新设置宽度
    }
   
    if($height < $imgInfo["height"]){
      $size["height"] = $height;        //缩放的高度如果比原图小才重新设置高度
    }
    /* 等比例缩放的算法 */
    if($imgInfo["width"]*$size["width"] > $imgInfo["height"] * $size["height"]){
      $size["height"] = round($imgInfo["height"]*$size["width"]/$imgInfo["width"]);
    }else{
      $size["width"] = round($imgInfo["width"]*$size["height"]/$imgInfo["height"]);
    }
   
    return $size;
  }
   
  /* 内部使用的私有方法,用于保存图像,并保留原有图片格式 */
  private function createNewImage($newImg, $newName, $imgInfo){
    $this->path = rtrim($this->path,"/")."/";
    switch ($imgInfo["type"]) {
      case 1:       //gif
        $result = imageGIF($newImg, $this->path.$newName);
        break;
      case 2:       //jpg
        $result = imageJPEG($newImg,$this->path.$newName);
        break;
      case 3:       //png
        $result = imagePng($newImg, $this->path.$newName);
        break;
    }
    imagedestroy($newImg);
    return $newName;
  }
   
  /* 内部使用的私有方法,用于加水印时复制图像 */
  private function copyImage($groundImg, $waterImg, $pos, $waterInfo){
    imagecopy($groundImg, $waterImg, $pos["posX"], $pos["posY"], 0, 0, $waterInfo["width"],$waterInfo["height"]);
    imagedestroy($waterImg);
    return $groundImg;
  }
   
  /* 内部使用的私有方法,处理带有透明度的图片保持原样 */
  private function kidOfImage($srcImg, $size, $imgInfo){
    $newImg = imagecreatetruecolor($size["width"], $size["height"]);
    $otsc = imagecolortransparent($srcImg);
    if( $otsc >= 0 && $otsc < imagecolorstotal($srcImg)) {
      $transparentcolor = imagecolorsforindex( $srcImg, $otsc );
      $newtransparentcolor = imagecolorallocate(
      $newImg,
      $transparentcolor['red'],
      $transparentcolor['green'],
      $transparentcolor['blue']
      );
      imagefill( $newImg, 0, 0, $newtransparentcolor );
      imagecolortransparent( $newImg, $newtransparentcolor );
    }
    imagecopyresized( $newImg, $srcImg, 0, 0, 0, 0, $size["width"], $size["height"], $imgInfo["width"], $imgInfo["height"] );
    imagedestroy($srcImg);
    return $newImg;
  }
}



2019-04-07 17:57:24 lena_bmp 阅读数 138
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4434 人正在学习 去看看 贾志刚

#用Visual C++实现图像双线性插值法等比例缩放
  在数字图像处理中,图像的缩放是基本的操作。下面介绍利用Visual C++ MFC多文档应用程序对话框,基于双线性插值法来实现图像缩放的功能,本实验采用Visual Studio 2017完成。
  MFC多文档应用程序的建立步骤略,代码中已经提前加入了图像的读取功能(在Doc类中,可在大部分Visual C++数字图像处理参考书中找到源代码)。
  先添加一个对话框,在资源视图中右键点击menu,点击插入dialog即可
  插入的对话框最好改一下ID,比如我这里叫ID_Zoom。再向其中添加一个Edit Control控件用于输入缩放比例因子,同样最好修改一下ID(我这里叫IDC_Ratio),旁边再加一个static text文本框用作说明。在这里插入图片描述
  然后需要为该对话框添加一个类,类名自定。基类可选择CDialog或CDialogEx,后者是前者的扩展类,具有CDialog的全部功能和一些新功能,这里我选择的是CDialog。
  创建完成后在该对话框类的头文件中加入成员变量Zoom,为缩放比例,类型为double。关于为新创建的类添加成员变量和成员函数,既可以直接在类的头文件中声明,也可以用类向导创建。由于前一种方法需要自行添加消息响应,比较麻烦,因此推荐直接利用类向导添加,方法为右键点击对话框空白处,选择添加变量,在弹出的“添加控件变量”对话框中为相应的控件(此处为IDC_Ratio,即Edit Control控件)添加变量,记得将“类别”设置为“值”,变量类型改为double
  下一步在资源视图menu中添加相应事件,并为其添加事件处理程序在这里插入图片描述
处理程序选择View类的command类型,点击添加编辑
  接下来就开始正式编写程序。
在这里插入图片描述
  首先要明白一点,位图作为若干个像素点的集合,在缩放过程中不可避免地会产生像素点的增加或减少。以放大为例,假如放大两倍则会多出原来图像三倍的像素点。这些多出来的像素点如何进行填充?基本的方法是把处理后的图片向原图进行映射,也成为向后映射,比如原图中一个点的位置为(5,4),在放大2倍后的图像中的坐标就变成了(10,8)。把放大后的图片中的任意一个像素点向原图映射,如放大2倍后的图片中某一像素点A坐标为A(15,9),则该像素点在原图中对应的坐标即B(7.5,4.5)。但我们知道像素坐标都是BYTE(或unsigned char)类型,不会出现小数值,因此就需要用B周围的点的灰度值来进行相应运算作为A点的灰度值。一种方法是最邻近插值法,也就是说计算一下B点离哪个像素点最近,则直接把该像素点的灰度值赋给B,但是由于该方法过于简单粗暴,因此获得的图像往往不够清晰;另一种方法——双线性插值法则较好地解决了这一问题,它的基本思想是将B点周围的四个像素点按权计算灰度赋给A点。权值的选取取决于距离的远近,离B点越近的点计算灰度权重最大。以下是一个双线性插值原理的示意图,生动形象。
  在这里插入图片描述
  计算方法如下:若某一向后映射点C(4.1,5.7),它周围的四个点为C1(4,5)、C2(4.6)、C3(5,5)、C4(5,6)。在纵向上,C点离C1、C2较近,离C3、C4较远,距离比为1:9,那么对应的C点原映射点的灰度值就应该等于C1、C2综合起来的灰度值gray12和C3、C4综合的灰度值gray34乘以0.9和0.1两个权值所得值,至于这个gray12和gray34怎么计算,则还是同样的方法。根据C点离C1、C2的距离l1、l2可按权计算出gray12=(gray1l2+gray2l1)/(l1+l2)。因此不难看出该方法通过两次权值线性分配来计算灰度值,因此得名双线性插值法。
接下来就是快乐的代码实现过程。由于之前我已经在Doc类头文件中声明过一些变量,此处直接用Doc类指针调用其中的数据。
这是代码中调用的头文件的成员变量

void CMFCApplication7View::OnZoom()
{
	// TODO: 在此添加命令处理程序代码
	ZoomDlg cgt;

	if (cgt.DoModal())//判断是否打开模式对话框。如果不调用对话框的DoModal方法,点击menu里添加的图像缩放后,之前创建的对话框ID_Ratio就不会弹出来
	{
		CMFCApplication7Doc *pDoc = GetDocument();

		ASSERT_VALID(pDoc);

		UINT outWidth;
		UINT outHeight;
	
		//pDoc->imageWidth = (UINT)(1.0*pDoc->imageWidth / 4.0 + 0.5) * 4;
		//pDoc->lpbmi->bmiHeader.biWidth = pDoc->imageWidth;

		UINT inWidth = pDoc->imageWidth;
		UINT inHeight = pDoc->imageHeight;
		UINT inSize = inWidth * inHeight;	

		outWidth = (UINT)(1.0*inWidth* cgt.Zoom / 4.0 + 0.5) * 4;
		outHeight = (UINT)(1.0*inHeight* cgt.Zoom + 0.5);//宽高为缩放后的图像宽高
		UINT outSize = outWidth * outHeight;

		int i, j;
		
		unsigned char * pBits = pDoc->m_pBits;
		unsigned char * pOldBits;

		pDoc->lpbmi->bmiHeader.biWidth = outWidth;
		pDoc->lpbmi->bmiHeader.biHeight = outHeight;
		pDoc->imageWidth = outWidth;
		pDoc->imageHeight = outHeight;

		if (pDoc->m_nColorBits == 8)
		{		
			pOldBits = new unsigned char[inSize];
			if (pOldBits == NULL)
				return;

			memcpy(pOldBits, pBits, inSize);//把原图像中的数据拷贝到pOldBits里

			delete[] pBits;	

			pBits = new unsigned char[outSize];
			if (pBits == NULL)
				return;

			pDoc->m_pBits = pBits;
			memset(pBits, 0, outSize);

			for ( j = 0; j < outHeight ; j++)
			{
				for ( i = 0; i < outWidth ; i++)
				{ 
					double x = 1.0*i / cgt.Zoom;
					double y = 1.0*j / cgt.Zoom;

					int x1, x2, y1, y2;//原图中映射点周围四个点的坐标

					x1 = (int)x;//左x
					x2 = x1 + 1;//右x					
					y1 = (int)y;//上y
					y2 = y1 + 1;//下y
					/*
					  x1、y1始终不会越界
					  x2、y2可能越界
					  因此须对x2、y2越界的情况单独讨论
					 */

					BYTE clr1, clr2, clr3, clr4;
					double u, v;
					u = x - x1;
					v = y - y1;
					
					if (i == outWidth - 1&& j== outHeight-1)//右下顶点
						pBits[j*outWidth + i] = pOldBits[y1*inWidth + inWidth - 1];
					else if(i==outWidth-1)//第一列除右下顶点部分
						{
							clr1 = pOldBits[y1*inWidth + x1];//左上
							clr3 = pOldBits[y2*inWidth + x1];//左下
							pBits[j*outWidth + i] = (BYTE)clr1 * (1 - v) + clr3 * v;
						}			
					else if (j == outHeight - 1)//最后一行除右下顶点部分
					{
							clr1 = pOldBits[y1*inWidth + x1];//左上
							clr2 = pOldBits[y1*inWidth + x2];//右上

							pBits[j*outWidth + i] = (BYTE)(clr1 * (1 - u) + clr2 * u);
					}
					else//其他部分
					{	
							clr1 = pOldBits[y1*inWidth + x1];//左上
							clr2 = pOldBits[y1*inWidth + x2];//右上
							clr3 = pOldBits[y2*inWidth + x1];//左下
							clr4 = pOldBits[y2*inWidth + x2];//右下

							pBits[j*outWidth + i] = (BYTE)((u*clr2 + (1 - u)*clr1)*(1 - v) + (u*clr3 + (1 - u)*clr4)*v);					
					}//end else					
				}//for(i = 0 ……)
			}//for(j = 0 ……)	

			Invalidate();
			delete pOldBits;//删除零时分配内存
			return;

		}	//结束m_pBits=8判定	

		
			
		if (pDoc->m_nColorBits == 24)
			{
				pOldBits = new unsigned char[inSize * 3];
					if (pOldBits == NULL)
						return;

				memcpy(pOldBits, pBits, inSize * 3);//把原图像中的数据拷贝到pOldBits里

				delete[] pBits;

				pBits = new unsigned char[outSize * 3];
				
				if (pBits == NULL)
					return;
			
				pDoc->m_pBits = pBits;
				memset(pBits, 0, outSize * 3);

				for (int k = 0; k < 3; k++)
					{
						for (j = 0; j < outHeight; j++)
								{
									for (i = 0; i < outWidth; i++)
										{
											double x = 1.0*i / cgt.Zoom;
											double y = 1.0*j / cgt.Zoom;

											int x1, x2, y1, y2;//原图中映射点周围四个点的坐标

											x1 = (int)x;//左x
											x2 = x1 + 1;//右x					
											y1 = (int)y;//上y
											y2 = y1 + 1;//下y

											BYTE clr1, clr2, clr3, clr4;
											double u, v;
											u = x - x1;
											v = y - y1;

											if (i == outWidth - 1 && j == outHeight - 1)//右下顶点
												pBits[(j*outWidth + i) * 3 + k] = pOldBits[(y1*inWidth + inWidth - 1) * 3 + k];
											else if (i == outWidth - 1)//最后一列除右下顶点部分
												{
													clr1 = pOldBits[(y1*inWidth + x1) * 3 + k];//左上
													clr3 = pOldBits[(y2*inWidth + x1) * 3 + k];//左下
													pBits[(j*outWidth + i) * 3 + k] = (BYTE)(clr1 * (1 - v) + clr3 * v);
												}
											else if (j == outHeight - 1)//最后一行除右下顶点部分
												{
													clr1 = pOldBits[(y1*inWidth + x1) * 3 + k];//左上
													clr2 = pOldBits[(y1*inWidth + x2) * 3 + k];//右上
													pBits[(j*outWidth + i) * 3 + k] = (BYTE)(clr1 * (1 - u) + clr2 * u);
												}
											else//其他部分
												{
													clr1 = pOldBits[(y1*inWidth + x1) * 3 + k];//左上
													clr2 = pOldBits[(y1*inWidth + x2) * 3 + k];//右上
													clr3 = pOldBits[(y2*inWidth + x1) * 3 + k];//左下
													clr4 = pOldBits[(y2*inWidth + x2) * 3 + k];//右下

													pBits[(j*outWidth + i) * 3 + k] = (BYTE)((u*clr2 + (1 - u)*clr1)*(1 - v) + (u*clr3 + (1 - u)*clr4)*v);
												}//end else
									}//for(i = 0 ……)
						}//for(j = 0 ……)		
				}	//三重循环分别处理三个分量		
					Invalidate();
					delete pOldBits;//删除零时分配内存
					return;
		}//结束m_pBits=24判定
	}//DoModal						
	else
		return;
}

运行一下看看(又是辣个熟悉的女人)
在这里插入图片描述
  放大1.5倍后:
在这里插入图片描述在这里插入图片描述
  利用双线性插值法完成对话框图像缩放应用程序完成!
  如有错漏,恳请指正!

数字图像处理基础

阅读数 171

FPGA数字图像处理

阅读数 448

没有更多推荐了,返回首页