2016-12-05 17:33:30 syc434432458 阅读数 642
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19563 人正在学习 去看看 夏曹俊

如果只需要图像的一部分,就必须对图像进行剪切处理,在原图像上选择一个剪切区域,使用PorterDuffXfermode图像叠加规则,就可以把指定的图像区域剪切下来,下面通过三个步骤来说明如果对图像进行剪切操作。

    第一步,创建一个新位图作为画板,然后把原图像画到新位图上面,Java代码如下:
BitmapDrawable bd = (BitmapDrawable) getResources().getDrawable(  
                R.drawable.beauty);  
Bitmap bitmap = bd.getBitmap();  
int w = bitmap.getWidth();  
int h = bitmap.getHeight();  
Bitmap bm = Bitmap.createBitmap(w, h, Config.ARGB_8888);  
Canvas canvas = new Canvas(bm);  
Paint mPaint = new Paint();  
mPaint.setAntiAlias(true);  
mPaint.setStyle(Style.STROKE);  
canvas.drawBitmap(bitmap, 0, 0, mPaint);  

第一步效果图
第一步效果图

    第二步,绘制一个剪切区域,比如要剪切人物的脸部区域,需要在指定的位置绘制一个圆角矩形区域,代码中的坐标是在调试中获得,在其他分辨率下会有所不同,Java代码如下:
int deltX = 76;  
int deltY = 98;  
DashPathEffect dashStyle = new DashPathEffect(new float[] { 10, 5,        5, 5 }, 2);//创建虚线边框样式  
RectF faceRect = new RectF(0, 0, 88, 106);  
float [] faceCornerii = new float[] {30,30,30,30,75,75,75,75};  
Paint mPaint = new Paint();//创建画笔  
mPaint.setColor(0xFF6F8DD5);  
mPaint.setStrokeWidth(6);  
mPaint.setPathEffect(dashStyle);  
Path clip = new Path();//创建路径  
clip.reset();  
clip.addRoundRect(faceRect, faceCornerii, Direction.CW);//添加圆角矩形路径  
canvas.save();//保存画布  
canvas.translate(deltX, deltY);  
canvas.clipPath(clip, Region.Op.DIFFERENCE);  
canvas.drawColor(0xDF222222);  
canvas.drawPath(clip, mPaint);//绘制路径  
canvas.restore();  

第二步效果
第二步效果

    第三步,从原图像上获取指定区域的图像,并绘制到屏幕上,java代码如下:
Rect srcRect = new Rect(0, 0, 88, 106);  
srcRect.offset(deltX, deltY);  
PaintFlagsDrawFilter dfd = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG,  
Paint.FILTER_BITMAP_FLAG);  
canvas.setDrawFilter(dfd);  
canvas.clipPath(clip);//使用路径剪切画布  
canvas.drawBitmap(bitmap, srcRect, faceRect, mPaint);  

第三部效果图
第三步效果图

2019-01-16 14:17:42 u010104301 阅读数 422
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19563 人正在学习 去看看 夏曹俊
#100-> 200 x
#100-> 300 y
import cv2
img = cv2.imread('image0.jpg',1)
imgInfo =img.shape
dst = img[100:200,100:300]
cv2.imshow('image',dst)
cv2.waitKey(0)

 

 方法一:

# 1 API 2算法原理 3源代码来实现
import cv2
import numpy as np
img = cv2.imread('image0.jpg',1)
cv2.imshow('src',img)
imgInfo = img.shape
height = imgInfo[0]
width = imgInfo[1]
matShift = np.float32([[1,0,100],[0,1,200]])
dst = cv2.warpAffine(img,matShift,(height,width))# 1 data 2 mat 3 info
#移位 矩阵
cv2.imshow('dst',dst)
cv2.waitKey(0)

# [1,0,100],[0,1,200] 2*2 2*1
# [[1,0],[0,1]] 2*2 A
#[[100],[200]] 2*1  B
#xy c
#A*C+B = [1*X + 0*Y],[0*X+1*Y]+[[100],[200]]
# = [[X+100],[Y+200]]

 方法2:

