使用樹莓派制作簡易監控模塊


    幾年前買的Raspberry P 1 (CPU: 700MHz ARM,  Memory: 512MB) 已積灰很久, 偶然發現嘗試做使用它的拍照功能做監控,如下圖:

 

1、系統安裝與配置

  首先安裝 raspbian-jessie-lite (官方下載),在windows下使用win32diskimager寫入SD卡比較容易。

  其次插入SD卡,接通電源啟動系統。對於沒有顯示器(也沒有外接的鍵盤)的情況下,只能選擇網絡方式訪問。遇到了一些問題

  (1)  雖然可以通過家庭路由器得到DHCP分配的IP(或者根據ARP,甚至使用nmap掃描)得到這個樹莓派的IP,但是前提要求網絡環境比較高。

  (2)  從2016.11月以后,Raspbian系統默認關閉SSH(估計是考慮安全性),也就是系統啟動后SSH服務根本沒啟動。

 

這種情況下只能通過修改系統里面的IP等相關參數使得啟動后能得到固定的IP。對於在windows下讀寫Linux分區始終是個不方便的事。

查找了多方面資料,只有Ext2Fsd能滿足要求,但是按照后插入SD卡卻變成了下圖這樣:

 

結果Linux分區無法成為一個被Ext2Fsd識別的卷,所以這個辦法就到這里終止了。也嘗試過將整個系統刻入SD卡(使用EXT3),但是系統無法啟動。

最后,只能通過Linux系統來讀寫這個Linux分區。在這里使用back track 5來制作一個U盤系統然后重啟筆記本,嘗試通過筆記本修改SD卡里面的系統參數。

 

SD卡在BT5下掛載成功:

 

然后修改 /etc/network/interfaces文件,配置一個固定IP,如下:

 

現在IP確定了,但是還有SSH默認情況下是關閉的,所以需要開機啟動,修改/etc/rc.local文件,加入SSH的啟動命令,如下圖:

 

然后umount SD卡后插入樹莓派啟動,狀態燈顯示工作正常,如下圖:

然后使用筆記本嘗試ping&ssh:

C:\>ping 192.168.0.125

Pinging 192.168.0.125 with 32 bytes of data:
Reply from 192.168.0.125: bytes=32 time=3ms TTL=64
Reply from 192.168.0.125: bytes=32 time=4ms TTL=64
Reply from 192.168.0.125: bytes=32 time=2ms TTL=64
Reply from 192.168.0.125: bytes=32 time=3ms TTL=64

Ping statistics for 192.168.0.125:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 2ms, Maximum = 4ms, Average = 3ms
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Aug 10 20:37:32 2017 from 192.168.0.210


pi@x:~$ 
pi@x:~$ 

至此,系統配置成功,下面可以通過raspi-config命令啟用SSH功能了,然后把rc.local中的啟動命令刪除,另外更改密碼。

 

2. 監控拍攝對比

通過raspi-config工具啟用Camera功能並把攝像模塊插入,然后就可以使用raspistill工具進行拍照了。

