分類: Telegram

  • 想知道網戀對象有沒有修圖嗎?試試看這款修圖偵測機器人!

    想知道網戀對象有沒有修圖嗎?試試看這款修圖偵測機器人!

     

    前陣子在咱們一群影像愛好者的群組開始流傳一套程式,
    一套號稱能檢測愛情動作片封面詐欺的程式!

    什麼?天底下有這等好事?

    於是我找到了開發這套程式的仁兄要到了原始論文,
    認為他實現得不夠完美,間接促使我完成這項服務。

    這篇文章可以幫你得出一個修圖參考值
    (但某些情況不適用,文後會補充說明。)

    接著會介紹論文以及背後數學原理,
    對於檢測服務比較有興趣可以直接跳到後面。

    原理

    本文是 Analyzing Benford’s Law’s Powerful Applications in Image Forensics 這篇論文的延伸應用:

    要講解這篇論文就要先解釋什麼是 Benford’s Law?

    Benford’s Law 的概念就是人類世界中隨機數其實並不隨機,
    其中數據的首位數字是遵循某種規律,這個規律就是 Benford’s Law。

    Benford’s Law 公式:

    $F_a = log_{10}{(frac{a+1}{a})}$, for all a = 1,2,…,9

    這樣算起來會呈現由首位數字出現比率是 1 往 9 遞減的一個分佈:

    舉個 Benford’s Law 的例子,

    如果你跑去韓總的宇宙造勢場合抓一萬人來訪問,

    問他們每個人存款有多少?

    如果他們沒有說謊的話,這一萬人的存款首位數字應該會符合 Benford’s Law。

    這篇論文主要結論是圖片經過 離散餘弦轉換(Discrete Cosine Transform)以下簡稱 DCT 後,
    轉換後的圖片會服從 Benford’s Law。

    而論文本意是拿這個結論做二次壓縮來估測 JPEG 的壓縮率,
    有興趣的大家可以自己閱讀一下論文

    實作

    我一開始是用土法煉鋼套 二維 DCT 公式:
    $D(i, j) = frac{2}{N} C(i)C(j) sum_{x=0}^{N-1} sum_{y=0}^{N-1} f(x, y) cosleft[frac{(2x+1)ipi}{2N}right] cosleft[frac{(2y+1)jpi}{2N}right]$

    $C(u) =
    begin{cases}
    frac{1}{sqrt{2}}, & text{if } i = 0 \
    1, & text{otherwise}
    end{cases}$

    $C(v) =
    begin{cases}
    frac{1}{sqrt{2}}, & text{if } j = 0 \
    1, & text{otherwise}
    end{cases}$

    慢得要死,時間大約是 OpenCV 內建二維 DCT 轉換的 4.5 倍。

    身為一個影像從業者,
    一定要做到比內建函式庫快!

    加速

    俗話說得好: 

    要看一個人會不會做立委,就要看他怎麼做立委。

    我是說要加速就要從數學看起,所以讓我們來看一下公式:

     
    其中 $D(i, j)$ 是轉換後的值,$i, j$ 是位置參數;
    $f(x, y)$ 是原始圖片的亮度值,$x, y$ 也是位置參數。

    發現亮度值可以提出來,其中位置參數可以自己組一個矩陣相乘,
    我們暫且把包含所有 $i, j$ 組合的稱為母矩陣。
    這個母矩陣可以重複用,就不用每個區塊都要算。
    (原始圖片會被 N*N 的小區塊分割,N 通常是 8。)

    因為一組 $i, j$ 負責一組子矩陣,
    所以如果輸出入尺寸相同,母矩陣大小為: $N * N * N * N$

    於是我用這方法寫了一個 Mask 法,
    的確速度與 OpenCV 內置函數比肩了,但是還沒有超越(#。

    再加速

    於是我又對平行運算生起了一絲邪念,
    如果我宣告一組共享記憶體紀錄首位數字,
    並對計算每個區塊的計算採取平行運算呢?

    起初我是用 Lock  方法,後來發現這樣設計寫入的時候會有 Race Condition 問題,
    後來便採取 Lock free 實作,缺點是佔用的記憶體較多。

    效率展示


    圖中數據為計算一張 1280*1280 的彩色 jpg 圖片:
    oldSingleTransform 為單執行緒的土法煉鋼法(公式法),
    SingleTransform 及 MultiTransform 則分別為 Mask 的 單執行緒、多執行緒方法。

    數據判讀


    本人拿自己的相片去做測試,發現若是原圖進去做計算,
    則出來的估計值大於 1 的話很有可能是修圖。
    若追求計算效率做 DownSample 到 512*512 的話,
    估計值約莫大於 0.4 就有可能是修圖。

    目前線上提供服務的機器人都是使用 DownSample 方法做計算。

    特別需要說明的是手機原生相機就會做白平衡、明暗部校正,
    尤其是 iPhone 做得非常優秀。

    (逆光下拍照還能看清人臉,這是一種明暗部校正的技術。)
    經過白平衡及明暗部校正後的圖片在本方法看來也是一種修圖,
    需要特別注意。
    惟整個圖片加權同一個值不是,
    所以如果整張調亮/暗的偵測不出來。

    開源


    本方法開源在 Github 
    https://github.com/wuyiulin/GraphAppBot

    也同時提供線上機器人服務,只要傳圖片就能得到修圖估計值:
    點我去 Telegram 機器人

    如果有錯誤歡迎聯絡我:wuyiulin@gmail.com

    Murmur


    想修改源碼的話,裡面有個 Ampfactor 是因為我使用 float32 存 Mask 值(DCT係數),
    係數有正負,為了避免計算途中正負相減過小(float32 小數點後有效七位),
    而把值歸零所以加入的。

    原始論文使用 Uncompressed Colour Image Database 資料庫,
    探討使用原始無損圖片如何估計 JPEG 壓縮率,
    所以本文的物理意義是估算原始無損圖片與輸入圖片的差異度。

    本文估計值僅供參考,
    窮盡科技之力後不如鼓起勇氣約網戀對象出來走走!

    謝謝大家

  • TelegramBot :花十分鐘開發聊天機器人,多執行十小時的程式!(Python)

     

    身為一個工科的研究生,你是否也有以下的困擾:

    "總是搶不到實驗室機器"、"擔心這個禮拜的實驗做得不夠多"、"睡前忘記收數據,導致下的實驗比別人少?"

    沒問題,你遇到的情況我通通都有遇過!

    筆者本人是心血來潮就會從臺中出發去花東吃早餐的放浪少年(#。

    但是週六日還要下實驗,那該怎麼辦?

    近期又要面臨暑期工作壓力,實驗進度更是要顧,

    所以產出了這篇文章。

    這篇文章能幫助你什麼?

    在 Python 程式跑完的時候,發 Telegram 訊息通知你。

    OK,讓我們開始吧。

    首先先安裝一下本文所使用的套件:

    pip3 install telegram
    pip3 install python-telegram-bot

    移到你目前想提醒 Python 程式的目錄底下

    cd ~/your_Python_program

     

    建立 Telegram 機器人的主程式(或是從這裡下載我寫好的)

    touch TelegramBot.py

    寫檔,接著複製貼上就可以了。

    vim TelegramBot.py
    import os
    import sys
    import re
    import configparser
    import telegram.ext
    import requests
    import time
    
    
    import logging
    
    from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
    
    # Enable logging
    logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                        level=logging.INFO)
    
    logger = logging.getLogger(__name__)
    # Initial bot by Telegram access token
    
    config = configparser.ConfigParser()
    config.read('config.ini')
    
    
    def start(update, context):
        """Send a message when the command /start is issued."""
        update.message.reply_text('Hi!')
    
    def echo(update, context):
        """Echo the user message."""
        update.message.reply_text(update.message.text)
    
    def help(update, context):
        """Send a message when the command /help is issued."""
        update.message.reply_text('Help!')
    
    def getUID(update, context):
        update.message.reply_text(update['message']['chat']['id'])
        print(update['message']['chat']['id'])
    
    def Alert():
        TOKEN = config.get('Bot','ACCESS_TOKEN')
        UID = config.get('Bot','UID')
        updater = Updater(token=TOKEN, use_context=True)
        dp = updater.dispatcher
        text ='實驗跑完啦!快上來收數據!'
        dp.bot.send_message(chat_id=UID, text=text) # 發送訊息
    
    
    def error(update, context):
        """Log Errors caused by Updates."""
        logger.warning('Update "%s" caused error "%s"', update, context.error)
    
    def main():
        
        TOKEN = config.get('Bot','ACCESS_TOKEN')
        UID = config.get('Bot','UID')
        updater = Updater(token=TOKEN, use_context=True)
        dp = updater.dispatcher
        dp.add_handler(CommandHandler("start", start))
        dp.add_handler(CommandHandler("help", help))
        dp.add_handler(CommandHandler("Alert", Alert))
        dp.add_handler(CommandHandler("getUID", getUID))
        dp.add_handler(MessageHandler(Filters.text, echo))
        updater.start_polling()
        updater.idle()
    
    if __name__ == '__main__':
        main()
    
    (然後向 BotFather 申請自己的機器人並拿到 Token,這部分可以看別的大大寫的這篇。)
    在同目錄底下建立 config.ini

    touch config.ini
    寫 config.ini
    vim config.ini

    把 your_token 換成你剛剛拿到的 TOKEN,
    UID 待會再教你拿:

    [Bot]
    ACCESS_TOKEN = your_token
    UID = your_uid

     

    填完之後,好沒問題來!
    我們來拿 UID!
    把這支程式跑起來:

    python TelegramBot
    如果一切沒問題,你的終端機會顯示:
    apscheduler.scheduler – INFO – Scheduler started
    再開 Telegram 到你與這隻機器人的對話框,輸入:

    /getUID

    這樣在 Telegram 對話框 及 終端機應該都會把你的 UID 噴出來,

    把 UID 回填到 config.ini 裡面去。
    然後把這支程式關掉(Ctrl-C)
    在你想監聽的 Python 程式最上面加入:

    from TelegramBot import Alert
    程式執行完處加入:

    Alert()
    把機器人跑起來,把你想監聽的程式也跑起來。
    當程式執行完時便會出現:

    已解決!