import cv2
import numpy as np
img = cv2.imread('image0.jpg',1)
cv2.imshow('src',img)
imgInfo = img.shape
dst = np.zeros(img.shape,np.uint8)
height = imgInfo[0]
width = imgInfo[1]
for i in range(0,height):
    for j in range(0,width-100):
        dst[i,j+100]=img[i,j]
cv2.imshow('image',dst)
cv2.waitKey(0)

2001-11-10 14:36:00 cherami 阅读数 6031
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19563 人正在学习 去看看 夏曹俊

 

Java图像处理技巧四则

作者:Cherami

email:cherami@163.net

 

本人的另外一些作品请查看:http://www.smiling.com.cn/group/homepage.ecgi?group_id=23141

 

下面代码中用到的sourceImage是一个已经存在的Image对象

图像剪切

对于一个已经存在的Image对象,要得到它的一个局部图像,可以使用下面的步骤:

//import java.awt.*;

//import java.awt.image.*;

Image croppedImage;

ImageFilter cropFilter;

CropFilter =new CropImageFilter(25,30,75,75);//四个参数分别为图像起点坐标和宽高,即CropImageFilter(int x,int y,int width,int height),详细情况请参考API

CroppedImage= Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(sourceImage.getSource(),cropFilter));

//如果是在Component的子类中使用,可以将上面的Toolkit.getDefaultToolkit().去掉。

//FilteredImageSource是一个ImageProducer对象。

图像缩放

对于一个已经存在的Image对象,得到它的一个缩放的Image对象可以使用ImagegetScaledInstance方法:

Image scaledImage=sourceImage. getScaledInstance(100,100, Image.SCALE_DEFAULT);//得到一个100X100的图像

Image doubledImage=sourceImage. getScaledInstance(sourceImage.getWidth(this)*2,sourceImage.getHeight(this)*2, Image.SCALE_DEFAULT);//得到一个放大两倍的图像,这个程序一般在一个swing的组件中使用,而类Jcomponent实现了图像观察者接口ImageObserver,所有可以使用this

//其它情况请参考API

 

灰度变换

下面的程序使用三种方法对一个彩色图像进行灰度变换,变换的效果都不一样。一般而言,灰度变换的算法是将象素的三个颜色分量使用R*0.3+G*0.59B*0.11得到灰度值,然后将之赋值给红绿蓝,这样颜色取得的效果就是灰度的。另一种就是取红绿蓝三色中的最大值作为灰度值。java核心包也有一种算法,但是没有看源代码,不知道具体算法是什么样的,效果和上述不同。

/* GrayFilter.java*/

/*@author:cherami */

/*email:cherami@163.net*/

import java.awt.image.*;

 

public class GrayFilter extends RGBImageFilter {

       int modelStyle;

       public GrayFilter() {

              modelStyle=GrayModel.CS_MAX;

              canFilterIndexColorModel=true;

       }

       public GrayFilter(int style) {

              modelStyle=style;

              canFilterIndexColorModel=true;

       }

       public void setColorModel(ColorModel cm) {

              if (modelStyle==GrayModel.CS_MAX) {

                     substituteColorModel(cm,new GrayModel(cm));

              }

              else if (modelStyle==GrayModel.CS_FLOAT) {

                     substituteColorModel(cm,new GrayModel(cm,modelStyle));

              }

       }

       public int filterRGB(int x,int y,int pixel) {

              return pixel;

       }

}

 

/* GrayModel.java*/

/*@author:cherami */

/*email:cherami@163.net*/

 

import java.awt.image.*;

 

public class GrayModel extends ColorModel {

       public static final int CS_MAX=0;

       public static final int CS_FLOAT=1;

       ColorModel sourceModel;

       int modelStyle;

       public GrayModel(ColorModel sourceModel) {

              super(sourceModel.getPixelSize());

              this.sourceModel=sourceModel;

              modelStyle=0;

       }

       public GrayModel(ColorModel sourceModel,int style) {

              super(sourceModel.getPixelSize());

              this.sourceModel=sourceModel;

              modelStyle=style;           

       }

       public void setGrayStyle(int style) {

              modelStyle=style;

       }

       protected int getGrayLevel(int pixel) {

              if (modelStyle==CS_MAX) {

                     return Math.max(sourceModel.getRed(pixel),Math.max(sourceModel.getGreen(pixel),sourceModel.getBlue(pixel)));

              }

              else if (modelStyle==CS_FLOAT){

                     return (int)(sourceModel.getRed(pixel)*0.3+sourceModel.getGreen(pixel)*0.59+sourceModel.getBlue(pixel)*0.11);

              }

              else {

                     return 0;

              }

       }

       public int getAlpha(int pixel) {

              return sourceModel.getAlpha(pixel);

       }

       public int getRed(int pixel) {

              return getGrayLevel(pixel);

       }

       public int getGreen(int pixel) {

              return getGrayLevel(pixel);

       }

       public int getBlue(int pixel) {

              return getGrayLevel(pixel);

       }

       public int getRGB(int pixel) {

              int gray=getGrayLevel(pixel);

              return (getAlpha(pixel)<<24)+(gray<<16)+(gray<<8)+gray;

       }

}

如果你有自己的算法或者想取得特殊的效果,你可以修改类GrayModel的方法getGrayLevel()

 

色彩变换

根据上面的原理,我们也可以实现色彩变换,这样的效果就很多了。下面是一个反转变换的例子:

/* ReverseColorModel.java*/

/*@author:cherami */

/*email:cherami@163.net*/

import java.awt.image.*;

 

public class ReverseColorModel extends ColorModel {

       ColorModel sourceModel;

       public ReverseColorModel(ColorModel sourceModel) {

              super(sourceModel.getPixelSize());

              this.sourceModel=sourceModel;

       }

       public int getAlpha(int pixel) {

              return sourceModel.getAlpha(pixel);

       }

       public int getRed(int pixel) {

              return ~sourceModel.getRed(pixel);

       }

       public int getGreen(int pixel) {

              return ~sourceModel.getGreen(pixel);

       }

       public int getBlue(int pixel) {

              return ~sourceModel.getBlue(pixel);

       }

       public int getRGB(int pixel) {

              return (getAlpha(pixel)<<24)+(getRed(pixel)<<16)+(getGreen(pixel)<<8)+getBlue(pixel);

       }

}

/* ReverseColorModel.java*/

/*@author:cherami */

/*email:cherami@163.net*/

 

import java.awt.image.*;

 

public class ReverseFilter extends RGBImageFilter {

       public ReverseFilter() {

              canFilterIndexColorModel=true;

       }

       public void setColorModel(ColorModel cm) {

                     substituteColorModel(cm,new ReverseColorModel(cm));

       }

       public int filterRGB(int x,int y,int pixel) {

              return pixel;

       }

}

要想取得自己的效果,需要修改ReverseColorModel.java中的三个方法,getRedgetGreengetBlue

下面是上面的效果的一个总的演示程序。

/*GrayImage.java*/

/*@author:cherami */

/*email:cherami@163.net*/

import java.awt.*;

import java.awt.image.*;

import javax.swing.*;

import java.awt.color.*;

 

public class GrayImage extends JFrame{

       Image source,gray,gray3,clip,bigimg;

       BufferedImage bimg,gray2;

       GrayFilter filter,filter2;

       ImageIcon ii;

       ImageFilter cropFilter;

       int iw,ih;

       public GrayImage() {

              ii=new ImageIcon("images/11.gif");

              source=ii.getImage();

              iw=source.getWidth(this);

              ih=source.getHeight(this);

              filter=new GrayFilter();

              filter2=new GrayFilter(GrayModel.CS_FLOAT);

              gray=createImage(new FilteredImageSource(source.getSource(),filter));

              gray3=createImage(new FilteredImageSource(source.getSource(),filter2));

              cropFilter=new CropImageFilter(5,5,iw-5,ih-5);

              clip=createImage(new FilteredImageSource(source.getSource(),cropFilter));

              bigimg=source.getScaledInstance(iw*2,ih*2,Image.SCALE_DEFAULT);

              MediaTracker mt=new MediaTracker(this);

              mt.addImage(gray,0);

              try {

                     mt.waitForAll();

              } catch (Exception e) {

              }

       }

       public void paint(Graphics g) {

              Graphics2D g2=(Graphics2D)g;

              bimg=new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);

        Graphics2D srcG = bimg.createGraphics();

        RenderingHints rhs = g2.getRenderingHints();

        srcG.setRenderingHints(rhs);

        srcG.drawImage(source, 0, 0, null);

              ColorSpace graySpace=ColorSpace.getInstance(ColorSpace.CS_GRAY);

              ColorConvertOp op=new ColorConvertOp(graySpace,rhs);

              gray2=new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);

              op.filter(bimg,gray2);

              g2.drawImage(source,40,40,this);

              g2.drawImage(gray,80,40,this);

              g2.drawImage(gray2,120,40,this);

              g2.drawImage(gray3,160,40,this);

              g2.drawImage(clip,40,80,this);

              g2.drawImage(bigimg,80,80,this);

       }

       public void update(Graphics g) {

              paint(g);

       }

       public static void main(String args[]) {

              GrayImage m=new GrayImage();

              m.setSize(400,400);

              m.setVisible(true);

       }

}

 

2019-07-26 16:16:07 WangRain1 阅读数 763
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19563 人正在学习 去看看 夏曹俊

通过前两篇我们已经对Opencv有所了解了,接下来就要真正的来处理我们的图像,然后把卡号给提取出来。首先我们先简单分析以下银行卡然后把处理流程列出来:

 由上图我们很容易知道既然我们要找到卡号,银行卡的外边轮廓,然后根据比例找到卡号的位置,处理流程:

  1. 把采集到的图片根据银行卡边缘进行剪切,得到银行卡的区域
  2. 根据比例把卡号区域剪切出来,得到卡号的区域(具体怎么截取自己可以想不同的算法识别卡号的位置)
  3. 处理得到的卡号区域(降噪/二值化/膨胀/腐蚀等等)
  4. 分割每一个卡号(用于后期识别和训练模型使用)

基本流程就是这些,接下来就让我们从查找银行卡边缘开始:

1.对银行卡边缘进行裁剪

图像处理:

    // 首先降噪
    Mat blur;
    GaussianBlur(mat, blur, Size(5, 5), BORDER_DEFAULT, BORDER_DEFAULT);

    // 梯度增强 , x 轴和 y 轴
    Mat grad_x, grad_y;
    Scharr(blur, grad_x, CV_32F, 1, 0);
    Scharr(blur, grad_y, CV_32F, 0, 1);
    Mat grad_abs_x, grad_abs_y;
    convertScaleAbs(grad_x, grad_abs_x);
    convertScaleAbs(grad_y, grad_abs_y);
    Mat grad;
    addWeighted(grad_abs_x, 0.5, grad_abs_y, 0.5, 0, grad);

    // 把图片转成灰度图,减少图片的信息
    Mat gray;
    cvtColor(grad, gray, COLOR_BGRA2GRAY);
    // 二值化,待进行轮廓查找
    Mat binary;
    threshold(gray, binary, 40, 255, THRESH_BINARY);

效果图:

通过梯度增强把银行卡的边缘更加突出,为我们查找更准确的边缘轮廓矩形。这里二值化主要是去除一些背影信息,减少不必要的轮廓,方便查找。图片处理好之后接下来就是查找边缘轮廓了。

轮廓查找:

轮廓查找我们要筛选出符合要求的论然后返回

    // 轮廓查找
    vector<vector<Point> > contours;
    //查找轮廓
    findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    for (int i = 0; i < contours.size(); ++i) {
        Rect rect = boundingRect(contours[i]);
        //绘制轮廓
        drawContours(mat,contours,i,Scalar(0, 0, 255),1);
        // 是不是符合规则
        if (rect.width > mat.cols / 2 && rect.width != mat.cols && rect.height > mat.rows / 2) {
            card_rect = rect;
            break;
        }
        //防止轮廓都是不合尺寸的
        if (i == contours.size() - 1) {
            card_rect.x = 0;
            card_rect.y = 0;
            card_rect.width = binary.cols;
            card_rect.height = binary.rows;
        }
    }

剪切后的效果图红色是轮廓:

可以看到边缘已经根据外接矩形给剪切了。然后就是寻找卡号区域

2.剪切卡号区域

我这里是根据比例来剪切卡号区域的,你可以想其他办法去实现,那样剪切的区域跟准确,这里可以做持续优化,方法方式不限,只要能找到合适的卡号区域就行。

首先对剪切后的银行卡图片进行缩放,因为边框位置的不同剪切后银行卡区域可能图片大小不一致,所以先缩放到一定大小之后,按照比例剪切卡号区域

    //缩放
    int srcRows = 1388;
    int srcCols = 2201;
    Mat srcScale(srcRows, srcCols, srcImg.type());
    resize(srcImg, srcScale, srcScale.size(), 0, 0, INTER_LINEAR);
    //剪切矩形
    Rect cR;
    cR.x = srcScale.cols / 20;
    cR.y = srcScale.rows / 2;
    cR.width = srcScale.cols * 18 / 20;
    cR.height = srcScale.rows / 6;
    //剪切
    Mat rectMat(srcScale, cR);

可能不同的银行卡,卡号位置不一样,其实银行卡识别为了能达到更高的识别效率,需要写多套不同的图像处理代码,单单写一套代码识别率还是有限的,比较我们拍摄的角度/拍摄的光照/银行卡的样式多种多样,银行卡卡号有印刷的有突出的,银行卡背景又有很多不一样,所以在处理中其实还是要多写几套代码去适配。而对对于身份证的识别就非常简单了,因为身份证都是国家统一的。

3.处理卡号区域图像

得到卡号区域之后我们需要做的就是把卡号和背景分开,避免背景对识别产生影响,导致识别失败。所以我们要做一些图像处理:

    // 首先降噪 噪点可以选大一点
    Mat blur;
    GaussianBlur(rectMat, blur, Size(9, 9), BORDER_DEFAULT, BORDER_DEFAULT);
    //转灰度图
    Mat g;
    cvtColor(blur, g, COLOR_BGRA2GRAY); //转为灰度图
    //获取自定义核
    //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
    Mat erodeOut;
    //腐蚀操作
    erode(g, erodeOut, element);
    //二值化 THRESH_OTSU自动阀值
    Mat th;
    threshold(erodeOut, th, 39, 255, THRESH_OTSU);
    //膨胀
    Mat dilate_element = getStructuringElement(MORPH_RECT, Size(8, 8));
    Mat dilate_erodeOut;
    dilate(th, dilate_erodeOut, dilate_element);