再這里使用Java開發定時拍照任務並做圖像對比,由於Camera不能通過Java直接調用,在這里就使用了jrpicam (https://github.com/Hopding/JRPiCam) 開源組件,

通過調用raspistill進行拍照然后得到圖像,整個工程的建立如下圖,我將從github得到的源碼一起合並到工程里面,工程就叫做smallcat吧(像一只貓一樣盯着):

 

2個類,一個Camera類是拍照獲得圖像的:

public class Camera {
    
    private RPiCamera piCamera = null;
    private String defaultSaveDir = "/home/pi/diff-photo";
    
    public Camera() {
        try {
            piCamera = new RPiCamera(defaultSaveDir);
        } catch (FailedToRunRaspistillException e) {
            e.printStackTrace();
        }
    }
    
    public BufferedImage takeOnePhoto() throws Exception {
        piCamera.setAWB(AWB.AUTO)       // Change Automatic White Balance setting to automatic
            .setDRC(DRC.OFF)            // Turn off Dynamic Range Compression
            .setContrast(100)
            .setSharpness(100)
            .setQuality(100)
            .setTimeout(1)
            .setBrightness(75)
            .turnOnPreview()            // Turn on image preview
            .setEncoding(Encoding.PNG); // Change encoding of images to PNG
        
        BufferedImage buffImg = piCamera.takeBufferedStill(800, 600); // Take image and store in BufferedImage
        return buffImg;
    }
    
}

 

還有一個MyCat是主任務類,獲取圖像后對比,符合條件后保存圖像:

public class MyCat {
    
    private Camera camera = null;
    private double diffPercentThreshold = 4.0;
    
    private String diffPhotoSaveDir = "/home/pi/diff-photo/";
    
    public static void main(String[] args) {
        System.out.println("My-Cat starting...");
        MyCat cat = new MyCat();
        cat.wakeUpMyCat();
        System.out.println("My-Cat started!");
    }
    
    public void wakeUpMyCat() {
        camera = new Camera();
        Thread inspector = new Thread(new CatInspector());
        inspector.setName("Cat-Inspector");
        inspector.start();
    }
    
    class CatInspector implements Runnable {
        private BufferedImage previousPhoto = null;
        
        @Override
        public void run() {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e1) {
                    System.out.println("Cat Inspector Interrupted");
                    return;
                }
                
                try {
                    BufferedImage currentPhoto = camera.takeOnePhoto();
                    if (previousPhoto != null) {
                        if (isDifferent(currentPhoto)) {
                            foundDiffPhoto(currentPhoto);
                        }
                    } else {
                        // Save the first photo
                        foundDiffPhoto(currentPhoto);
                    }
                    
                    previousPhoto = currentPhoto; // Set current photo as previous
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
        /**
         * Process different photo
         */
        private void foundDiffPhoto(BufferedImage photo) {
            String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
            File saveFile = new File(diffPhotoSaveDir + fileName + ".png");
            try {
                ImageIO.write(photo, "png", saveFile);
                System.out.println("New image saved to: " + saveFile.getAbsolutePath());
            } catch (IOException e) {
                System.out.println("Save image error: ");
                e.printStackTrace();
            }
        }
        
        /**
         * Compare current photo with previous photo
         */
        private boolean isDifferent(BufferedImage currentPhoto) {
            int currentWidth = currentPhoto.getWidth();
            int currentHeight = currentPhoto.getHeight();
            
            int previousWidth = previousPhoto.getWidth();
            int previousHeight = previousPhoto.getHeight();
            
            if ((currentWidth != previousWidth) || (currentHeight != previousHeight)) {
                System.err.println("Error: Images dimensions mismatch");
                System.exit(1);
            }
            long diff = 0;
            // Find RGB difference
            for (int y = 0; y < currentHeight; y++) {
                for (int x = 0; x < currentWidth; x++) {
                    int rgb1 = currentPhoto.getRGB(x, y);
                    int rgb2 = previousPhoto.getRGB(x, y);
                    
                    int r1 = (rgb1 >> 16) & 0xff;
                    int g1 = (rgb1 >> 8) & 0xff;
                    int b1 = (rgb1) & 0xff;
                    
                    int r2 = (rgb2 >> 16) & 0xff;
                    int g2 = (rgb2 >> 8) & 0xff;
                    int b2 = (rgb2) & 0xff;
                    
                    diff += Math.abs(r1 - r2);
                    diff += Math.abs(g1 - g2);
                    diff += Math.abs(b1 - b2);
                }
            }
            double n = currentWidth * currentHeight * 3;
            double p = diff / n / 255.0;
            
            double diffPercent = (p * 100.0);
            System.out.println("Diff percent: " + diffPercent);
            return diffPercent > diffPercentThreshold;
        }
    }
    
}

 

圖像的相似性匹配是一個復雜的論題,這里使用最簡單的RGB值比對(復雜的算法這個小樹莓很難承受),每次比對后睡眠3秒鍾后面再執行任務。

打包jar放到樹莓派上面執行吧,但首先需要安裝下JDK:

# sudo apt-get update
# sudo apt-get install oracle-java7-jdk

 

3. 結果如何 ?

(1)  得到記錄的兩張圖片如下(相似比例調試到一個比較合適的值)。

 

 

(2)  這個攝像頭確實比較差,嘗試調整參數效果也不理想,沒有自動對焦等功能對於監控來說實際上用起來困難。

(3)  簡單的RGB循環對比,CPU一下子撐到80-90%,這個單核的700MHz CPU做這類工作確定有點困難,高負載的時候SSH經常卡頓。

(4)  總之,實用性不強。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM