精华内容
下载资源
问答
  • 二维码之父
    2017-11-21 19:37:52

    考虑到如果一个用户发布一个app的话,便于推广,一般需要将其下载地址封装到二维码中去,当前比较火热的移动端系统,安卓和iso系统,下载链接是不一样的,ios需要链接到苹果商店里面,为了实现一码多用,实现这么一个功能,做了这个小系统(也是为了完成作业,好像给加分闭嘴--!)


     执行步骤:

       1、用户在前台表单提交APP的IOS和Android下载地址。

      2、后台接收IOS和Android下载地址然后生成一个UUID,把这三个信息存储在数据库中。

      3、定义一个@RequestMapping地址,通过地址中传递的UUID,在数据库中查询到IOS和Android

      4、把当前服务器的网址+@RequestMapping地址+UUID合成二维码返回给前端显示

      5、用户扫描的二维码实际上是访问了我们事先定义的@RequestMapping地址,通过用户的http请求,我们可以获取到用户的手机操作系统是IOS还是Android,这样就可以跳转到指定的地址了。

      项目选型,我使用Spring SpringMVC MyBatis现在最流行的框架,然后用maven搭建项目,在二维码的生成和解析的时候,使用google的zxing jar。

      添加的依赖pom.xml

    [html]  view plain  copy
    1.     
    2.   <dependencies>  
    3.      <!-- freemarker依赖 -->  
    4.      <dependency>  
    5.        <groupId>org.freemarker</groupId>  
    6.        <artifactId>freemarker</artifactId>  
    7.        <version>${freemarker.version}</version>  
    8.      </dependency>  
    9.         
    10.     <!-- spring mvc 框架 -->  
    11.     <dependency>  
    12.         <groupId>org.springframework</groupId>  
    13.         <artifactId>spring-webmvc</artifactId>  
    14.         <version>${spring.version}</version>  
    15.     </dependency>  
    16.         
    17.         <dependency>    
    18.             <groupId>org.springframework</groupId>    
    19.             <artifactId>spring-jdbc</artifactId>    
    20.             <version>${spring.version}</version>    
    21.         </dependency>  
    22.           
    23.         <dependency>    
    24.             <groupId>org.springframework</groupId>    
    25.             <artifactId>spring-context-support</artifactId>    
    26.             <version>${spring.version}</version>    
    27.         </dependency>  
    28.           
    29.         <dependency>    
    30.             <groupId>org.springframework</groupId>    
    31.             <artifactId>spring-test</artifactId>    
    32.             <version>${spring.version}</version>    
    33.         </dependency>   
    34.           
    35.         <dependency>  
    36.             <groupId>c3p0</groupId>  
    37.             <artifactId>c3p0</artifactId>  
    38.              <version>0.9.1.2</version>  
    39.         </dependency>  
    40.           
    41.          <!-- 导入Mysql数据库链接jar包 -->    
    42.         <dependency>  
    43.             <groupId>mysql</groupId>  
    44.             <artifactId>mysql-connector-java</artifactId>  
    45.             <version>5.1.4</version>  
    46.         </dependency>   
    47.           
    48.         <!-- mybatis核心包 -->    
    49.         <dependency>    
    50.             <groupId>org.mybatis</groupId>    
    51.             <artifactId>mybatis</artifactId>    
    52.             <version>${mybatis.version}</version>    
    53.         </dependency>    
    54.         <!-- mybatis/spring包 -->    
    55.         <dependency>    
    56.             <groupId>org.mybatis</groupId>    
    57.             <artifactId>mybatis-spring</artifactId>    
    58.             <version>1.2.2</version>    
    59.         </dependency>   
    60.           
    61.          <!-- 上传组件包 -->    
    62.         <dependency>    
    63.             <groupId>commons-fileupload</groupId>    
    64.             <artifactId>commons-fileupload</artifactId>    
    65.             <version>1.3.1</version>    
    66.         </dependency>    
    67.         <dependency>    
    68.             <groupId>commons-io</groupId>    
    69.             <artifactId>commons-io</artifactId>    
    70.             <version>2.4</version>    
    71.         </dependency>    
    72.         <dependency>    
    73.             <groupId>commons-codec</groupId>    
    74.             <artifactId>commons-codec</artifactId>    
    75.             <version>1.9</version>    
    76.         </dependency>   
    77.       
    78.     <!-- servlet -->  
    79.     <dependency>  
    80.         <groupId>javax.servlet</groupId>  
    81.         <artifactId>servlet-api</artifactId>  
    82.         <version>2.5</version>  
    83.     </dependency>  
    84.       
    85.     <!-- jsp/jstl/core 页面标签 -->  
    86.     <dependency>  
    87.         <groupId>javax.servlet</groupId>  
    88.         <artifactId>jstl</artifactId>  
    89.         <version>1.2</version>  
    90.     </dependency>  
    91.     <dependency>  
    92.         <groupId>taglibs</groupId>  
    93.         <artifactId>standard</artifactId>  
    94.         <version>1.1.2</version>  
    95.     </dependency>  
    96.       
    97.     <!-- SLF4J API -->  
    98.     <!-- SLF4J 是一个日志抽象层,允许你使用任何一个日志系统,并且可以随时切换还不需要动到已经写好的程序 -->  
    99.     <dependency>  
    100.         <groupId>org.slf4j</groupId>  
    101.         <artifactId>slf4j-api</artifactId>  
    102.         <version>1.7.22</version>  
    103.     </dependency>  
    104.       
    105.     <!-- Log4j 日志系统(最常用) -->  
    106.     <dependency>  
    107.         <groupId>org.slf4j</groupId>  
    108.         <artifactId>slf4j-log4j12</artifactId>  
    109.         <version>1.7.22</version>  
    110.     </dependency>  
    111.       
    112.     <!-- jackson -->  
    113.     <dependency>  
    114.         <groupId>com.fasterxml.jackson.core</groupId>  
    115.         <artifactId>jackson-core</artifactId>  
    116.         <version>${jackson.version}</version>  
    117.     </dependency>  
    118.     <dependency>  
    119.         <groupId>com.fasterxml.jackson.core</groupId>  
    120.         <artifactId>jackson-annotations</artifactId>  
    121.         <version>${jackson.version}</version>  
    122.     </dependency>  
    123.     <dependency>  
    124.         <groupId>com.fasterxml.jackson.core</groupId>  
    125.         <artifactId>jackson-databind</artifactId>  
    126.         <version>${jackson.version}</version>  
    127.     </dependency>  
    128.         
    129.     <dependency>  
    130.       <groupId>junit</groupId>  
    131.       <artifactId>junit</artifactId>  
    132.       <version>3.8.1</version>  
    133.       <scope>test</scope>  
    134.     </dependency>  
    135.       
    136.     <!-- 谷歌的zxingjar包 -->  
    137.     <dependency>  
    138.         <groupId>com.google.zxing</groupId>  
    139.         <artifactId>core</artifactId>  
    140.         <version>3.1.0</version>  
    141.     </dependency>  
    142.       
    143.     <dependency>    
    144.         <groupId>com.google.zxing</groupId>    
    145.         <artifactId>javase</artifactId>    
    146.         <version>3.0.0</version>    
    147.     </dependency>   
    148.       
    149.        
    150.       
    151.     <!-- swagger2整合springmvc快速生成rest风格接口文档 -->  
    152.     <dependency>  
    153.         <groupId>io.springfox</groupId>  
    154.         <artifactId>springfox-swagger2</artifactId>  
    155.         <version>2.5.0</version>  
    156.     </dependency>  
    157.     <dependency>  
    158.         <groupId>io.springfox</groupId>  
    159.         <artifactId>springfox-swagger-ui</artifactId>  
    160.         <version>2.5.0</version>  
    161.     </dependency>  
    162.       
    163.   </dependencies>  
    164.     
    165.   <build>  
    166.     <finalName>spring-mvc-web</finalName>  
    167.   </build>  
    168. </project>  

    二维码生成、解析工具类ZXingCodeUtil.java 

    [java]  view plain  copy

    1.   
    2. import java.awt.Color;  
    3. import java.awt.Font;  
    4. import java.awt.Graphics2D;  
    5. import java.awt.image.BufferedImage;  
    6. import java.io.ByteArrayOutputStream;  
    7. import java.io.File;  
    8. import java.io.IOException;  
    9.   
    10. import java.util.HashMap;  
    11. import java.util.Map;  
    12. import javax.imageio.ImageIO;  
    13. import javax.servlet.http.HttpServletRequest;  
    14. import com.google.zxing.BarcodeFormat;  
    15. import com.google.zxing.Binarizer;  
    16. import com.google.zxing.BinaryBitmap;  
    17. import com.google.zxing.DecodeHintType;  
    18. import com.google.zxing.EncodeHintType;  
    19. import com.google.zxing.LuminanceSource;  
    20. import com.google.zxing.MultiFormatReader;  
    21. import com.google.zxing.MultiFormatWriter;  
    22. import com.google.zxing.NotFoundException;  
    23. import com.google.zxing.Result;  
    24. import com.google.zxing.WriterException;  
    25. import com.google.zxing.client.j2se.BufferedImageLuminanceSource;  
    26. import com.google.zxing.common.BitMatrix;  
    27. import com.google.zxing.common.HybridBinarizer;  
    28. import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;  
    29.   
    30.   
    31. /** 
    32.  * @Description: (二维码生成工具类) 
    33.  * @author 苏叶biubiu 
    34.  */  
    35. public class ZXingCodeUtil {  
    36.     private static final int QRCOLOR = 0xFF000000;   //默认是黑色  
    37.     private static final int BGWHITE = 0xFFFFFFFF;   //背景颜色  
    38.   
    39.   
    40.     public static void main(String[] args) throws WriterException{    
    41.         try {    
    42.             //getLogoQRCode("https://www.baidu.com/", null, "跳转到百度的二维码");  
    43.             //getQRCode("https://www.baidu.com/", null, "跳转到百度的二维码");  
    44.               
    45.         }    
    46.         catch (Exception e)  {    
    47.             e.printStackTrace();    
    48.         }    
    49.     }    
    50.       
    51.     /** 
    52.      * 二维码解析 
    53.      * @param file 二维码图片文件 
    54.      * @return 解析结果 
    55.      */  
    56.     public static String parseQRCode(File file) {  
    57.         BufferedImage image;    
    58.         try {    
    59.             image = ImageIO.read(file);    
    60.             LuminanceSource source = new BufferedImageLuminanceSource(image);    
    61.             Binarizer binarizer = new HybridBinarizer(source);    
    62.             BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);    
    63.             Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();    
    64.             hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");    
    65.             Result result = new MultiFormatReader().decode(binaryBitmap, hints);// 对图像进行解码    
    66.             return result.getText();  
    67.         } catch (IOException e) {    
    68.             e.printStackTrace();   
    69.             return null;  
    70.         } catch (NotFoundException e) {    
    71.             e.printStackTrace();  
    72.             return null;  
    73.         }    
    74.     }  
    75.       
    76.       
    77.     /** 
    78.      * 生成不带logo的二维码 
    79.      * @param qrUrl 链接地址 
    80.      * @param request 请求 
    81.      * @param productName 二维码名称 
    82.      * @param file 上传路径+文件名 
    83.      * @return 
    84.      */  
    85.     public static String getQRCode(String qrUrl,HttpServletRequest request,String productName,File file ) {  
    86.         // String filePath = request.getSession().getServletContext().getRealPath("/") + "resources/images/logoImages/llhlogo.png";  
    87.         //filePath是二维码logo的路径,但是实际中我们是放在项目的某个路径下面的,所以路径用上面的,把下面的注释就好  
    88.           
    89.         String content = qrUrl;  
    90.         try {    
    91.             ZXingCodeUtil zp = new ZXingCodeUtil();  
    92.             BufferedImage image = zp.getQR_CODEBufferedImage(content, BarcodeFormat.QR_CODE, 400400, zp.getDecodeHintType());  
    93.             image.flush();  
    94.             ByteArrayOutputStream baos = new ByteArrayOutputStream();  
    95.             baos.flush();  
    96.             ImageIO.write(image, "png", baos);  
    97.             //二维码生成的路径,但是实际项目中,我们是把这生成的二维码显示到界面上的,因此下面的折行代码可以注释掉  
    98.             //可以看到这个方法最终返回的是这个二维码的imageBase64字符串  
    99.             //前端用 <img src="data:image/png;base64,${imageBase64QRCode}"/> 其中${imageBase64QRCode}对应二维码的imageBase64字符串  
    100.             //new File("E:/" + new Date().getTime() + "test.png")  
    101.             //判断目标文件所在的目录是否存在    
    102.             if(!file.getParentFile().exists()) {    
    103.                 //如果目标文件所在的目录不存在,则创建父目录    
    104.                 System.out.println("目标文件所在目录不存在,准备创建它!");    
    105.                 if(!file.getParentFile().mkdirs()) {    
    106.                     System.out.println("创建目标文件所在目录失败!");    
    107.                 }    
    108.             }  
    109.             ImageIO.write(image, "png", file);   
    110.              
    111.             String imageBase64QRCode =  new sun.misc.BASE64Encoder().encodeBuffer(baos.toByteArray());  
    112.             baos.close();  
    113.             return imageBase64QRCode;  
    114.         }  
    115.         catch (Exception e){  
    116.             e.printStackTrace();  
    117.         }  
    118.         return null;  
    119.     }  
    120.   
    121.     /** 
    122.      * 生成带logo的二维码图片 
    123.      * @param qrUrl 链接地址 
    124.      * @param request 请求 
    125.      * @param productName 二维码名称 
    126.      * @param logoFile logo文件 
    127.      * @param createFile 生成的文件路径 
    128.      * @return 
    129.      */  
    130.     public static String getLogoQRCode(String qrUrl,HttpServletRequest request,String productName,File logoFile,File createFile) {  
    131.         // String filePath = request.getSession().getServletContext().getRealPath("/") + "resources/images/logoImages/llhlogo.png";  
    132.         //filePath是二维码logo的路径,但是实际中我们是放在项目的某个路径下面的,所以路径用上面的,把下面的注释就好  
    133.         String content = qrUrl;  
    134.         try{    
    135.             ZXingCodeUtil zp = new ZXingCodeUtil();  
    136.             BufferedImage bim = zp.getQR_CODEBufferedImage(content, BarcodeFormat.QR_CODE, 400400, zp.getDecodeHintType());  
    137.             return zp.addLogo_QRCode(bim, logoFile , new LogoConfig(), productName, createFile);  
    138.         }  
    139.         catch (Exception e) {  
    140.             e.printStackTrace();  
    141.         }  
    142.         return null;  
    143.     }  
    144.   
    145.     /** * 给二维码图片添加Logo * * @param qrPic * @param logoPic */  
    146.     public String addLogo_QRCode(BufferedImage bim, File logoPic, LogoConfig logoConfig, String productName, File createFile) {  
    147.         try {  
    148.             /** * 读取二维码图片,并构建绘图对象 */  
    149.             BufferedImage image = bim;  
    150.             Graphics2D g = image.createGraphics();  
    151.   
    152.             /** * 读取Logo图片 */  
    153.             BufferedImage logo = ImageIO.read(logoPic);  
    154.             /** * 设置logo的大小,本人设置为二维码图片的20%,因为过大会盖掉二维码 */  
    155.             int widthLogo = logo.getWidth(null)>image.getWidth()*3/10?(image.getWidth()*3/10):logo.getWidth(null),   
    156.                 heightLogo = logo.getHeight(null)>image.getHeight()*3/10?(image.getHeight()*3/10):logo.getWidth(null);  
    157.   
    158.             /** * logo放在中心 */  
    159.              int x = (image.getWidth() - widthLogo) / 2;  
    160.              int y = (image.getHeight() - heightLogo) / 2;  
    161.              /** * logo放在右下角 * int x = (image.getWidth() - widthLogo); * int y = (image.getHeight() - heightLogo); */  
    162.   
    163.             //开始绘制图片  
    164.             g.drawImage(logo, x, y, widthLogo, heightLogo, null);  
    165.             // g.drawRoundRect(x, y, widthLogo, heightLogo, 15, 15);  
    166.             // g.setStroke(new BasicStroke(logoConfig.getBorder()));  
    167.             // g.setColor(logoConfig.getBorderColor());  
    168.             // g.drawRect(x, y, widthLogo, heightLogo);  
    169.             g.dispose();  
    170.   
    171.             //把商品名称添加上去,商品名称不要太长哦,这里最多支持两行。太长就会自动截取啦  
    172.             if (productName != null && !productName.equals("")) {  
    173.                 //新的图片,把带logo的二维码下面加上文字  
    174.                 BufferedImage outImage = new BufferedImage(400445, BufferedImage.TYPE_4BYTE_ABGR);  
    175.                 Graphics2D outg = outImage.createGraphics();  
    176.                 //画二维码到新的面板  
    177.                 outg.drawImage(image, 00, image.getWidth(), image.getHeight(), null);  
    178.                 //画文字到新的面板  
    179.                 outg.setColor(Color.BLACK);   
    180.                 outg.setFont(new Font("宋体",Font.BOLD,30)); //字体、字型、字号   
    181.                 int strWidth = outg.getFontMetrics().stringWidth(productName);  
    182.                 if (strWidth > 399) {  
    183.                     // //长度过长就截取前面部分  
    184.                     // outg.drawString(productName, 0, image.getHeight() + (outImage.getHeight() - image.getHeight())/2 + 5 ); //画文字  
    185.                     //长度过长就换行  
    186.                     String productName1 = productName.substring(0, productName.length()/2);  
    187.                     String productName2 = productName.substring(productName.length()/2, productName.length());  
    188.                     int strWidth1 = outg.getFontMetrics().stringWidth(productName1);  
    189.                     int strWidth2 = outg.getFontMetrics().stringWidth(productName2);  
    190.                     outg.drawString(productName1, 200  - strWidth1/2, image.getHeight() + (outImage.getHeight() - image.getHeight())/2 + 12 );  
    191.                     BufferedImage outImage2 = new BufferedImage(400485, BufferedImage.TYPE_4BYTE_ABGR);  
    192.                     Graphics2D outg2 = outImage2.createGraphics();  
    193.                     outg2.drawImage(outImage, 00, outImage.getWidth(), outImage.getHeight(), null);  
    194.                     outg2.setColor(Color.BLACK);   
    195.                     outg2.setFont(new Font("宋体",Font.BOLD,30)); //字体、字型、字号   
    196.                     outg2.drawString(productName2, 200  - strWidth2/2, outImage.getHeight() + (outImage2.getHeight() - outImage.getHeight())/2 + 5 );  
    197.                     outg2.dispose();   
    198.                     outImage2.flush();  
    199.                     outImage = outImage2;  
    200.                 }else {  
    201.                     outg.drawString(productName, 200  - strWidth/2 , image.getHeight() + (outImage.getHeight() - image.getHeight())/2 + 12 ); //画文字   
    202.                 }  
    203.                 outg.dispose();   
    204.                 outImage.flush();  
    205.                 image = outImage;  
    206.             }  
    207.             logo.flush();  
    208.             image.flush();  
    209.             ByteArrayOutputStream baos = new ByteArrayOutputStream();  
    210.             baos.flush();  
    211.             ImageIO.write(image, "png", baos);  
    212.   
    213.             //二维码生成的路径,但是实际项目中,我们是把这生成的二维码显示到界面上的,因此下面的折行代码可以注释掉  
    214.             //可以看到这个方法最终返回的是这个二维码的imageBase64字符串  
    215.             //前端用 <img src="data:image/png;base64,${imageBase64QRCode}"/> 其中${imageBase64QRCode}对应二维码的imageBase64字符串  
    216.             if(!createFile.getParentFile().exists()) {    
    217.                 //如果目标文件所在的目录不存在,则创建父目录    
    218.                 System.out.println("目标文件所在目录不存在,准备创建它!");    
    219.                 if(!createFile.getParentFile().mkdirs()) {    
    220.                     System.out.println("创建目标文件所在目录失败!");    
    221.                 }    
    222.             }  
    223.             ImageIO.write(image, "png", createFile);   
    224.              
    225.             String imageBase64QRCode =  new sun.misc.BASE64Encoder().encodeBuffer(baos.toByteArray());  
    226.             baos.close();  
    227.             return imageBase64QRCode;  
    228.         }  
    229.         catch (Exception e){  
    230.             e.printStackTrace();  
    231.         }  
    232.         return null;  
    233.     }  
    234.   
    235.   
    236.     /** * 构建初始化二维码 * * @param bm * @return */  
    237.     public BufferedImage fileToBufferedImage(BitMatrix bm){  
    238.         BufferedImage image = null;  
    239.         try  
    240.         {  
    241.             int w = bm.getWidth(), h = bm.getHeight();  
    242.             image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);  
    243.   
    244.             for (int x = 0; x < w; x++)  
    245.             {  
    246.                 for (int y = 0; y < h; y++)  
    247.                 {  
    248.                     image.setRGB(x, y, bm.get(x, y) ? 0xFF000000 : 0xFFCCDDEE);  
    249.                 }  
    250.             }  
    251.   
    252.         }  
    253.         catch (Exception e){  
    254.             e.printStackTrace();  
    255.         }  
    256.         return image;  
    257.     }  
    258.   
    259.     /** * 生成二维码bufferedImage图片 * * @param content * 编码内容 * @param barcodeFormat * 编码类型 * @param width * 图片宽度 * @param height * 图片高度 * @param hints * 设置参数 * @return */  
    260.     public BufferedImage getQR_CODEBufferedImage(String content, BarcodeFormat barcodeFormat, int width, int height, Map<EncodeHintType, ?> hints) {  
    261.         MultiFormatWriter multiFormatWriter = null;  
    262.         BitMatrix bm = null;  
    263.         BufferedImage image = null;  
    264.         try{  
    265.             multiFormatWriter = new MultiFormatWriter();  
    266.             // 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数  
    267.             bm = multiFormatWriter.encode(content, barcodeFormat, width, height, hints);  
    268.             int w = bm.getWidth();  
    269.             int h = bm.getHeight();  
    270.             image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);  
    271.   
    272.             // 开始利用二维码数据创建Bitmap图片,分别设为黑(0xFFFFFFFF)白(0xFF000000)两色  
    273.             for (int x = 0; x < w; x++){  
    274.                 for (int y = 0; y < h; y++) {  
    275.                     image.setRGB(x, y, bm.get(x, y) ? QRCOLOR : BGWHITE);  
    276.                 }  
    277.             }  
    278.         }  
    279.         catch (WriterException e){  
    280.             e.printStackTrace();  
    281.         }  
    282.         return image;  
    283.     }  
    284.   
    285.     /** * 设置二维码的格式参数 * * @return */  
    286.     public Map<EncodeHintType, Object> getDecodeHintType() {  
    287.         // 用于设置QR二维码参数  
    288.         Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();  
    289.         // 设置QR二维码的纠错级别(H为最高级别)具体级别信息  
    290.         hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);  
    291.         // 设置编码方式  
    292.         hints.put(EncodeHintType.CHARACTER_SET, "utf-8");  
    293.         hints.put(EncodeHintType.MARGIN, 0);  
    294.         hints.put(EncodeHintType.MAX_SIZE, 350);  
    295.         hints.put(EncodeHintType.MIN_SIZE, 100);  
    296.   
    297.         return hints;  
    298.     }  
    299. }  
    300.   
    301.     class LogoConfig {  
    302.         // logo默认边框颜色  
    303.         public static final Color DEFAULT_BORDERCOLOR = Color.WHITE;  
    304.         // logo默认边框宽度  
    305.         public static final int DEFAULT_BORDER = 2;  
    306.         // logo大小默认为照片的1/5  
    307.         public static final int DEFAULT_LOGOPART = 5;  
    308.   
    309.         private final int border = DEFAULT_BORDER;  
    310.         private final Color borderColor;  
    311.         private final int logoPart;  
    312.   
    313.         /** * Creates a default config with on color {@link #BLACK} and off color * {@link #WHITE}, generating normal black-on-white barcodes. */  
    314.         public LogoConfig(){  
    315.             this(DEFAULT_BORDERCOLOR, DEFAULT_LOGOPART);  
    316.         }  
    317.   
    318.         public LogoConfig(Color borderColor, int logoPart){  
    319.             this.borderColor = borderColor;  
    320.             this.logoPart = logoPart;  
    321.         }  
    322.   
    323.         public Color getBorderColor() {  
    324.             return borderColor;  
    325.         }  
    326.   
    327.         public int getBorder(){  
    328.             return border;  
    329.         }  
    330.   
    331.         public int getLogoPart() {  
    332.             return logoPart;  
    333.         }  
    334.     }  
     8位短uuid生成工具类

    [java]  view plain  copy

    1.   
    2. import java.awt.Color;  
    3. import java.awt.Font;  
    4. import java.awt.Graphics2D;  
    5. import java.awt.image.BufferedImage;  
    6. import java.io.ByteArrayOutputStream;  
    7. import java.io.File;  
    8. import java.io.IOException;  
    9. import java.util.Date;  
    10. import java.util.HashMap;  
    11. import java.util.Map;  
    12. import java.util.UUID;  
    13.   
    14. import javax.imageio.ImageIO;  
    15. import javax.servlet.http.HttpServletRequest;  
    16. import com.google.zxing.BarcodeFormat;  
    17. import com.google.zxing.EncodeHintType;  
    18. import com.google.zxing.MultiFormatWriter;  
    19. import com.google.zxing.WriterException;  
    20. import com.google.zxing.common.BitMatrix;  
    21. import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;  
    22.   
    23.   
    24. /** 
    25.  * @Description: 短uuid生成 
    26.  * @author 苏叶biubiu 
    27.  */  
    28. public class UuidUtil {  
    29.      
    30.   
    31.   
    32.     public static void main(String[] args) throws WriterException{    
    33.         String uuid = generateShortUuid();  
    34.         System.out.println(uuid);  
    35.     }    
    36.   
    37.     public static String[] chars = new String[] { "a""b""c""d""e""f",    
    38.         "g""h""i""j""k""l""m""n""o""p""q""r""s",    
    39.         "t""u""v""w""x""y""z""0""1""2""3""4""5",    
    40.         "6""7""8""9""A""B""C""D""E""F""G""H""I",    
    41.         "J""K""L""M""N""O""P""Q""R""S""T""U""V",    
    42.         "W""X""Y""Z" };    
    43.   
    44.   
    45.     public static String generateShortUuid() {    
    46.         StringBuffer shortBuffer = new StringBuffer();    
    47.         String uuid = UUID.randomUUID().toString().replace("-""");    
    48.         for (int i = 0; i < 8; i++) {    
    49.             String str = uuid.substring(i * 4, i * 4 + 4);    
    50.             int x = Integer.parseInt(str, 16);    
    51.             shortBuffer.append(chars[x % 0x3E]);    
    52.         }    
    53.         return shortBuffer.toString();    
    54.       
    55.     }    
    56.       
    57.       
    58.       
    59. }  
    60.   
    61.      

    核心控制器

    [java]  view plain  copy

    1.   
    2. import java.io.BufferedInputStream;  
    3. import java.io.File;    
    4. import java.io.FileNotFoundException;  
    5. import java.io.FileOutputStream;  
    6. import java.io.IOException;  
    7. import java.io.InputStream;  
    8. import java.io.OutputStream;  
    9. import java.net.HttpURLConnection;  
    10. import java.net.URL;  
    11. import java.util.Date;    
    12. import java.util.HashMap;  
    13. import java.util.Map;  
    14.     
    15. import javax.servlet.http.HttpServletRequest;    
    16. import javax.servlet.http.HttpServletResponse;  
    17.     
    18. import org.slf4j.Logger;  
    19. import org.slf4j.LoggerFactory;  
    20. import org.springframework.beans.factory.annotation.Autowired;  
    21. import org.springframework.stereotype.Controller;    
    22. import org.springframework.ui.ModelMap;    
    23. import org.springframework.web.bind.annotation.RequestMapping;    
    24. import org.springframework.web.bind.annotation.RequestMethod;  
    25. import org.springframework.web.bind.annotation.RequestParam;    
    26. import org.springframework.web.bind.annotation.ResponseBody;  
    27. import org.springframework.web.multipart.MultipartFile;    
    28. import org.springframework.web.multipart.MultipartHttpServletRequest;  
    29. import org.springframework.web.multipart.commons.CommonsMultipartFile;  
    30.   
    31. import com.jiafuwei.spring.po.JsonResult;  
    32. import com.jiafuwei.spring.po.QRCode;  
    33. import com.jiafuwei.spring.service.IQRCodeService;  
    34. import com.jiafuwei.spring.util.UuidUtil;  
    35. import com.jiafuwei.spring.util.ZXingCodeUtil;  
    36.     
    37. @Controller    
    38. public class UploadController {    
    39.     final Logger logger = LoggerFactory.getLogger(getClass());  
    40.       
    41.     @Autowired  
    42.     private IQRCodeService qRCodeService;  
    43.    
    44.     /* 
    45.      * 通过流的方式上传文件 
    46.      * @RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象 
    47.      */  
    48.     @RequestMapping("fileUpload")  
    49.     public String  fileUpload(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request,  
    50.             ModelMap model) throws IOException {  
    51.            
    52.            
    53.         //用来检测程序运行时间  
    54.         long  startTime=System.currentTimeMillis();  
    55.         String path = request.getSession().getServletContext().getRealPath("upload");   
    56.         String fileName = file.getOriginalFilename();  
    57.         System.out.println("fileName:"+fileName);  
    58.           
    59.         File targetFile = new File(path, fileName);    
    60.         if(!targetFile.exists()){    
    61.             targetFile.mkdirs();    
    62.         }   
    63.         file.transferTo(targetFile);  
    64.           
    65.         model.addAttribute("fileUrl", request.getContextPath()+"/upload/"+fileName);    
    66.           
    67.          
    68.         long  endTime=System.currentTimeMillis();  
    69.         System.out.println("方法一的运行时间:"+String.valueOf(endTime-startTime)+"ms");  
    70.         return "/result";   
    71.     }  
    72.     
    73.       
    74.     @RequestMapping("fileUploadLogo")  
    75.     @ResponseBody  
    76.     public String  fileUploadLogo(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request,  
    77.             ModelMap model) throws IOException {  
    78.            
    79.            
    80.         //用来检测程序运行时间  
    81.         long  startTime=System.currentTimeMillis();  
    82.         String path = request.getSession().getServletContext().getRealPath("upload");   
    83.         String fileName = file.getOriginalFilename();  
    84.         System.out.println("fileName:"+fileName);  
    85.           
    86.         File targetFile = new File(path, fileName);    
    87.         if(!targetFile.exists()){    
    88.             targetFile.mkdirs();    
    89.         }   
    90.         file.transferTo(targetFile);  
    91.           
    92.         model.addAttribute("fileUrl", request.getContextPath()+"/upload/"+fileName);    
    93.           
    94.          
    95.         long  endTime=System.currentTimeMillis();  
    96.         System.out.println("方法一的运行时间:"+String.valueOf(endTime-startTime)+"ms");  
    97.         return "result";   
    98.     }  
    99.       
    100.     /** 
    101.      * APP 合成二维码生成 
    102.      * @param request 
    103.      * @param model 
    104.      * @return 
    105.      * @throws IOException 
    106.      */  
    107.     @RequestMapping("synthesisQRCode")  
    108.     @ResponseBody  
    109.     public JsonResult  SynthesisQRCode(HttpServletRequest request,  
    110.             @RequestParam(value="file",required=false) CommonsMultipartFile logoFile,  
    111.             @RequestParam(value="ios_url",required=true) String ios_url,  
    112.             @RequestParam(value="android_url",required=true) String android_url,  
    113.             ModelMap model) throws IOException {  
    114.           
    115.          logger.info("SynthesisQRCode - {}""开始了");  
    116.          logger.info("path - {}", request.getSession().getServletContext().getRealPath("/"));  
    117.            
    118.          StringBuffer url = request.getRequestURL();    
    119.          String tempContextUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append(request.getContextPath()).append("/").toString();   
    120.          logger.info("tempContextUrl - {}", tempContextUrl);  
    121.            
    122.          String key_id = UuidUtil.generateShortUuid();  
    123.          QRCode qRCode = new QRCode();  
    124.          qRCode.setAndroid_url(android_url);  
    125.          qRCode.setIos_url(ios_url);  
    126.          qRCode.setKey_id(key_id);  
    127.          int intInsert = qRCodeService.insert(qRCode);  
    128.            
    129.          String path = request.getSession().getServletContext().getRealPath("upload");  
    130.          String fileName = new Date().getTime() + "qrcode.png";  
    131.          File createFile = new File(path+"/"+fileName);  
    132.            
    133.          //访问路径 服务器地址+ findAppUrl + key_id  
    134.          String urltxt = tempContextUrl+"findAppUrl/"+key_id;  
    135.          logger.info("urltxt - {}", urltxt);  
    136.            
    137.          //生成二维码  
    138.          String imageBase64QRCode = "";  
    139.          if(logoFile != null){  
    140.              String logoFileName = logoFile.getOriginalFilename();  
    141.              File targetFile = new File(path, logoFileName);    
    142.              if(!targetFile.exists()){    
    143.                  targetFile.mkdirs();    
    144.              }   
    145.              logoFile.transferTo(targetFile);  
    146.              imageBase64QRCode = ZXingCodeUtil.getLogoQRCode(urltxt, request, "", targetFile, createFile);  
    147.              //删除上传的logo  
    148.              targetFile.delete();  
    149.          }else{  
    150.              imageBase64QRCode = ZXingCodeUtil.getQRCode(urltxt, request, "", createFile);  
    151.          }  
    152.            
    153.          //二维码地址  
    154.          String qrcode_path = tempContextUrl+"upload/"+fileName;  
    155.            
    156.          JsonResult jsonResult = new JsonResult();  
    157.          Map data = new HashMap<String, String>();  
    158.          data.put("recreateFlag""0");  
    159.          data.put("qrcode_path", qrcode_path);  
    160.          data.put("accessKey""13598992");  
    161.          data.put("shortUrl", urltxt);  
    162.          jsonResult.setData(data);  
    163.          jsonResult.setMeg("生成成功");  
    164.          jsonResult.setRes(1);  
    165.        
    166.         return jsonResult;   
    167.     }  
    168.       
    169.     /** 
    170.      * url链接或者文本生成二维码 
    171.      * @param request 
    172.      * @param model 
    173.      * @return 
    174.      * @throws IOException 
    175.      */  
    176.     @RequestMapping("urlQRCode")  
    177.     @ResponseBody  
    178.     public JsonResult  UrlQRCode(HttpServletRequest request,ModelMap model,  
    179.             @RequestParam(value="file",required=false) CommonsMultipartFile logoFile,  
    180.             @RequestParam(value="urltxt",required=false) String urltxt) throws IOException {  
    181.           
    182.          logger.info("UrlQRCode - {}""开始了");  
    183.          logger.info("页面传递的文本内容- {}", urltxt);  
    184.            
    185.          StringBuffer url = request.getRequestURL();    
    186.          String tempContextUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append(request.getContextPath()).append("/").toString();   
    187.            
    188.            
    189.          String path = request.getSession().getServletContext().getRealPath("upload");  
    190.          String fileName = new Date().getTime() + "url.png";  
    191.          File createFile = new File(path+"/"+fileName);  
    192.          //生成二维码  
    193.          String imageBase64QRCode = "";  
    194.          if(logoFile != null){  
    195.              String logoFileName = logoFile.getOriginalFilename();  
    196.              File targetFile = new File(path, logoFileName);    
    197.              if(!targetFile.exists()){    
    198.                  targetFile.mkdirs();    
    199.              }   
    200.              logoFile.transferTo(targetFile);  
    201.              imageBase64QRCode = ZXingCodeUtil.getLogoQRCode(urltxt, request, "", targetFile, createFile);  
    202.              //删除上传的logo  
    203.              targetFile.delete();  
    204.          }else{  
    205.              imageBase64QRCode = ZXingCodeUtil.getQRCode(urltxt, request, "", createFile);  
    206.          }  
    207.            
    208.          //二维码地址  
    209.          String qrcode_path = tempContextUrl+"upload/"+fileName;  
    210.            
    211.          JsonResult jsonResult = new JsonResult();  
    212.          Map data = new HashMap<String, String>();  
    213.          data.put("qrcode_path", qrcode_path);  
    214.          data.put("qrcode", imageBase64QRCode);  
    215.          jsonResult.setData(data);  
    216.          jsonResult.setMeg("生成成功");  
    217.          jsonResult.setRes(1);  
    218.        
    219.         return jsonResult;   
    220.     }  
    221.       
    222.     /** 
    223.      * 二维码下载 
    224.      * @param qrcode_path 
    225.      * @param request 
    226.      * @param response 
    227.      * @throws IOException 
    228.      */  
    229.     @RequestMapping("/download")    
    230.     public void downloadFile(@RequestParam(value="qrcode_path",required=true) String qrcode_path,  
    231.             HttpServletRequest request, HttpServletResponse response) throws IOException {      
    232.           
    233.         String destUrl = qrcode_path;  
    234.         // 建立链接      
    235.         URL url = new URL(destUrl);      
    236.         HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection();      
    237.         // 连接指定的资源      
    238.         httpUrl.connect();      
    239.         // 获取网络输入流      
    240.         BufferedInputStream bis = new BufferedInputStream(httpUrl.getInputStream());      
    241.         
    242.         response.setContentType("application/x-msdownload");      
    243.         response.setHeader("Content-Disposition""attachment; filename="+java.net.URLEncoder.encode(new Date().getTime()+"url.png","UTF-8"));      
    244.         OutputStream out = response.getOutputStream();      
    245.         byte[] buf = new byte[1024];      
    246.         if (destUrl != null) {      
    247.             BufferedInputStream br = bis;      
    248.             int len = 0;      
    249.             while ((len = br.read(buf)) > 0){      
    250.                 out.write(buf, 0, len);      
    251.             }                     
    252.             br.close();      
    253.         }      
    254.         out.flush();      
    255.         out.close();      
    256.     }   
    257.       
    258.       
    259.     /** 
    260.      * 二维码解析 
    261.      * @param request 
    262.      * @param model 
    263.      * @return 
    264.      * @throws IOException 
    265.      */  
    266.     @RequestMapping("parseQRCode")  
    267.     @ResponseBody  
    268.     public JsonResult  ParseQRCode(HttpServletRequest request,ModelMap model,  
    269.             @RequestParam(value="file",required=false) CommonsMultipartFile logoFile) throws IOException {  
    270.           
    271.          logger.info("ParseQRCode - {}""开始了");  
    272.            
    273.          StringBuffer url = request.getRequestURL();    
    274.          String tempContextUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append(request.getContextPath()).append("/").toString();   
    275.           
    276.          String path = request.getSession().getServletContext().getRealPath("upload");  
    277.            
    278.          String logoFileName = logoFile.getOriginalFilename();  
    279.          File targetFile = new File(path, logoFileName);    
    280.          if(!targetFile.exists()){    
    281.              targetFile.mkdirs();    
    282.          }   
    283.          logoFile.transferTo(targetFile);  
    284.            
    285.          String text = ZXingCodeUtil.parseQRCode(targetFile);  
    286.            
    287.          targetFile.delete();  
    288.            
    289.          logger.info("解析结果 - {}", text);  
    290.            
    291.          JsonResult jsonResult = new JsonResult();  
    292.          Map data = new HashMap<String, String>();  
    293.          data.put("text", text);  
    294.          jsonResult.setData(data);  
    295.          jsonResult.setMeg("生成成功");  
    296.          jsonResult.setRes(1);  
    297.        
    298.         return jsonResult;   
    299.     }  
    300. }   
    更多相关内容
  • OpenCV实现二维码发现与定位

    千次阅读 2021-02-03 15:23:47
    关键点在于如何从图像中快速而准确的找到二维码区域,寻找到二维码三个匹配模式点(定位块)。 一:二维码的组成 要关注的是图中三个黑色正方形区域,它们就是用来定位一个二维码的最重要的三个区域,首先要做的...

    关键点在于如何从图像中快速而准确的找到二维码区域,寻找到二维码三个匹配模式点(定位块)。

    一:二维码的组成

    在这里插入图片描述
    要关注的是图中三个黑色正方形区域,它们就是用来定位一个二维码的最重要的三个区域,首先要做的就是要发现这三个区域。如果找到这个三个定位块(区域),就认为成功发现一个二维码,就可以对它定位与识别了。二维码其它各个部分的说明如下:
    在这里插入图片描述
    可以发现一个显著的几何特征:
    三个角上的定位块(正方形区域)从左到右、从上到下黑白比例为1:1:3:1:1。
    在这里插入图片描述
    不管角度如何变化,这个比例是最显著的特征,通过这个特征,

    展开全文
  • OpenCV C++案例实战三《二维码检测》

    千次阅读 多人点赞 2021-11-04 08:50:28
    OpenCV C++案例实战三《二维码检测》前言一、二维码检测二、二维码识别1.通过findContours找到轮廓层级关系三、二维码绘制总结 前言 本文将使用OpenCV C++ 进行二维码检测。 一、二维码检测 首先我们要先将图像...


    前言

    本文将使用OpenCV C++ 进行二维码检测。

    一、二维码检测

    请添加图片描述
    首先我们要先将图像进行预处理,通过灰度、滤波、二值化等操作提取出图像轮廓。在这里我还添加了形态学操作,消除噪点,有效将矩形区域连接起来。

    	Mat gray;
    	cvtColor(src, gray, COLOR_BGR2GRAY);
    
    	Mat blur;
    	GaussianBlur(gray, blur, Size(3, 3), 0);
    
    	Mat bin;
    	threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
    
    	//通过Size(5,1)开运算消除边缘毛刺
    	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));
    	Mat open;
    	morphologyEx(bin, open, MORPH_OPEN, kernel);
    
    	//通过Size(21,1)闭运算能够有效地将矩形区域连接 便于提取矩形区域
    	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1));
    	Mat close;
    	morphologyEx(open, close, MORPH_CLOSE, kernel1);
    

    请添加图片描述
    如图为经过一系列图像处理之后得到的效果。之后我们需要对该图进行轮廓提取,找到二维码所在的矩形区域。

    	//使用RETR_EXTERNAL找到最外轮廓
    	vector<vector<Point>>MaxContours;
    	findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    
    	for (int i = 0; i < MaxContours.size(); i++)
    	{
    		Mat mask = Mat::zeros(src.size(), CV_8UC3);
    		mask = Scalar::all(255);
    
    		double area = contourArea(MaxContours[i]);
    
    		//通过面积阈值找到二维码所在矩形区域
    		if (area > 6000 && area < 100000)
    		{
    			//计算最小外接矩形
    			RotatedRect MaxRect = minAreaRect(MaxContours[i]);
    			//计算最小外接矩形宽高比
    			double ratio = MaxRect.size.width / MaxRect.size.height;
    
    			if (ratio > 0.8 && ratio < 1.2)
    			{
    				Rect MaxBox = MaxRect.boundingRect();
    
    				//rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2);
    				//将矩形区域从原图抠出来
    				Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));
    
    				ROI.copyTo(mask(MaxBox));
    
    				ROI_Rect.push_back(mask);
    
    			}
    
    		}
    
    	}
    

    由以下代码段我们就可以很好的找出二维码所在的矩形区域,并将这些区域抠出来保存以便进行下面的识别工作。

    //找到二维码所在的矩形区域
    void Find_QR_Rect(Mat src, vector<Mat>&ROI_Rect)
    {
    	Mat gray;
    	cvtColor(src, gray, COLOR_BGR2GRAY);
    
    	Mat blur;
    	GaussianBlur(gray, blur, Size(3, 3), 0);
    
    	Mat bin;
    	threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
    
    	//通过Size(5,1)开运算消除边缘毛刺
    	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));
    	Mat open;
    	morphologyEx(bin, open, MORPH_OPEN, kernel);
    	//通过Size(21,1)闭运算能够有效地将矩形区域连接 便于提取矩形区域
    	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1));
    	Mat close;
    	morphologyEx(open, close, MORPH_CLOSE, kernel1);
    
    
    	//使用RETR_EXTERNAL找到最外轮廓
    	vector<vector<Point>>MaxContours;
    	findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    
    	for (int i = 0; i < MaxContours.size(); i++)
    	{
    		Mat mask = Mat::zeros(src.size(), CV_8UC3);
    		mask = Scalar::all(255);
    
    		double area = contourArea(MaxContours[i]);
    
    		//通过面积阈值找到二维码所在矩形区域
    		if (area > 6000 && area < 100000)
    		{
    			//计算最小外接矩形
    			RotatedRect MaxRect = minAreaRect(MaxContours[i]);
    			//计算最小外接矩形宽高比
    			double ratio = MaxRect.size.width / MaxRect.size.height;
    
    			if (ratio > 0.8 && ratio < 1.2)
    			{
    				Rect MaxBox = MaxRect.boundingRect();
    
    				//rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2);
    				//将矩形区域从原图抠出来
    				Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));
    
    				ROI.copyTo(mask(MaxBox));
    
    				ROI_Rect.push_back(mask);
    
    			}
    
    		}
    
    	}
    
    }
    

    请添加图片描述
    如图所示,这是找到的二维码矩形。这里只展示其中之一。

    二、二维码识别

    1.通过findContours找到轮廓层级关系

    	//用于存储检测到的二维码
    	vector<vector<Point>>QR_Rect;
    	
    	//遍历所有找到的矩形区域
    	for (int i = 0; i < ROI_Rect.size(); i++)
    	{
    		Mat gray;
    		cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY);
    
    		Mat bin;
    		threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);
    
    		//通过hierarchy、RETR_TREE找到轮廓之间的层级关系
    		vector<vector<Point>>contours;
    		vector<Vec4i>hierarchy;
    		findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
    
    		//父轮廓索引
    		int ParentIndex = -1;
    		int cn = 0;
    
    		//用于存储二维码矩形的三个“回”
    		vector<Point>rect_points;
    		for (int i = 0; i < contours.size(); i++)
    		{
    			//hierarchy[i][2] != -1 表示该轮廓有子轮廓  cn用于计数“回”中第几个轮廓
    			if (hierarchy[i][2] != -1 && cn == 0)
    			{
    				ParentIndex = i;
    				cn++;
    			}
    			else if (hierarchy[i][2] != -1 && cn == 1)
    			{
    				cn++;
    			}
    			else if (hierarchy[i][2] == -1)
    			{
    				//初始化
    				ParentIndex = -1;
    				cn = 0;
    			}
    
    			//如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’
    			if (hierarchy[i][2] != -1 && cn == 2)
    			{
    				drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1);
    
    				RotatedRect rect;
    
    				rect = minAreaRect(contours[ParentIndex]);
    
    				rect_points.push_back(rect.center);
    
    			}
    
    		}
    	}
    

    以上代码段的整体思路为:首先经过图像预处理进行轮廓检测,
    通过hierarchy、RETR_TREE找到轮廓之间的层级关系。根据hierarchy[i][2]是否为-1判断该轮廓是否有子轮廓。若该轮廓存在子轮廓,则统计有几个子轮廓。如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’ 。关于轮廓的层级关系,大家可以自行百度查找资料,理解一下其中原理。

    //对找到的矩形区域进行识别是否为二维码
    int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect)
    {
    	//用于存储检测到的二维码
    	vector<vector<Point>>QR_Rect;
    	
    	//遍历所有找到的矩形区域
    	for (int i = 0; i < ROI_Rect.size(); i++)
    	{
    		Mat gray;
    		cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY);
    
    		Mat bin;
    		threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);
    
    		//通过hierarchy、RETR_TREE找到轮廓之间的层级关系
    		vector<vector<Point>>contours;
    		vector<Vec4i>hierarchy;
    		findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
    
    		//父轮廓索引
    		int ParentIndex = -1;
    		int cn = 0;
    
    		//用于存储二维码矩形的三个“回”
    		vector<Point>rect_points;
    		for (int i = 0; i < contours.size(); i++)
    		{
    			//hierarchy[i][2] != -1 表示该轮廓有子轮廓  cn用于计数“回”中第几个轮廓
    			if (hierarchy[i][2] != -1 && cn == 0)
    			{
    				ParentIndex = i;
    				cn++;
    			}
    			else if (hierarchy[i][2] != -1 && cn == 1)
    			{
    				cn++;
    			}
    			else if (hierarchy[i][2] == -1)
    			{
    				//初始化
    				ParentIndex = -1;
    				cn = 0;
    			}
    
    			//如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’
    			if (hierarchy[i][2] != -1 && cn == 2)
    			{
    				drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1);
    
    				RotatedRect rect;
    
    				rect = minAreaRect(contours[ParentIndex]);
    
    				rect_points.push_back(rect.center);
    
    			}
    
    		}
    
    		//将找到地‘回’连接起来
    		for (int i = 0; i < rect_points.size(); i++)
    		{
    			line(canvas, rect_points[i], rect_points[(i + 1) % rect_points.size()], Scalar::all(255), 5);
    		}
    
    		QR_Rect.push_back(rect_points);
    
    	}
    
    	
    	return QR_Rect.size();
    
    }
    

    由以上代码段,我们就可以识别出二维码。效果如图所示。

    请添加图片描述

    三、二维码绘制

    	//框出二维码所在位置
    	Mat gray;
    	cvtColor(canvas, gray, COLOR_BGR2GRAY);
    
    	vector<vector<Point>>contours;
    	findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    
    	Point2f points[4];
    
    	for (int i = 0; i < contours.size(); i++)
    	{
    		RotatedRect rect = minAreaRect(contours[i]);
    		
    		rect.points(points);
    
    		for (int j = 0; j < 4; j++)
    		{
    			line(src, points[j], points[(j + 1) % 4], Scalar(0, 255, 0), 2);
    		}
    
    	}
    

    请添加图片描述
    最终效果如图所示。

    请添加图片描述
    请添加图片描述

    四、源码

    #include<iostream>
    #include<opencv2/core.hpp>
    #include<opencv2/imgproc.hpp>
    #include<opencv2/highgui.hpp>
    using namespace std;
    using namespace cv;
    
    
    //找到二维码所在的矩形区域
    void Find_QR_Rect(Mat src, vector<Mat>&ROI_Rect)
    {
    	Mat gray;
    	cvtColor(src, gray, COLOR_BGR2GRAY);
    
    	Mat blur;
    	GaussianBlur(gray, blur, Size(3, 3), 0);
    
    	Mat bin;
    	threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
    
    	//通过Size(5,1)开运算消除边缘毛刺
    	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));
    	Mat open;
    	morphologyEx(bin, open, MORPH_OPEN, kernel);
    	//通过Size(21,1)闭运算能够有效地将矩形区域连接 便于提取矩形区域
    	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1));
    	Mat close;
    	morphologyEx(open, close, MORPH_CLOSE, kernel1);
    
    
    	//使用RETR_EXTERNAL找到最外轮廓
    	vector<vector<Point>>MaxContours;
    	findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    
    	for (int i = 0; i < MaxContours.size(); i++)
    	{
    		Mat mask = Mat::zeros(src.size(), CV_8UC3);
    		mask = Scalar::all(255);
    
    		double area = contourArea(MaxContours[i]);
    
    		//通过面积阈值找到二维码所在矩形区域
    		if (area > 6000 && area < 100000)
    		{
    			//计算最小外接矩形
    			RotatedRect MaxRect = minAreaRect(MaxContours[i]);
    			//计算最小外接矩形宽高比
    			double ratio = MaxRect.size.width / MaxRect.size.height;
    
    			if (ratio > 0.8 && ratio < 1.2)
    			{
    				Rect MaxBox = MaxRect.boundingRect();
    
    				//rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2);
    				//将矩形区域从原图抠出来
    				Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));
    
    				ROI.copyTo(mask(MaxBox));
    
    				ROI_Rect.push_back(mask);
    
    			}
    
    		}
    
    	}
    
    }
    
    
    //对找到的矩形区域进行识别是否为二维码
    int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect)
    {
    	//用于存储检测到的二维码
    	vector<vector<Point>>QR_Rect;
    	
    	//遍历所有找到的矩形区域
    	for (int i = 0; i < ROI_Rect.size(); i++)
    	{
    		Mat gray;
    		cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY);
    
    		Mat bin;
    		threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);
    
    		//通过hierarchy、RETR_TREE找到轮廓之间的层级关系
    		vector<vector<Point>>contours;
    		vector<Vec4i>hierarchy;
    		findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
    
    		//父轮廓索引
    		int ParentIndex = -1;
    		int cn = 0;
    
    		//用于存储二维码矩形的三个“回”
    		vector<Point>rect_points;
    		for (int i = 0; i < contours.size(); i++)
    		{
    			//hierarchy[i][2] != -1 表示该轮廓有子轮廓  cn用于计数“回”中第几个轮廓
    			if (hierarchy[i][2] != -1 && cn == 0)
    			{
    				ParentIndex = i;
    				cn++;
    			}
    			else if (hierarchy[i][2] != -1 && cn == 1)
    			{
    				cn++;
    			}
    			else if (hierarchy[i][2] == -1)
    			{
    				//初始化
    				ParentIndex = -1;
    				cn = 0;
    			}
    
    			//如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’
    			if (hierarchy[i][2] != -1 && cn == 2)
    			{
    				drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1);
    
    				RotatedRect rect;
    
    				rect = minAreaRect(contours[ParentIndex]);
    
    				rect_points.push_back(rect.center);
    
    			}
    
    		}
    
    		//将找到地‘回’连接起来
    		for (int i = 0; i < rect_points.size(); i++)
    		{
    			line(canvas, rect_points[i], rect_points[(i + 1) % rect_points.size()], Scalar::all(255), 5);
    		}
    
    		QR_Rect.push_back(rect_points);
    
    	}
    
    	
    	return QR_Rect.size();
    
    }
    
    int main()
    {
    
    	Mat src = imread("6.png");
    
    	if (src.empty())
    	{
    		cout << "No image data!" << endl;
    		system("pause");
    		return 0;
    	}
    
    	vector<Mat>ROI_Rect;
    	Find_QR_Rect(src, ROI_Rect);
    
    	Mat canvas = Mat::zeros(src.size(), src.type());
    	int flag = Dectect_QR_Rect(src, canvas, ROI_Rect);
    	//imshow("canvas", canvas);
    
    	if (flag <= 0)
    	{
    		cout << "Can not detect QR code!" << endl;	
    		system("pause");
    		return 0;
    	}
    
    	cout << "检测到" << flag << "个二维码。" << endl;
    
    
    	//框出二维码所在位置
    	Mat gray;
    	cvtColor(canvas, gray, COLOR_BGR2GRAY);
    
    	vector<vector<Point>>contours;
    	findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    
    	Point2f points[4];
    
    	for (int i = 0; i < contours.size(); i++)
    	{
    		RotatedRect rect = minAreaRect(contours[i]);
    		
    		rect.points(points);
    
    		for (int j = 0; j < 4; j++)
    		{
    			line(src, points[j], points[(j + 1) % 4], Scalar(0, 255, 0), 2);
    		}
    
    	}
    
    
    	imshow("source", src);
    	waitKey(0);
    	destroyAllWindows();
    
    	system("pause");
    	return 0;
    }
    
    

    总结

    本文使用OpenCV C++进行二维码检测,关键步骤有以下几点。
    1、图像预处理,筛选出二维码所在的矩形区域,并将该区域抠出来进行后续的识别工作。
    2、对筛选出的矩形区域进行轮廓检测,判断它们之前的层级关系,以此来识别二维码。
    3、最后根据检测到的二维码“回”字,将其绘制出来就可以了。

    以上就是我对二维码检测识别的整体思路,欢迎大家一起交流学习。若存在不足的地方欢迎大家指正。

    展开全文
  • python之父实名反对996

    2019-04-26 08:07:58
    python之父实名反对996 在互联网公司之中,实行“996 工作制”几乎成为默认标配,在去年的年会中,有赞 CEO 白鸦将这种企业文化公开的在公司年会提出、并被广泛地传播出来,撕掉了互联网企业因焦虑而追赶的遮羞布...

    这两天关于996的讨论热度直线上升:

    • 996.ICU域名炮轰“996”工作制

    • GitHub 996ICU项目不到一天以突破6W star

    • python之父实名反对996

    在互联网公司之中,实行“996 工作制”几乎成为默认标配,在去年的年会中,有赞 CEO 白鸦将这种企业文化公开的在公司年会提出、并被广泛地传播出来,撕掉了互联网企业因焦虑而追赶的遮羞布。

    因不满最近各个 IT 公司的工作 996 制度,这次程序员终于忍不了啦。

    昨天,一个域名为 996.ICU 的网站突然出现,控诉“工作 996,生病 ICU”,并且还发在了 GitHub 上,GitHub项目,不到一天的时间,就获得了6W+标星。

    GitHub 地址:

    https://github.com/996icu/996.ICU

    当打开 996.ICU 这个域名页面,标注着大大的“996.ICU”字样粉色背景的单页面映入眼帘,下面的一段文字说明,充满了讽刺。

    何谓 996 工作制?“996”工作制,即每天早 9 点到岗,一直工作到晚上 9 点。每周工作 6 天。“996”工作制的周工作时间为最低 60 小时。

    在日本有一个词叫做【过劳死线】,指的就是一个月加班 80 小时以上的话,发生过劳死的可能性会升高。按照国内的这个 996 工作制计算,相当接近这条线。

    此外,这名程序员还列举了两条突然实施 996 工作制的公司案例,分别是有赞和京东。按照劳动法规定,996 工作制下只有拿到当前工资的 2.275 倍,才在经济账上不吃亏

    image

    《中华人民共和国劳动法》第四十一条和四十四条分别对加班时间以及加班费问题做了明确规定,用人单位由于生产经营需要,经与工会和劳动者协商后可以延长工作时间,一般每日不得超过一小时;因特殊原因需要延长工作时间的,在保障劳动者身体健康的条件下延长工作时间每日不得超过三小时,但是每月不得超过三十六小时。有下列情形之一的,用人单位应当按照下列标准支付高于劳动者正常工作时间工资的工资报酬:

    **(**一)安排劳动者延长工作时间的,支付不低于工资的百分之一百五十的工资报酬;

    (二)休息日安排劳动者工作又不能安排补休的,支付不低于工资的百分之二百的工资报酬;

    (三)法定休假日安排劳动者工作的,支付不低于工资的百分之三百的工资报酬。

    这戳中了程序员们对 996 工作制的发泄和不满:

    实际上,996 工作制在互联网企业中几乎成为默认机制,随着互联网企业的高速发展与激烈竞争,越来越多互联网企业纷纷开始铁腕治理,其中之一便是 996 工作制。

    最近脉脉上爆出的京东强行实施 995 工作制,如下图:

    在事情发酵一天后、官方对此事进行了回应、总结下来就是:不强制、但要全情投入。

    996 机制虽然在互联网企业中较为普遍,但多数公司并不会如此直接地作为硬性要求提出来,而是采取其他变相方式“鼓励”加班。

    如根据加班情况评先进个人等奖项、加班有加班费、加班打车报销、加班至夜间 10 点提供免费夜宵等。

    面对 996 工作制,当然不乏吐槽称“辣鸡工作制度”,“钱到位 007 都没问题的”。不过,996 工作制表面上看出于工作时间的安排,而不满的背后实际上也无外乎出于加班薪酬福利之间的衡量。

    想得到更多,就必须付出更多,这是无可逃避的社会规律。每个人的时间和精力都是有限的,你既要专注事业,又要照顾家人,995 甚至 996 这样的工作,占据了你太多的个人时间,很难在这种强度的工作下获得幸福感。

    我们工作生活的目的,本质上是为了维护并提升自己和家人的幸福感,而非长时间高强度地一路高歌猛进。

    当你在某个方面投入过多的时候,势必会忽略掉其他一些事情,比如情感、健康。长远来看,这并不利于你的人生发展。

    工作和生活的意义,更多的在于求取一种平衡。对于主张 996、995 等工作制度的企业,我们可以选择拒绝。

    8小时工作制这样的规则需要每一个人去守护,这样才能让大家都获得愉快舒适的工作体验,能够更好地兼顾事业、自我和家庭。

    不过有意思的是!

    996.ICU项目Issues马上已经变成了一个论坛了,各种ICU交流群一大堆!

    有兴趣的小伙伴可以去瞅一下,也可以发出自己的声音,把一些996公司的名单都提上去,每一个程序员的身心健康需要你我的一份努力。


    扫描下方二维码,及时获取更多互联网求职面经javapython爬虫大数据等技术,和海量资料分享
    公众号**菜鸟名企梦后台发送“csdn”即可免费领取【csdn】和【百度文库】下载服务;
    公众号
    菜鸟名企梦后台发送“资料”:即可领取5T精品学习资料**、java面试考点java面经总结,以及几十个java、大数据项目资料很全,你想找的几乎都有
    扫码关注,及时获取更多精彩内容。(博主今日头条大数据工程师)

    展开全文
  • 》策划了《开源世界的深度指南》专题,并有幸邀请到Python之父GuidovanRossum展开技术专访,与大家分享他眼中的开源世界与新动态,畅谈开源人生。》面向所有开发者征集“你最想问Python之父的1个问题”,欢迎大家在...
  • Python之父Guido Rossum:打造Google第三大開發語言酷勤網 23-Jan-10 IT人物2009年4月1日凌晨,Guido van Rossum(吉多•範羅蘇姆)在Python社區發 表聲明:在領導Python開發20年之後,正式宣布退休,即時生效。...
  • 微信小程序 带参二维码 纯Java实现

    千次阅读 多人点赞 2020-05-25 17:25:27
    微信小程序中一物一码的实现,当用户使用微信扫描该二维码,会自动跳转到指定的小程序,并传入指定的参数......
  • Opencv的使用小教程3——利用轮廓检测实现二维码定位二维码具有什么特征识别二维码的流程1、预处理图像2、寻找轮廓 好好学习噢! 二维码具有什么特征 二维码就是两个维度的条形码,平常我们在生活中随处可见,“QR...
  • 移动开发现在是一个非常火爆的行业,那么作为扫一扫就可以添加神器——微信的二维码来说,那自然也是相当的牛逼的了。  不多说了,先看看要生成一个二维码,需要几步?  1.准备工作,一个生成二维码中间的...
  • iOS开发扫描二维码

    2017-12-13 11:48:42
    自iOS7以后,iOS扫描二维码不需要借助于第三方框架了,苹果在AVFoundation中原生支持了扫描二维码的API,主要涉及到5个类,这5个类在自定义相机或者视频时也用得上,网上有很多介绍,这5个类分别为: ...
  • 用Python绘制杨紫作品集动态二维码

    千次阅读 2020-03-07 16:33:09
    用杨紫作品集中的动图制作动态二维码
  • 2、使用merge来加载一个布局时,必须指定一个ViewGroup作为其元素,并且要设置加载的attachToRoot参数为true。 3、不能在ViewStub中使用Merge标签。原因就是ViewStub的inflate方法中根本没有attachToRoot的设置。...
  • Java 17 目前已经进入Rampdown Phase One阶段,所有的功能特性都已经被冻结。这说明Java 17的新特性已经定了,不会再增加新的JEP(JDK增强建议)。所以关心J...
  • 神奇的二维码

    千次阅读 2018-10-26 14:23:58
    以前看到二维码,总觉得这是一个神奇的东西,也没有去深入的了解过,最近一个偶然的机会接触到了二维码,真实的体会了它的奥妙处,然后就从中汲取了一些养分与大家分享,希望对大家有用,代码如下: 工具类(Util...
  • 在网站开发中,经常会遇到要生成二维码的情况,比如要使用微信支付、网页登录等,本文分享一个Spring Boot生成二维码的例子,这里用到了google的zxing工具类。...
  • 仿写网易云-项目初始化-扫描二维码登录1. 项目初始化2. 安装使用 Element UI3. 设置一下页面的布局4. 开始写 Header5. 点击未登录弹出登录窗口5.1 原理分析5.2 扫码登录 LoginByScanCode 组件5.2.1 组件UI 样式5.2.2...
  • 让一部分开发者看到未来文 | 局长出品 | OSC开源社区(ID:oschina2013)Java 之父 James Gosling 近日发表推特称,开发者应尽快弃用 JDK 8,可以选择 JDK 17 LTS,因为后者在各个方面都带来了巨大的改进。...
  • 前言 因工作需要,需要定位图片中的二维码;我遂查阅了相关资料,也学习了opencv开源库。通过一番努力,终于很好的实现了二维码定位。本文将讲解如何使用opencv定位二维码。...二维码在设计初就考虑到了识...
  • Java实现二维码

    2017-02-08 16:35:36
    今天这篇博客,主要是利用Java实现二维码: 在写代码之前先讲一下整体思路,以方便更好更快捷的实现功能。 (1).首先要想实现二维码功能需要导入com.google.zxing的核心jar包,我这里导入的是core-3.2.1.jar; (2)....
  • Guido van Rossum,Python之父,就是他用一部英国喜剧《蒙提·派森的飞行马戏团》(Monty Python and the Flying Circus)命名了这门语言。1989年,他在荷兰创造了Python(果然名字里有Van的都是荷兰人吗); 1991年...
  • 能让包含iframe的页面导航到其他地方,所有的插件,如flash等也全部不能起作用 简单来说iframe就只剩下一个展示数据的功能,正如他的名字一样,所有的内容都被放进了一个 “单独的沙盒” sandbox包含的属性及作
  •   我们在游戏开发过程中,经常要配合运营做一些推广等相关功能,其中二维码推广就是一种手段,今天我们一起来探讨一下! 2、场景搭建 场景很简单,我们创建空节点,在创建一个Test.Ts,并挂在改空节点上。如图所示...
  • C++之父的两个小故事

    2021-03-08 13:36:25
    这个年轻人便是今天被尊称为C++之父的Bjarne Stroustrup。Bjarne出生于1950年,当时25岁,已经在丹麦的奥尔胡斯大学获得了硕士学位。这次面试让他得到了到现代计算机的摇篮之一继续学习的机会。也让他满足了女朋友的...
  • 2.二维码生成及结合FastDFS文件服务器储存Utils package com.ytkj.teammanager.common.utils; import java.awt.*; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.*...
  • 好书推荐计划:Keras之父作品《Python 深度学习》

    万次阅读 多人点赞 2018-09-28 08:20:00
    第一本,是由 Keras 之父、现任 Google 人工智能研究员 Francois Chollet 大作: 《Python 深度学习》。 本书详尽介绍了用 Python 和 Keras 进行深度学习的探索实践,包括计算机视觉、自然语言处理、产生式模型等...
  • Redis之父实名认证“Redis实战文档”,你拥有了嘛?

    千次阅读 热门讨论 2021-07-23 13:49:04
    《Redis实战文档》将是一份把读者带入Redis世界、向读者指明正确方向从而避免常见陷阱的文档。我认为《Redis实战文档》对于Redis的生态系统非常有帮助,Redis 的用户应该...——Salvatore Sanfilippo,“Redis之父
  • Python实战社群Java实战社群长按识别下方二维码,按需求添加扫码关注添加客服进Python社群▲扫码关注添加客服进Java社群▲本文转载自:机器心 | 作者:魔王、杜伟不工作...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,620
精华内容 3,048
热门标签
关键字:

二维码之父