从图像可以看出,还有些是干扰区域(注意这里可能你处理后的数字直接有粘练的问题,这个在这里就不说了,可以自己去研究),然后就是通过获取这个灰度图的所有矩形。

 vector<vector<Point> > contours;
    vector<Vec4i> hierarcy;
    findContours(dilate_erodeOut, contours, hierarcy, RETR_TREE, CHAIN_APPROX_NONE); //查找所有轮廓
    vector<Rect> boundRect(contours.size()); //定义外接矩形集合
    int x0 = 0, y0 = 0, w0 = 0, h0 = 0;
    int dilate_area = dilate_erodeOut.cols * dilate_erodeOut.rows;

    for (int i = 0; i < contours.size(); i++) {
        boundRect[i] = boundingRect(contours[i]); //查找每个轮廓的外接矩形
        x0 = boundRect[i].x;
        y0 = boundRect[i].y;
        w0 = boundRect[i].width;
        h0 = boundRect[i].height;
        //绘制第i个外接矩形
        rectangle(dilate_erodeOut, Point(x0, y0), Point(x0 + w0, y0 + h0), Scalar(0, 255, 0), 2, 8);

        int area = boundRect[i].area();
        if (h0 < dilate_erodeOut.rows * 2 / 5) {
            drawContours(dilate_erodeOut, contours, i, Scalar(0), 1);
            contours.erase(contours.begin() + i);
        } else if (area < dilate_area / 200) {
            drawContours(dilate_erodeOut, contours, i, Scalar(0), 1);
            contours.erase(contours.begin() + i);
        }
        // TODO : 根据高度和面积 处理腐蚀,伐值化,膨胀 不掉的干扰点。大面积的干扰点。
    }

但是我们会发现有很多的干扰矩形,就是除了我们数字区域之外的其他噪点区域,所以我们对得到的全部矩形做了“面积”,“高度”,大小的过滤。当然这里仅仅做了高度,面积的过滤其实还不够,还应该根据x,y坐标的特点进行再次过滤排除等等,这里可以自由发挥,根据自己的算法,可以做个更加优化,识别率更高。

过滤之后:

把我们的区域都找到了,把干扰区域都过滤掉了,生下来的就是我们的数字了。接下来就是把数字提取,分割出来,为识别和训练作数据采集。

4.分割每一个卡号

上面我们已经通过过滤得到了所有的卡号区域,下面就让我们把卡号区域给截取出来,然后保存到本地,为我们下一章节的训练使用:注意我们得到的矩形集合,他并不是有序的,就是如果我们直接按照集合內矩形的顺序截取,卡号是乱的,所有在截取之前需要根据x坐标排序。

    //排序
    for (int i = 0; i < split_mat.size() - 1; ++i) {

        for (int j = 0; j < split_mat.size() - 1 - i; ++j) {

            if (split_mat[j].x > split_mat[j + 1].x) {
                swapRect = split_mat[j];
                split_mat[j] = split_mat[j + 1];
                split_mat[j + 1] = swapRect;
            }
        }
    }

    //分割
  for (int i = 0; i < split_mat.size(); ++i) {
        //分割
        Mat sp(dilate_erodeOut, split_mat[i]);
        //缩放
        int nRows = 11;
        int nCols = 7;
        Mat dst(nRows, nCols, sp.type());
        resize(sp, dst, dst.size(), 0, 0, INTER_LINEAR);
        Mat bit;
        bitwise_not(dst, bit);
        //输出------训练数据提取样本时候使用
        char name[50];
        //坑 保存的时候图片尺寸不要太大(由于刚开始图片保存的是110:70的导致怎么训练都不成功),最好格式是png.
        sprintf(name,"/storage/emulated/0/dstImg_%d.png",i);
        imwrite(name, bit);
}

需要注意这里对分割的结果进行了缩放,起初我是直接保存的原始尺寸是110:70,但是在训练的时候始终训练失败,通过查找原因,一步一步的排除错误,最终发现图片太大。

下一篇就是拿到我们分割后的数字进行训练,这里你可以多分割几个卡号,多采集一些数据。

2017-09-27 15:25:13 guyuealian 阅读数 15836
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19563 人正在学习 去看看 夏曹俊

OpenCV图像剪切的扩展和高级用法:任意裁剪,边界扩充

尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/78113325
   利用感兴趣区域ROI和矩形类Rect,在OpenCV中可以很简单的就实现图像裁剪和剪切的功能,但剪切时常常会出现超出图像边界的区域的情况,对于超出图像边界的区域,我们必须进行特殊的处理,以避免出组数组越界的错误,如图1所示的裁剪错误。
cv::Mat src = cv::imread("D:\\OpencvTest\\1.jpg");//原始图像是200*200  
cv::Rect rect(-100, -100, 500, 500);  
cv::Mat image = src(rect);//这时裁剪,必然出错

