精华内容
下载资源
问答
  • 图像中,已知了某连通域的一个像素点,如何根据该像素点确定像素点所在的连通域(比如图像中有多个连通域,而现在只知一个连通域的像素点,如何根据该点反推像素点所在的连通域,并标记出来)
  • Continue 玩转像素点,Python 图像处理学习的第 3 天

    万次阅读 多人点赞 2020-12-26 21:14:07
    还是要解决图像像素点问题。

    今天是持续写作的第 36 / 100 天。
    如果你有想要交流的想法、技术,欢迎在评论区留言。

    今天要学习的内容还是围绕图像、像素展开,直觉这部分对后面的学习会有帮助,来自老程序员的经验。
    Continue 玩转像素点,Python 图像处理学习的第 3 天

    从数组对比彩色图像与灰度图像

    通过 cv2 库读取一个图片之后,可以展示图像的 shape 属性,该属性与 numpy shape,它表示的数组的维度。这是一个整数的元组,元组中的每一个元素对应着每一维度的大小(size)。
    若元组只有一个元素,则说明这个数组是一维数组:如元组(2,) 表示一维数组,只含有 2 个元素;同理,可知(1,3)表示的是一个 2 维数组,因为含有 2 个元素 :1,3。

    import numpy as np
    import cv2
    # 读取图片
    color_img = cv2.imread("test.jpg")
    print(color_img.shape)
    

    输出的内容是 (516, 787, 3),核对一下图片的宽度和高度,恰好与刚才的数值对应。
    Continue 玩转像素点,Python 图像处理学习的第 3 天
    上述输出的元组最后的 3 表示这张图片是一个 RGB 图像。

    相应的修改一下读取图片的参数,读取进来一个灰度图,再输出 shape 属性,得到的就没有最后的 3 了。

    import numpy as np
    import cv2
    
    # 读取灰度图片
    gray_img = cv2.imread("test.jpg", cv2.IMREAD_GRAYSCALE)
    # 输出的结果中并没有 3
    print(gray_img.shape)
    

    输出的结果如下。

    (516, 787)
    

    如果直接输出元组里面的内容,得到的数据如下:

    [[70 70 70 ... 70 70 70]
     [69 70 69 ... 70 70 70]
     [70 70 70 ... 70 70 70]
     ...
     [26 26 25 ... 27 27 27]
     [26 26 25 ... 27 27 27]
     [27 26 26 ... 26 26 26]]
    

    如果读取正常的彩色图片,得到的数组为:

    # 正常读取图片
    img = cv2.imread("test.jpg")
    print(img.shape)
    print(img)
    
    [[[43 67 87]
      [43 67 87]
      [43 67 87]
      ...
      [43 67 87]
      [43 67 87]
      [43 67 87]]
    
     [[42 66 86]
      [43 67 87]
      [42 66 86]
      ...
    

    可以看到上述两个内容最大的区别是,彩色图片输出的是三维数组,灰度图片输出的是二维数组,如果不太清楚,可以反复多尝试几次,如果橡皮擦把概念说错了,咱就先按能理解的理解,后面慢慢进行更正,非专业人士,请谅解。

    操作图片的像素点

    既然图片都能当成一个矩阵来进行操作了,那对于每个位置的元素都可以进行获取与修改,例如获取 100,100 位置的像素颜色。

    import numpy as np
    import cv2
    
    img = cv2.imread("test.jpg")
    pixel = img[100, 100]
    print(pixel)
    

    输出结果是 [44 69 89] ,这个知识点经过检索发现说法一般叫做使用 OpenCV 获取每个像素点的 RGB 颜色分量,还有一个需要注意,得到的数组是按照 BGR 的顺序返回,这个要记忆一下。

    设置像素点的 RGB 颜色分量。

    import numpy as np
    import cv2
    
    img = cv2.imread("test.jpg")
    pixel = img[100, 100]
    print(pixel)
    img[100, 100] = [200, 220, 1]  # 设置像素值
    
    cv2.imshow("img show", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    打开图片,在 100,100 位置寻找设置的点,可以预先猜一下这个点的颜色是什么颜色。
    [200,220,1] 对应的 BGR,200 的蓝色,220 的绿色,1 的红色,实际用屏幕取色工具找到那个点,发现颜色为 1, 220, 200,注意下图的小点。

    Continue 玩转像素点,Python 图像处理学习的第 3 天
    通过取色器对应了一下,发现颜色对应的没有问题。
    Continue 玩转像素点,Python 图像处理学习的第 3 天
    其实看到这里,我就在想如果在图片上随机画白点,岂不是能得到好看的图片?

    椒盐现象图片

    搜了一下,还真找到了,刚才提及的叫做椒盐图片,还挺有趣的,说干就干,看代码。
    不过在实现之前,还要

    import numpy as np
    import cv2
    
    img = cv2.imread("test.jpg")
    # pixel = img[100, 100]
    # print(pixel)
    # img[100, 100] = [255, 255, 255]  # 设置像素值
    # 输出的是(高度像素,宽度像素)
    print(img.shape)
    
    for k in range(1000):
        i = int(np.random.random() * img.shape[1])
        j = int(np.random.random() * img.shape[0])
    
        img[j, i] = [255, 255, 255]
        img[j, i] = [255, 255, 255]
        img[j, i] = [255, 255, 255]
    
    cv2.imshow("img show", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    运行之后的效果还可以,有那么点意思了。
    Continue 玩转像素点,Python 图像处理学习的第 3 天
    上述代码,在实验的时候,可以按照下面的代码设置内容。

    img[j, i] = 255
    img[j, i] = 255
    img[j, i] = 255
    

    通过该方法修改之后,得到的结果和直接设置三个值是一样的。
    Continue 玩转像素点,Python 图像处理学习的第 3 天

    今天的 OpenCV 尾声

    1 个小时好快,尤其是在学习新的东西的时候,今天的日程到这里啦,一起加油吧。本阶段可以执着于像素点上的操作,这个应该是未来解决图片问题的最佳突破点。


    如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
    博主 ID:梦想橡皮擦,希望大家点赞评论收藏

    展开全文
  • OpenCV像素点邻域遍历效率比较,以及访问像素点的几种方法

    OpenCV像素点邻域遍历效率比较,以及访问像素点的几种方法

    前言:

    以前笔者在项目中经常使用到OpenCV的算法,而大部分OpenCV的算法都需要进行遍历操作,而且很多遍历操作都是需要对目标像素点的邻域进行二次遍历操作。笔者参考了很多博文,经过了实验,在这篇博文中总结了OpenCV的遍历操作的效率。

    参考博文:
    《OpenCV获取与设置像素点的值的几个方法 》
    《【OpenCV】访问Mat中每个像素的值(新)》
    《[OpenCV] 访问Mat矩阵中的元素》
    《OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年》
    《OpenCV学习笔记(四十三)——存取像素值操作汇总core》
    《图像噪声的抑制——均值滤波、中值滤波、对称均值滤波》

    一. 均值滤波

    由于笔者想要了解像素点及其邻域的遍历,所以本文用于测试的算法是均值滤波。
    均值滤波的方法比较简单。对待需要处理的当前像素,选择一个模板,该模板为其邻域内若干像素,然后用模板内所有像素的均值来替代原像素值。公式如下:

    均值滤波模板如下图所示:

    图中的模板大小选择了3 × 3矩阵,图中的1–8绿色部分的像素都是邻域内像素,黄色像素是在图像(x, y)像素处的均值滤波结果。

    该方法十分简单,包括了简单的邻域像素点的操作,优缺点也十分明显:

    • 优点:算法简单,计算速度快;
    • 缺点:降低噪声的同时使图像产生模糊,特别是景物的边缘和细节部分。

    二. 邻域遍历方法

    笔者对不同邻域遍历方法使用的算法与图片如下:

    • 测试算法:均值滤波
    • 滤波内核尺寸:3 × 3
    • 图片尺寸:580 × 410

    原图如下:

    笔者参考了博文《【OpenCV】访问Mat中每个像素的值(新)》,从其中学习了几种遍历像素点的方法。针对邻域遍历,笔者最后总结了三种方法如下:

    1. ptr与[]

    Mat最直接的访问方法,是通过.ptr<>函数得到一行的指针,并用[]操作符访问某一列的像素值。
    源码如下:

    // using .ptr and []
    void MyBlur_1(Mat src, Mat& dst)
    {
        dst.create(src.size(), src.type());
        // 强制将ksize变为奇数
    //   int ksize = ksize / 2 * 2 + 1;
        int ksize = 3;
        // kernel的半径
    //    kr = ksize / 2;
        int kr = 1;
    
        int nr = src.rows - kr;
        int nc = (src.cols - kr) * src.channels();
    
        for(int j = kr; j < nr; j++)
        {
            // 获取图像三行数据的地址
            const uchar* previous = src.ptr<uchar>(j - 1);
            const uchar* current = src.ptr<uchar>(j);
            const uchar* next = src.ptr<uchar>(j + 1);
    
            uchar* output = dst.ptr<uchar>(j);
    
            for(int i = 1 * src.channels(); i < nc; i++)
            {
                output[i] = cv::saturate_cast<uchar>(
                            (previous[i - src.channels()] + previous[i] + previous[i + src.channels()]
                        + current[i - src.channels()] + current[i] + current[i + src.channels()]
                        + next[i - src.channels()] + next[i] + next[i + src.channels()]) / 9);
            }
        }
    }

    2. at获取图像坐标

    .at操作可以用于操作单个像素点。通常的操作如下:

    // 对于单通道图像
    img.at<uchar>(i, j) = 255;
    // 对于多通道图像
    img.at<cv::Vec3b>(i, j)[0] = 255;

    用at实现均值滤波的代码如下:

    // using at
    void MyBlur_2(Mat src, Mat& dst)
    {
        dst.create(src.size(), src.type());
        // 强制将ksize变为奇数
    //   int ksize = ksize / 2 * 2 + 1;
        int ksize = 3;
        // kernel的半径
    //    kr = ksize / 2;
        int kr = 1;
    
        int nr = src.rows - kr;
        int nc = src.cols - kr;
    
        for(int j = kr; j < nr; j++)
        {
            for(int i = src.channels(); i < nc; i++)
            {
                dst.at<Vec3b>(j, i)[0] = cv::saturate_cast<uchar>(
                            (src.at<Vec3b>(j-1, i-1)[0] + src.at<Vec3b>(j, i-1)[0] + src.at<Vec3b>(j+1, i-1)[0]
                        + src.at<Vec3b>(j-1, i)[0] + src.at<Vec3b>(j, i)[0] + src.at<Vec3b>(j+1, i)[0]
                        + src.at<Vec3b>(j-1, i+1)[0] + src.at<Vec3b>(j, i+1)[0] + src.at<Vec3b>(j+1, i+1)[0]) / 9 );
                dst.at<Vec3b>(j, i)[1] = cv::saturate_cast<uchar>(
                            (src.at<Vec3b>(j-1, i-1)[1] + src.at<Vec3b>(j, i-1)[1] + src.at<Vec3b>(j+1, i-1)[1]
                        + src.at<Vec3b>(j-1, i)[1] + src.at<Vec3b>(j, i)[1] + src.at<Vec3b>(j+1, i)[1]
                        + src.at<Vec3b>(j-1, i+1)[1] + src.at<Vec3b>(j, i+1)[1] + src.at<Vec3b>(j+1, i+1)[1]) / 9 );
                dst.at<Vec3b>(j, i)[2] = cv::saturate_cast<uchar>(
                            (src.at<Vec3b>(j-1, i-1)[2] + src.at<Vec3b>(j, i-1)[2] + src.at<Vec3b>(j+1, i-1)[2]
                        + src.at<Vec3b>(j-1, i)[2] + src.at<Vec3b>(j, i)[2] + src.at<Vec3b>(j+1, i)[2]
                        + src.at<Vec3b>(j-1, i+1)[2] + src.at<Vec3b>(j, i+1)[2] + src.at<Vec3b>(j+1, i+1)[2]) / 9 );
            }
        }
    }

    然而,Debug版本下,at操作要比指针的操作慢很多,所以对于不连续数据或者单个点处理,可以考虑at操作,对于连续的大量数据,尽量不要使用它。

    Release版本与Debug版本的对比,以及at操作的效率,在后面会有比较。

    3. data

    Mat类中,对data的定义如下:

    //! pointer to the data
    uchar* data;

    data是Mat对象中的指针,指向存放内存中存放矩阵数据的一块内存,类型为uchar*。
    又由于二维矩阵Mat中任一像素的地址为:

    addr(Mi,j)=M.data+M.step[0]×i+M.step[1]×j

    计算得到的便是M矩阵中像素(i, j)的地址。而又有:
    M.step[0]=M.cols×M.channels
    M.step[1]=M.channels
    所以上式可以改写为:
    addr(Mi,j)=M.data+M.cols×M.channels×i+M.channels×j

    综上,用.data实现均值滤波的代码如下:

    // using .data
    void MyBlur_3(Mat src, Mat& dst)
    {
        dst.create(src.size(), src.type());
        // 强制将ksize变为奇数
    //   int ksize = ksize / 2 * 2 + 1;
        int ksize = 3;
        // kernel的半径
    //    kr = ksize / 2;
        int kr = 1;
    
        int nr = src.rows - kr;
        int nc = (src.cols - kr) * src.channels();
        uchar* srcdata = (uchar*)src.data;
        uchar* dstdata = (uchar*)dst.data;
    
        for(int j = kr; j < nr; j++)
        {
            uchar* psrc_0 = srcdata + (j-1) * src.cols * src.channels();
            uchar* psrc_1 = srcdata + j * src.cols * src.channels();
            uchar* psrc_2 = srcdata + (j+1) * src.cols * src.channels();
            uchar* pdst = dstdata + j * dst.cols * dst.channels();
            for(int i = src.channels(); i < nc; i++)
            {
                *pdst++ = (psrc_0[i - src.channels()] + psrc_0[i] + psrc_0[i + src.channels()]
                        + psrc_1[i - src.channels()] + psrc_1[i] + psrc_1[i + src.channels()]
                        + psrc_2[i - src.channels()] + psrc_2[i] + psrc_2[i + src.channels()]) / 9;
            }
        }
    }

    需要注意的是,前面说的at操作与ptr操作,都是带有内存检查,防止操作越界的,然而使用data指针比较危险,虽然在Debug版本下的速度确实让人眼前一亮。所以在《Mat数据操作之普通青年、文艺青年、暴力青年》中,博主将.data操作称之为暴力青年。

    三. 不同方法效率比较

    写这篇博文之前,我还以为不同的方法对效率的影响十分巨大。当然这种想法是没有错的,因为在Debug版本中确实不同的遍历方法有着很大的效率区别。但是看了博客《Mat数据操作之普通青年、文艺青年、暴力青年》中的总结后,笔者才意识到原来在Release版本下,上述方法的效率其实差别不大。

    1. 测试源码:

    #include "cv.h"
    #include "highgui.h"
    #include <vector>
    #include <cmath>
    #include <math.h>
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    void MyBlur_1(Mat src, Mat& dst);
    void MyBlur_2(Mat src, Mat& dst);
    void MyBlur_3(Mat src, Mat& dst);
    
    // 测试遍历像素点对均值滤波的效率
    int main()
    {
        double time;
        double start;
    
        Mat img;
        img = imread("/home/grq/miska.jpg");
    
        Mat dst1;
        start = static_cast<double>(getTickCount());
        MyBlur_1(img, dst1);
        time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
        cout << "using .ptr and []"<<endl;
        cout << "Time: " << time << "ms" << endl<<endl;
    
        Mat dst2;
        start = static_cast<double>(getTickCount());
        MyBlur_2(img, dst2);
        time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
        cout << "using at "<<endl;
        cout<< "Time: " << time << "ms" << endl <<endl;
    
        Mat dst4;
        start = static_cast<double>(getTickCount());
        MyBlur_3(img, dst4);
        time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
        cout <<"using .data"<<endl;
        cout << "Time: " << time << "ms" << endl<<endl;
    
        Mat dst3;
        start = static_cast<double>(getTickCount());
        blur(img, dst3, Size(3, 3));
        time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
        cout<<"using OpenCV's blur"<<endl;
        cout << "Time: " << time << "ms" <<endl << endl;
    
        imshow("src", img);
        imshow("using .ptr and []", dst1);
        imshow("using at ", dst2);
        imshow("using .data", dst4);
        waitKey(0);
    
        return 0;
    }

    2. Debug版本

    运行结束后,结果输出如下:
    这里写图片描述

    如图所示,在Debug版本下,不同的遍历方法对遍历的效率影响还是很大的。值得注意的有下面几点:

    (1) 检查操作对效率的影响

    实际上,at操作符与ptr操作符在Debug版本下都是有内存检查、防止操作越界的操作,而data十分简单粗暴,没有任何检查,由于它的简单粗暴所以使得data操作速度很快。所以在Debug版本下,at操作符与ptr操作符相较于data,速度还是慢了不少。
    另外在Debug版本下,at操作要比指针操作慢得多,所以对于不连续数据或者单个点处理,可以考虑at操作,对于连续的大量数据,尽量不要使用它。

    (2) 对重复计算进行优化

    在博文《OpenCV学习笔记(四十三)——存取像素值操作汇总core 》中,博主提到:“在循环中重复计算已经得到的值,是个费时的工作。”并做了对比:

        int nc = img.cols * img.channels();  
        for (int i=0; i<nc; i++)  
        {.......}  
    
        //**************************  
    
        for (int i=0; i<img.cols * img.channels(); i++)  
        {......}  

    博主说经过他的测试,前者明显快于后者。
    笔者发现这正好是笔者的编程风格,躺着中了一枪…… 那么如果笔者把这一点改了,是不是也会有比较好的效率提升呢?作死的笔者尝试了一下:
    在没有更改for循环之前,Debug版本的效率是这样的:
    这里写图片描述
    之后笔者对所有for循环做了上面的优化,测试结果如下:
    这里写图片描述
    结果显示,确实对于这几种遍历方式都是有一定的提升效果的。

    3. Release版本

    笔者尝试运行了一下Release版本,结果如下:
    这里写图片描述
    作对比如下:

    Method Debug(ms) Release(ms)
    ptr 15.226 3.5582
    at 35.370 2.8227
    data 11.642 2.1686

    的确,Release版本下的遍历方法基本上效率都差不多,尤其是at,本来速度应该最慢,但在Release版本下也有很快的速度。这是由于Mat::at()操作在Debug模式下加入了CV_DbgAssert()来判断一些条件,所以拖慢了函数的运行;而在Release版本下没有了CV_DbgAssert(),所以速度有了提升。

    四. 其他遍历像素点的方法

    笔者推荐博文《【OpenCV】访问Mat中每个像素的值(新) 》,博主在文中提出了十余种遍历像素点的方法,且在文章最后给出了各种方法的运行效率,可谓十分详细,所以笔者在此就不赘述了。
    注:博主的对不同方法的比较,在评论区也被指出都是在Debug版本下的对比,如果将程序调整至Release版本,各个方法的效率也没有太大差别。

    展开全文
  • LCD编程显示像素点

    千次阅读 2019-11-22 20:30:31
    分辨率的意思是:每行有800个像素点,一共有480行。 全彩的意思是:像素点显示的颜色是由RGB红绿蓝三种单颜色组成,其次还有阿尔法粒子透明度A。也就是ARGB。 LCD显示屏在工作中需要显卡,显卡中要有显存。而本次...

    一 LCD屏幕

    本次学习使用的LCD屏是一块 800*480 分辨率的全彩屏幕。
    分辨率的意思是:每行有800个像素点,一共有480行。
    全彩的意思是:像素点显示的颜色是由RGB红绿蓝三种单颜色组成,其次还有阿尔法粒子透明度A。也就是ARGB。

    LCD显示屏在工作中需要显卡,显卡中要有显存。而本次学习的开发板没有独立显卡,所以需要分配虚拟显存。而每个像素点在显存中占据4个字节的空间,也就是A、R、G、B各占一个字节。

    二 操作显示屏

    1 直接操作

    我们仿照对文件的读写操作方法,来直接读写LCD显示屏。

    对文件的操作可翻阅: 文件IO

    首先,理一下思路,步骤是:
    ① 打开LCD对应的设备文件,利用open函数,权限是可读写,返回入口指针;
    ② 利用write函数往打开后的入口写入颜色数据;
    ③ 关闭设备文件,close函数。

    这样做的依据与原理是什么呢?在这里插入图片描述
    如图,这样做的原理是编写程序直接访问LCD的设备文件,LCD属于字符设备,通过给设备文件写入数据,然后设备文件的驱动会一直被系统所调用,不断地来读取设备文件里面的数据,从而将其转化为实际的效果,也就是LCD屏幕像素点的显示效果。像图中,我们为第三个像素点写入0xff0000的数据,它将显示为红色。一个像素点的数值刚好为一个int型,也就是四个字节。

    我们创建一个lcd.c文件,编写代码来实现这个原理。

    #include <stdio.h>
    /* open */
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    /* write close */
    #include <unistd.h>
    
    int main()
    {
    	int colorbuf[800*480] = {0};
    	int i;
    	
    	//1 打开lcd,权限为读写
    	int lcd_fd = open("/dev/fb0", O_RDWR);
    	if(lcd_fd == -1)
    	{
    		perror("打开lcd错误,原因是");
    		return -1;
    	}
    	
    	// 准备颜色数据
    	for(i=0; i<800*480; i++)
    	{
    		colorbuf[i] = 0xFF0000;
    	}
    
    	//2 写颜色数据
    	write(lcd_fd, colorbuf, 800*480*4);
    	
    	//3 关闭lcd
    	close(lcd_fd);
    }
    

    我们先编译然后运行一下程序。可以看到整个屏幕被慢慢覆盖为红色。开始有一些小点没有被覆盖,但最终的效果是覆盖整个屏幕的。

    这里解释一下代码,里面有一些地方可能会难以理解。
    int lcd_fd = open("/dev/fb0", O_RDWR);
    open打开的就是LCD屏幕的设备文件,它的路径是在 /dev 根目录下设备目录中,它的名字为 fb0。需要给打开权限为只写或者可读写。然后存储返回的入口地址。

    for(i=0; i< 800*480; i++) { colorbuf[i] = 0xFF0000; }
    这个 colorbuf 数组的类型为 int 型,数组大小为 800x480,这个数组代表整个屏幕所有的像素点,共有 800x480 个,每个像素点都是 int 型,这个在上面已经解释过了。用 for 循环遍历每个像素点,为每个像素点涂上红色。每个像素点的地址都是从左往右递增的,所以当第一行 800 个像素点赋值之后,下一个地址会指向第二行首个像素点,有点类似于自动换行。

    write(lcd_fd, colorbuf, 800 * 480 * 4);
    这可能是最难理解的地方了,难点在于第三个参数。其实这也不难理解。第一个参数是被写入数据的入口地址,第二个参数是写入的数据内容的首地址,第三个参数是所写内容的长度。由于 colorbuf 是 int 型,共有 800x480 个数据,而 int 型为 4 个字节,那么总的字节数就是 800x480x4 个。如果还是难以理解,那么就将程序改为strlen,也是一样的效果。

    write(lcd_fd, colorbuf, strlen(colorbuf)); 
    

    2 内存映射

    直接操作的方法能够实现我们所要的显示效果。但是当运行程序时,可以观察到,程序在终端中很快就被运行完毕,可在LCD显示屏中,却出现类似卡顿的效果,像素点不是一下子铺满整个屏幕,而是缓慢地,一卡一卡地慢慢覆盖屏幕。这是为什么呢?

    这是因为被程序打开的LCD设备文件处于系统的内核级,我们直接对内核级里面的文件进行修改,进行数据的写入,这些数据直接被驱动捕获,然后显示成图像被我们所看到。如果写入数据这一整个过程没有结束,我们观察到的图像就是一卡一卡,慢慢地覆盖屏幕。也就是说,驱动一直在跑,而数据写入跟不上,从而影响了整体的运行效率,最终造成了图像显示的卡顿。那么,如何解决这个问题呢?

    解决这个问题的方法就是先将图像的颜色数据准备好,然后直接放入LCD设备文件。直接一整张图放入,而不是慢慢地写入。可这样的操作是不被允许的,因为LCD设备文件是处于系统的内核级,我们没有这样操作的权限。那如何办呢?只能采用内存映射的方法。

    内存映射,就是将内核级的文件入口地址与我们编程使用的用户级内存空间进行映射。我们开辟一块内存空间,然后这块内存空间与内核级的文件捆绑,如果我们对这块内存空间进行操作,也就相当于对设备文件进行操作。

    这样,当我们对映射的内存空间直接一次性放入图像颜色信息时,数据也会同时出现在内核级的文件中。那么,驱动读取到颜色数据就是流畅的,而不是一顿、一顿地读取。最终的结果就是图像流畅地进行显示。
    在这里插入图片描述

    我们创建一个lcd_map.c文件,编写代码来实现这个原理。

    #include <stdio.h>
    /* open */
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    /* write close */
    #include <unistd.h>
    /* memcpy */
    #include <string.h>
    /* mmap */
    #include <sys/mman.h>
    
    int main()
    {
    	int colorbuf[800*480] = {0};
    	int i;
    	
    	//1 打开lcd
    	int lcd_fd = open("/dev/fb0", O_RDWR);
    	if(lcd_fd == -1)
    	{
    		perror("open lcd error");
    		return -1;
    	}
    	//映射,返回映射空间的首地址
    	int *lcd_memory = mmap(NULL,   				//映射空间的首地址, 如果写NULL 表示由系统自动分配空间
    						 800*480*4, 			//要映射的空间的大小
    						 PROT_READ|PROT_WRITE, 	//权限可读可写
    						 MAP_SHARED,			//映射空间的作用为共享内存
    						 lcd_fd,				//有效的文件描述符(入口)
    						 0);					//偏移量
    					
    	
    	
    	// 准备颜色数据
    	for(i=0; i<800*480; i++) 
    	{
    		colorbuf[i] = 0xFF0000; 
    	}
    	
    	
    	//2 写颜色数据
    	//使用memcpy函数把一个空间的数据复制到另一个空间
    	//直接将数据拷贝放入,避免慢慢写入造成的图像卡顿
    	memcpy( lcd_memory, colorbuf, 800*480*4);
    
    	//释放内存空间
    	munmap(lcd_memory, 800*480*4);
    	//3 关闭lcd
    	close(lcd_fd);
    }
    

    我们对代码进行编译然后运行,可以看到屏幕瞬间就被点亮为红色。而不会出现卡顿的现象。

    这里就不对代码进行解释。

    三 作业

    1、在LCD屏幕上,从上到下显示红橙黄绿青蓝紫七种颜色,实现彩虹屏幕效果。
    2、封装LCD屏的显示函数,实现从左到右彩虹屏幕效果。
    3、实现进度条滚动效果。

    作业1代码:

    /************ 作业1 *************/
    #include "stdio.h"
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <string.h>
    
    int main()
    {
    	int lcd_fd = open("/dev/fb0",O_RDWR);
    	int color_buf[800 * 480]={0};
    	//红橙黄绿青蓝紫
    	int color_caihong[7]={0xFF0000,0xFF6100,0xFFFF00,0x00FF00,0x00FFFF,0x0000FF,0xA020F0};
    	int i,j;
    	int* lcd_memory = mmap( NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
    
    	//前6个区域,高为70
    	for(i=0;i<6;i++)
    	{
    		for(j=i*800*70 ; j<800*70*(i+1);j++)
    			color_buf[j] = color_caihong[i];
    	}
    	//第7个区域,高为60
    	for(i=800*70*6;i<800*480;i++)
    	{
    		color_buf[i] = color_caihong[6];
    	}
    	//写入颜色
    	memcpy(lcd_memory,color_buf,800*480*4);
    
    	munmap(lcd_memory,800*480*4);
    	close(lcd_fd);
    	return 0;
    }
    

    作业1效果:
    在这里插入图片描述

    作业2代码:

    /************ 作业2 *************/
    #include "stdio.h"
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <string.h>
    
    /**************************************
    *	函数功能:指定坐标显示任意宽高任意颜色的矩形
    *	函 数 名:showColor
    *	参	  数:x		:起点x坐标
    *			  y		:起点y坐标
    *			  width	:宽度
    *			  height:高度
    *			  color :颜色
    *	返 回 值:成功返回 0,失败返回 -1
    *	说	  明:按行从上到下扫描
    *****************************************/
    int showColor(int x, int y, int width, int height, int color)
    {
    	int i,j,x_end,y_end;
    	int lcd_fd = open("/dev/fb0", O_RDWR);
    	int* lcd_memory = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
    
    	if(((x_end = x+width) > 800) || ((y_end = y+height) > 480))
    	{
    		printf("超过宽度或者高度限制\n");
    		return -1;
    	}
    
    	for(i=y; i<y+height; i++)  //扫描行
    	{
    		for(j=x; j<x+width; j++) //扫描列 
    		{
    			*(lcd_memory+i*800+j) = color;
    		}
    	}
    
    	munmap(lcd_memory,800*480*4);
    	close(lcd_fd);
    	return 0;
    }
    
    int main()
    {
    	showColor(0,0,114,479,0xff0000);
    	showColor(114,0,114,479,0xff6100);
    	showColor(228,0,114,479,0xffff00);
    	showColor(342,0,114,479,0x00ff00);
    	showColor(456,0,114,479,0x00ffff);
    	showColor(570,0,114,479,0x0000ff);
    	showColor(684,0,114,479,0xa020f0);	
    	
    	return 0;
    }
    

    作业2效果:
    在这里插入图片描述

    作业3代码:

    #include "stdio.h"
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <string.h>
    
    /**************************************
    *	函数功能:清除屏幕内容
    *	函 数 名:clearLCD
    *	说	  明:屏幕变黑
    *****************************************/
    void clearLCD()
    {
    	int lcd_fd = open("/dev/fb0", O_RDWR);
    	int* lcd_memory = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
    	memset(lcd_memory,0,800*480*4);//将lcd_memory起始的800*480*4的空间设置为0
    	munmap(lcd_memory,800*480*4);
    	close(lcd_fd);
    }
    
    /**************************************
    *	函数功能:指定坐标显示进度条
    *	函 数 名:showBar
    *	参	数:
    *			x		:起点x坐标
    *			y		:起点y坐标
    *			width	:宽度
    *			height	:高度
    *			color	:颜色
    *	说	  明:按列从左到右扫描
    *****************************************/
    void showBar(int x,int y,int width,int height,int color)
    {
    	int i,j;
    	int lcd_fd = open("/dev/fb0", O_RDWR);
    	int* lcd_memory = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
    	
    	for(j=x; j<x+width; j++)//扫描列 
    	{
    		for(i=y; i<y+height; i++)//扫描行
    		{
    			lcd_memory[i*800+j] = color;
    		}
    		usleep(3000);//关键!!延时才能产生动态效果
    	}
    	munmap(lcd_memory,800*480*4);
    	close(lcd_fd);
    }
    
    
    int main()
    {
    	clearLCD();
    	showBar(140,100,500,40,0xff0000);
    	return 0;
    }
    

    四 总结

    在这里插入图片描述

    展开全文
  • python中使用PIL库对图片像素点遍历以及像素值改变将图片中特定像素值的部分改为另一种像素值读取图片的像素值改变颜色实验图片展示完整代码 将图片中特定像素值的部分改为另一种像素值 实验目的:将一张图片中白色...

    将图片中特定像素值的部分改为另一种像素值

    实验目的:将一张图片中白色的部分变成蓝色,灰色的部分改成黄色…,使用了PIL库,PIL库只支持python2.X版本,在python3.X版本中被并到pillow库中,想要使用PIL要先导入pillow库,然后在代码引用from PIL import Image 即可。

    在这里插入图片描述

    读取图片的像素值

    先对整个图片每个像素点的像素值进行遍历,PIL库需要将图像先转成numpy数组,img_array = np.array(img)或者img = np.asarray(image),后面存储处理好的新图像时要把数组转成图像, dst[h,w] = img_array[h,w],img2 = Image.fromarray(np.uint8(dst))。

    改变颜色

    将特定像素值的点改变颜色

    height = shape[0]
    width = shape[1]
    dst = np.zeros((height,width,3))
    for h in range(0,height):
        for w in range (0,width):
            (b,g,r) = img_array[h,w]
            if (b,g,r)==(255,255,255):#白色
                img_array[h,w] = (0,255,255)#蓝色
    

    实验图片展示

    左图为原始图像,右图为处理后的图像
    在这里插入图片描述

    完整代码

    代码片.

    from PIL import Image
    import numpy as np
    img = Image.open("F:/PYproject/unet_camvid/CamVid/testimage/2.png")
    #img.show()
    img_array = np.array(img)#把图像转成数组格式img = np.asarray(image)
    shape = img_array.shape
    print(img_array.shape)
    for i in range(0,shape[0]):
        for j in range(0,shape[1]):
            value = img_array[i, j]
            #print("",value)
            if value[0] != 0:
                print("", value)
    height = shape[0]
    width = shape[1]
    dst = np.zeros((height,width,3))
    for h in range(0,height):
        for w in range (0,width):
            (b,g,r) = img_array[h,w]
            if (b,g,r)==(255,255,255):#白色
                img_array[h,w] = (0,255,255)#蓝色
            if (b, g, r) == (85, 85, 85):  # 深灰
                img_array[h, w] = (0, 128, 0)  # 绿色
            if (b, g, r) == (170, 170, 170):  # 灰色
                img_array[h, w] = (255, 255, 0)  # 黄色
            if (b, g, r) == (0, 0, 0):  # 黑色
                img_array[h, w] = (255, 0, 0)  # 红色
            dst[h,w] = img_array[h,w]
    img2 = Image.fromarray(np.uint8(dst))
    img2.show(img2)
    img2.save("3.png","png")
    
    
    展开全文
  • OpenCV像素点处理

    千次阅读 2011-05-10 09:34:00
    OpenCV像素点处理
  • 像素点和设备独立像素点的区别

    千次阅读 2011-05-17 17:38:00
    这里要提到另外一个概念,density,直译是密度,在显示领域里表示每平方英寸的像素点密度,每个像素点可以近似看作屏幕上的一个发光 点,点的密度越大,则显示效果越清晰,在单位面积下显示内容越多。 Android...
  • opengl 获取像素点颜色

    千次阅读 2016-07-28 19:37:55
    opengl获取像素点颜色
  • 可以使用xnview来快速查看图片中某个像素点的像素值;
  • python PIL获取图片像素点

    千次阅读 2020-03-01 14:27:34
    from PIL import Image # 打开要处理的图像 img_src = Image.open('sun.png') # 转换图片的模式为...# 获得文字图片的每个像素点 src_strlist = img_src.load() # 100,100 是像素点的坐标 data = src_strlist[...
  • 图像处理-像素点分析

    万次阅读 2017-11-27 19:32:40
    一张图片放大以后就会呈现一个个的点阵,每一个点就是一个像素点,通过我们对RGB的颜色配比就可以显示出不同的颜色效果了,所以说最终我们想对一个图像进行处理的话就是对一个个像素点进行处理。
  • GetPixel 取像素点

    千次阅读 2013-07-21 11:19:04
    函数原型:;... nXPos:指定要检查的像素点的逻辑X轴坐标。  nYPos:指定要检查的像素点的逻辑Y轴坐标。  返回值:返回值是该象像点的RGB值。如果指定的像素点在当前剪辑区之外;那么返回值是CLR_I
  • 取图像中的像素点

    千次阅读 2018-08-28 20:13:44
    src为读取的Mat类型图像 像素点的像素值:src.at&lt;uchar&gt;(i, j) 像素点的RGB通道,k值对应着不同的通道:(uchar)src.at&lt;Vec3b&gt;(i, j)[k]
  • opencv 基于图像单像素点的处理

    千次阅读 2019-01-10 21:54:51
    1 基于图像单像素点的处理 看过数字图像处理一书的都知道,图像处理中基于像素点的处理分为两种 灰度变换:本质就是基于单像素点的变化处理。 空间滤波:本质就是基于邻域像素点的变化处理。 今天要讲的是在OpenCv下...
  • OpenCV的Mat操作像素点

    千次阅读 2018-05-27 10:09:05
    Mat数据结构,操作灰度图像像素点:int gray_value = (int) image.at&lt;uchar&gt;(i , j) ;操作彩色图像像素点:int color_value = (int) image.at&lt;Vec3b&gt;(i , j) [k];说明:其中gray_value...
  • c语言像素点的简单获取

    千次阅读 2018-06-10 16:36:23
    多日的单片机,让我心烦,今天写一个获取像素点信息的程序,查百度。呵呵 并没有发现什么有用的信息,还是走老本行吧。/******************************************************************** ** 文 件 名:像素点...
  • 基于像素点特征的Harris角点检测图像拼接(matlab实现) piccolo,之前做的东西,简单整理下,不是做图像方向的,写的不好轻喷 主要原理参看Harris角点检测原理及实现和Harris角点检测算法优化 下面简单说下: ...
  • 用matlab实现,如何对一幅图片不同像素点进行多条曲线拟合?相同的像素点用一条曲线拟合。如图。![图片说明](https://img-ask.csdn.net/upload/201803/23/1521775083_135625.jpg)其中不同颜色代表不同像素点。把它拟...
  • python+OpenCV 图片像素点的读写操作

    千次阅读 2019-08-18 14:38:24
    #对图片的像素进行读写操作 ...4.图片的宽和高 “640*480”表示图片在水平方向上有640个像素点,在竖直方向上有480个像素点 5.未压缩的图片的计算方法: 1.14M(图片的大小)=720*547(图片像素点的个数)*3(颜色通...
  • Qt快速绘制像素点的处理方法

    千次阅读 2018-12-13 16:12:09
    在有些情况下,我们需要对屏幕上的像素点进行大量的绘制操作。比如我之前模拟写的一个渲染管线开源练习,涉及到了大量的像素点操作。而Qt本身的QPen和QPainter::drawPoint的API如果操作大量的像素点,会非常耗时,...
  • 一、遍历访问图片每个像素点,并修改相应的RGB import cv2 as cv def access_pixels(image): print(image.shape) height = image.shape[0] width = image.shape[1] channels = image.shape[2] print(&...
  • OpenCV访问像素点的灰度值

    千次阅读 2019-02-25 15:48:10
    单通道图片访问像素点灰度值 img_1.ptr&lt;uchar&gt;(i)[j]; 多通道图片访问像素点灰度值 img_1.ptr&lt;uchar&gt;(i)[j*3]; //第i行j列的B通道其灰度值: img_1.ptr&lt;uchar&gt;(i)[j*3...
  • 因为最近在做交互式图像分割方面的东西,当用户去点击图片上的某一点时,即标注点,要计算该图像上所有的像素点到这个点的距离,作为后续步骤的预处理。 用到的函数 scipy.ndimage.distance_transform_edt ...
  • 使用Opencv获取每个像素点的RGB颜色分量/HSV颜色分量

    万次阅读 多人点赞 2018-03-27 17:16:47
    一. 所需结构体CvScalar结构体介绍typedef struct ... 所需函数cvGet2D函数功能:获取像素点上的RGB颜色分量,如果是灰度图直接返回灰度图颜色分量函数原型:CvScalar cvGet2D( const CvArr* arr, int idx0, int idx...
  • 在同一块小的区域内,像素点级别的颜色填充,如果一开始这一快区域是红色的,然后再直接调用填充方法在这块区域填充成黄色,那么这块区域被填充之后,是变成黄色?还是红色和黄色混到一块变成第三种颜色?Java方面的...
  • 官方文档中没看到有绘制点的调用,有直线和园矩形几何图形等,我就想绘制一个像素点
  • python opencv 修改某像素点

    千次阅读 2018-07-02 16:15:04
    #coding=utf-8 #修改某像素点值 import cv2 import numpy as np img=cv2.imread('./o.jpg') print img.item(10,10,2) img.itemset((10,10,2),100) print img.item(10,10,2)
  • python使用PIL模块获取图片像素点

    万次阅读 2018-05-16 15:57:27
    from PIL import Image########获取图片指定像素点的像素def getPngPix(pngPath = "aa.png",pixelX = 1,pixelY = 1): img_src = Image.open(pngPath) img_src = img_src.convert('RGBA') str_strlist =...
  • 一个可以锚点定位的 ImageView(根据像素点)
  • python中除了使用PIL库处理图像之外还可以使用CV2 import cv2 import numpy as np fileName = '2.png' print(fileName) img =...dst) 结果图: 使用PIL库遍历图片以及改变颜色详见上篇博客: PIL遍历像素以及改变像素

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,018
精华内容 12,807
关键字:

像素点