图1
   对于特殊的要求,如,我们希望可以指定的颜色来填充,或者复制边界的像素的填充,甚至想镜像某个位置填充超出的边界,应该怎么办呢?OpenCV3中提供了一个图像边界的函数cv::copyMakeBorder(对应opencv2中的cvCopyMakeBorder)以及borderInterpolate,利用这个函数,可以间接实现这个功能。
    copyMakeBorder函数的用法,请参考博客:http://blog.csdn.net/qianqing13579/article/details/42323397
    注意:copyMakeBorder函数不能直接用于图像裁剪,博客后面会贴出封装好的ImageCropPadding()函数,方便亲们调用

函数原型:copyMakeBorder

void copyMakeBorder( const Mat& src, Mat& dst,
     int top, int bottom, int left, int right,
     int borderType, const Scalar& value=Scalar() );

函数功能:

      用于扩充src图像的边缘,使得图像变大变宽,该函数调用了cv::borderInterpolate函数
参数说明:
   src,dst:原图与目标图像
   top,bottom,left,right分别表示在原图四周扩充边缘的大小
   borderType:扩充边缘的类型,OpenCV中给出以下几种方式
  * BORDER_REPLICATE
  * BORDER_REFLECT
  * BORDER_REFLECT_101
  * BORDER_WRAP
  * BORDER_CONSTANT
    说明如下:
Enumerator
BORDER_CONSTANT 

iiiiii|abcdefgh|iiiiiii with some specified i(指定常数填充)

BORDER_REPLICATE 

aaaaaa|abcdefgh|hhhhhhh(复制边缘像素填充)

BORDER_REFLECT 

fedcba|abcdefgh|hgfedcb(反射复制边界像素)

BORDER_WRAP 

cdefgh|abcdefgh|abcdefg

BORDER_REFLECT_101 

gfedcb|abcdefgh|gfedcba(对称填充,也就是以最边缘像素为轴)

BORDER_TRANSPARENT 

uvwxyz|absdefgh|ijklmno

BORDER_REFLECT101 

same as BORDER_REFLECT_101

BORDER_DEFAULT 

same as BORDER_REFLECT_101

BORDER_ISOLATED 

do not look outside of ROI

(1)BORDER_REPLICATE:复制法,也就是复制最边缘像素。

     如上图,红色区域为src的最边界像素,蓝色区域是扩充的边界,我们将边缘扩大了5个像素(right=5),蓝色区域的宽度就是5,复制了5次红色区域的值。这种方式也就是OpenCV中的中值滤波medianBlur采用的边界处理方式
  (2)BORDER_REFLECT_101:对称法,也就是以最边缘像素为轴,对称扩展。如下面的图

     绿色区域是src最边界的像素,蓝色区域是我们扩充的5个像素的扩充边界,而红色区域就是蓝色区域在src的对称部分。这种方式也是OpenCV边界处理的默认方式(BORDER_DEFAULT=BORDER_REFLECT_101)
也是filter2D,blur,GaussianBlur,bilateralFilter的默认处理方式,所以这种方式在边界处理中应用还是非常广泛的
(3)BORDER_CONSTANT:常量法,可指定颜色填充
    常量法就是以一个常量像素值(由参数 value给定)填充扩充的边界值,这种方式在仿射变换,透视变换中非常常见
如下图:

     这里使用了默认的value,黑色填充了边界,所以红色区域的扩充的5个像素宽的边界是黑色的在copyMakeBorder的内部,调用了函数borderInterpolate
  前面提到,copyMakeBorder函数只是进行图像的简单扩充而已,而我们需要的是在图像裁剪时,对超过边界区域实现颜色自动填充。这里实现了一个Demo,对于超出剪切的区域,用红色(或其他)填充,这里贴出封装好的ImageCropPadding()函数,方便亲们调用下面
#include "stdafx.h"  
#include <iostream>    
#include <opencv2\opencv.hpp>    
#include <opencv2\highgui\highgui.hpp>    
using namespace std;
using namespace cv;


cv::Mat ImageCropPadding(cv::Mat srcImage, cv::Rect rect)
{
	//cv::Mat srcImage = image.clone();
	int crop_x1 = cv::max(0, rect.x);
	int crop_y1 = cv::max(0, rect.y);
	int crop_x2 = cv::min(srcImage.cols, rect.x + rect.width); // 图像范围 0到cols-1, 0到rows-1      
	int crop_y2 = cv::min(srcImage.rows, rect.y + rect.height);


	int left_x = (-rect.x);
	int top_y = (-rect.y);
	int right_x = rect.x + rect.width - srcImage.cols;
	int down_y = rect.y + rect.height - srcImage.rows;
	//cv::Mat roiImage = srcImage(cv::Range(crop_y1, crop_y2 + 1), cv::Range(crop_x1, crop_x2 + 1));    
	cv::Mat roiImage = srcImage(cv::Rect(crop_x1, crop_y1, (crop_x2 - crop_x1), (crop_y2 - crop_y1)));


	if (top_y > 0 || down_y > 0 || left_x > 0 || right_x > 0)//只要存在边界越界的情况,就需要边界填充    
	{
		left_x = (left_x > 0 ? left_x : 0);
		right_x = (right_x > 0 ? right_x : 0);
		top_y = (top_y > 0 ? top_y : 0);
		down_y = (down_y > 0 ? down_y : 0);
		//cv::Scalar(0,0,255)指定颜色填充    
		cv::copyMakeBorder(roiImage, roiImage, top_y, down_y, left_x, right_x, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 255));
		//cv::copyMakeBorder(roiImage, roiImage, top_y, down_y, left_x, right_x, cv::BORDER_REPLICATE);//复制最边缘像素    
		//cv::copyMakeBorder(roiImage, roiImage, top_y, down_y, left_x, right_x, BORDER_REFLECT_101);  //边缘对称法填充     
	}
	//else//若不存在边界越界的情况,则不需要填充了    
	//{    
	//  destImage = roiImage;    
	//}    
	return roiImage;
}
int main(int argc)
{
	Mat src = imread("D:\\OpencvTest\\B1.jpg");//原始图像是200*200  
	cv::imshow("src", src);
	cv::Rect rect(-50, -50, 300, 300);
	printf("src:[%d,%d]", src.cols, src.rows);	printf("\n");
	cv::Mat crop_im1 = ImageCropPadding(src, rect);
	printf("rect:[%d,%d,%d,%d]", rect.x, rect.y, rect.width, rect.height);printf("\n");
	printf("crop_im1:[%d,%d]", crop_im1.cols, crop_im1.rows); printf("\n");
	cv::imshow("crop_im1", crop_im1);
	cvWaitKey(0);
	return 0;
}

   这美女不错吧,哈哈!注意,关注点不是美女,而是裁剪超出图像的红色区域。不过,对本人而言,我更喜欢复制边缘像素的填充方法:
   将原来copyMakeBorder函数参数改为:cv::BORDER_REPLICATE
cv::copyMakeBorder(roiImage, destImage, top_y, down_y, left_x, right_x, cv::BORDER_REPLICATE);//复制边缘像素填充
效果如下图所示,是不是看到美女的手好细好长,美美哒~注意,关注点不是美女,而是裁剪超出图像被复制的像素区域。

   还有更妖媚的填充方法:边缘对称法填充 ,效果如下图所示
cv::copyMakeBorder(roiImage, destImage, top_y, down_y, left_x, right_x, BORDER_REFLECT_101); //边缘对称法填充 
如果你觉得该帖子帮到你,还望贵人多多支持,鄙人会再接再厉,继续努力的~

图像剪切(2)

阅读数 650

C#.NET图像处理

阅读数 3729

Flex 图像裁剪、剪切

博文 来自: wuxiubing
没有更多推荐了,返回首页