diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 2e4d1b1..0e59517 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -26,6 +26,6 @@ jobs: - name: Build and push Docker image run: | - PLATFORMS=linux/arm64,linux/amd64 + PLATFORMS=linux/arm64,linux/amd64,linux/arm/v7 DOCKER_IMAGE=arcw/sgcc_electricity - docker buildx build --platform $PLATFORMS -t $DOCKER_IMAGE:latest -t $DOCKER_IMAGE:1.3.3 --file Dockerfile-for-github-action --push . \ No newline at end of file + docker buildx build --platform $PLATFORMS -t $DOCKER_IMAGE:latest -t $DOCKER_IMAGE:1.4.0 --file Dockerfile-for-github-action --push . \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f25fa89..f5555ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,7 @@ RUN cd /tmp \ && pip config --global set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \ && pip config --global set install.trusted-host pypi.tuna.tsinghua.edu.cn \ && python3 -m pip install --upgrade pip \ + && PIP_ROOT_USER_ACTION=ignore pip install onnxruntime==1.17.3 \ && PIP_ROOT_USER_ACTION=ignore pip install \ --disable-pip-version-check \ --no-cache-dir \ diff --git a/Dockerfile-for-github-action b/Dockerfile-for-github-action index 2a5258d..7adc6d7 100644 --- a/Dockerfile-for-github-action +++ b/Dockerfile-for-github-action @@ -21,17 +21,17 @@ RUN apt-get --allow-releaseinfo-change update \ RUN cd /tmp \ && python3 -m pip install --upgrade pip -# RUN if [${TARGETARCH} == "arm"]; then \ -# cd /tmp \ -# && curl -O -L https://github.com/nknytk/built-onnxruntime-for-raspberrypi-linux/raw/master/wheels/buster/onnxruntime-1.8.1-cp39-cp39-linux_armv7l.whl \ -# && PIP_ROOT_USER_ACTION=ignore pip3 install onnxruntime-1.8.1-cp39-cp39-linux_armv7l.whl \ -# && PIP_ROOT_USER_ACTION=ignore pip3 install cmake==3.14.3; \ -# fi - -# RUN if [${TARGETARCH} == "arm64"]; then \ -# cd /tmp \ -# && PIP_ROOT_USER_ACTION=ignore pip3 install onnxruntime==1.8.1; \ -# fi +RUN if [${TARGETARCH} == "arm"]; then \ + cd /tmp \ + && curl -O -L https://github.com/nknytk/built-onnxruntime-for-raspberrypi-linux/blob/master/wheels/bullseye/onnxruntime-1.16.0-cp39-cp39-linux_armv7l.whl \ + && PIP_ROOT_USER_ACTION=ignore pip3 install onnxruntime-1.16.0-cp39-cp39-linux_armv7l.whl \ + && PIP_ROOT_USER_ACTION=ignore pip3 install cmake==3.14.3; \ + fi + +RUN if [${TARGETARCH} == "arm64"]; then \ + cd /tmp \ + && PIP_ROOT_USER_ACTION=ignore pip3 install onnxruntime==1.17.3; \ + fi RUN cd /tmp \ && PIP_ROOT_USER_ACTION=ignore pip install \ diff --git a/README.md b/README.md index fb8991c..44c2d9c 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ 本镜像支持架构: > - `linux/amd64`:适用于 x86-64(amd64)架构的 Linux 系统,例如windows电脑。 -> - `linux/arm64`:适用于 ARMv8 架构的 Linux 系统,例如树莓派,N1盒子。 -> - 其他架构比如32位arm/v7,不提供docker镜像,可参考[github仓库](https://github.com/ARC-MX/sgcc_electricity_new.git)的[Dockerfile-for-github-action-armv7](%B9%E9%B5%B5%2FDockerfile-for-github-action-armv7)自行部署。 +> - `linux/arm64`:适用于 ARMv8 架构的 Linux 系统,例如树莓派3+,N1盒子等。 +> - `linux/armv7`,适用于 ARMv8 架构的 Linux 系统,例如树莓派2,玩客云等。 ## 二、实现流程 @@ -51,6 +51,7 @@ ## 三、安装 ### 1)注册国家电网账户 + 首先要注册国家电网账户,绑定电表,并且可以手动查询电量 注册网址:[https://www.95598.cn/osgweb/login](https://www.95598.cn/osgweb/login) @@ -121,9 +122,9 @@ docker compose pull # 更新镜像 docker compose up # 重新运行 ``` - 6. 运行成功应该显示如下日志: - ```bash + +```bash 2024-06-06 16:00:43 [INFO ] ---- 程序开始,当前仓库版本为1.3.3,仓库地址为https://github.com/ARC-MX/sgcc_electricity_new.git 2024-06-06 16:00:43 [INFO ] ---- enable_database_storage为false,不会储存到数据库 2024-06-06 16:00:43 [INFO ] ---- 当前登录的用户名为: xxxxxx,homeassistant地址为http://192.168.1.xx:8123/,程序将在每天00:00执行 @@ -144,7 +145,7 @@ 2024-06-06 16:01:59 [INFO ] ---- Get daily power consumption for xxxxxxx successfully, , 2024-06-05 usage is xxx kwh. 2024-06-06 16:02:07 [INFO ] ---- Webdriver quit after fetching data successfully. 2024-06-06 16:02:07 [INFO ] ---- 浏览器已退出 - ``` +``` ## 四、配置与使用 diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 0000000..f52a7a9 Binary files /dev/null and b/assets/background.png differ diff --git a/requirements.txt b/requirements.txt index 2910ae2..769cc2d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,8 @@ -# cmake==3.10.3 requests==2.31.0 selenium==4.5.0 schedule==1.1.0 Pillow==9.2.0 undetected_chromedriver==3.4.7 pymongo~=3.12.0 -onnxruntime==1.17.3 -python-dotenv -opencv-python==4.9.0.80 \ No newline at end of file +# onnxruntime==1.17.3 +python-dotenv \ No newline at end of file diff --git a/scripts/data_fetcher.py b/scripts/data_fetcher.py index f8e914d..7905839 100644 --- a/scripts/data_fetcher.py +++ b/scripts/data_fetcher.py @@ -23,7 +23,9 @@ from const import * import numpy as np -import cv2 +# import cv2 +from io import BytesIO +from PIL import Image from onnx import ONNX import platform @@ -75,45 +77,52 @@ def _get_tracks(distance): logging.info(f"image tracks distance is {sum(tracks)}") return tracks -# cv2转base64 -def cv2_to_base64(img): - img = cv2.imencode('.jpg', img)[1] - image_code = str(base64.b64encode(img))[2:-1] +def base64_to_PLI(base64_str: str): + base64_data = re.sub('^data:image/.+;base64,', '', base64_str) + byte_data = base64.b64decode(base64_data) + image_data = BytesIO(byte_data) + img = Image.open(image_data) + return img - return image_code +# # cv2转base64 +# def cv2_to_base64(img): +# img = cv2.imencode('.jpg', img)[1] +# image_code = str(base64.b64encode(img))[2:-1] -# base64转cv2 -def base64_to_cv2(base64_code): - img_data = base64.b64decode(base64_code) - img_array = np.fromstring(img_data, np.uint8) - img = cv2.imdecode(img_array, cv2.IMREAD_COLOR) - return img +# return image_code -def bytes2cv(img): - '''二进制图片转cv2 +# # base64转cv2 +# def base64_to_cv2(base64_code): +# img_data = base64.b64decode(base64_code) +# img_array = np.fromstring(img_data, np.uint8) +# img = cv2.imdecode(img_array, cv2.IMREAD_COLOR) +# return img - :param im: 二进制图片数据,bytes - :return: cv2图像,numpy.ndarray - ''' - img_array = np.fromstring(img, np.uint8) # 转换np序列 - img_raw = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED) # 转换Opencv格式BGR - return img_raw +# def bytes2cv(img): +# '''二进制图片转cv2 -def cv2bytes(im): - '''cv2转二进制图片 +# :param im: 二进制图片数据,bytes +# :return: cv2图像,numpy.ndarray +# ''' +# img_array = np.fromstring(img, np.uint8) # 转换np序列 +# img_raw = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED) # 转换Opencv格式BGR +# return img_raw - :param im: cv2图像,numpy.ndarray - :return: 二进制图片数据,bytes - ''' - return np.array(cv2.imencode('.png', im)[1]).tobytes() +# def cv2bytes(im): +# '''cv2转二进制图片 -def cv2_crop(im, box): - '''cv2实现类似PIL的裁剪 +# :param im: cv2图像,numpy.ndarray +# :return: 二进制图片数据,bytes +# ''' +# return np.array(cv2.imencode('.png', im)[1]).tobytes() - :param im: cv2加载好的图像 - :param box: 裁剪的矩形,(left, upper, right, lower)元组 - ''' - return im.copy()[box[1]:box[3], box[0]:box[2], :] +# def cv2_crop(im, box): +# '''cv2实现类似PIL的裁剪 + +# :param im: cv2加载好的图像 +# :param box: 裁剪的矩形,(left, upper, right, lower)元组 +# ''' +# return im.copy()[box[1]:box[3], box[0]:box[2], :] def get_transparency_location(image): '''获取基于透明元素裁切图片的左上角、右下角坐标 @@ -305,14 +314,14 @@ def _login(self, driver): time.sleep(self.RETRY_WAIT_TIME_OFFSET_UNIT) # swtich to username-password login page driver.find_element(By.CLASS_NAME, "user").click() - logging.info("find_element user.\r") + logging.info("find_element 'user'.\r") time.sleep(self.RETRY_WAIT_TIME_OFFSET_UNIT) # input username and password input_elements = driver.find_elements(By.CLASS_NAME, "el-input__inner") input_elements[0].send_keys(self._username) - logging.info("input_elements username :{self._username}.\r") + logging.info(f"input_elements username : {self._username}.\r") input_elements[1].send_keys(self._password) - logging.info("input_elements password :{self._password}.\r") + logging.info(f"input_elements password : {self._password}.\r") # click agree button self._click_button(driver, By.XPATH, '//*[@id="login_box"]/div[2]/div[1]/form/div[1]/div[3]/div/span[2]') logging.info("Click the Agree option.\r") @@ -330,7 +339,7 @@ def _login(self, driver): # get base64 image data im_info = driver.execute_script(background_JS) background = im_info.split(',')[1] - background_image = base64_to_cv2(background) + background_image = base64_to_PLI(background) logging.info(f"Get electricity canvas image successfully.\r") distance = self.onnx.get_distance(background_image) logging.info(f"Image CaptCHA distance is {distance}.\r") diff --git a/scripts/onnx.py b/scripts/onnx.py index dec69ea..6b8a778 100644 --- a/scripts/onnx.py +++ b/scripts/onnx.py @@ -1,4 +1,5 @@ -import cv2 +# import cv2 +from PIL import ImageDraw,Image,ImageOps import numpy as np import onnxruntime import time @@ -100,12 +101,17 @@ def draw(self,image, box_data): top, left, right, bottom = box # print('class: {}, score: {}'.format(CLASSES[cl], score)) # print('box coordinate left,top,right,down: [{}, {}, {}, {}]'.format(top, left, right, bottom)) - image = cv2.rectangle(image, (top, left), (right, bottom), (0, 0, 255), 1) + # image = cv2.rectangle(image, (top, left), (right, bottom), (0, 0, 255), 1) + draw = ImageDraw.Draw(image) + draw.rectangle([(top, left), (right, bottom)], outline ="red") # cv2.imwrite("result"+str(left)+".jpg",image) - image = cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score), - (top, left), - cv2.FONT_HERSHEY_SIMPLEX, - 0.6, (0, 0, 255), 2) + # font = ImageFont.truetype(font='PingFang.ttc', size=40) + draw.text(xy=(top, left),text='{0} {1:.2f}'.format(CLASSES[cl], score), fill=(255, 0, 0)) + + # image = cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score), + # (top, left), + # cv2.FONT_HERSHEY_SIMPLEX, + # 0.6, (0, 0, 255), 2) return image # 获取预测框 @@ -187,17 +193,21 @@ def letterbox(self, img, new_shape=(640, 640), color=(114, 114, 114), auto=False dh /= 2 if shape[::-1] != new_unpad: # resize - img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) - + # img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) + img = img.resize(new_unpad) top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) - img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + # img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + img = ImageOps.expand(img, border=(left, top, right, bottom), fill=0)##left,top,right,bottom return img, ratio, (dw, dh) def _inference(self,image): - org_img = cv2.resize(image, [416, 416]) # resize后的原图 (640, 640, 3) - img = cv2.cvtColor(org_img, cv2.COLOR_BGR2RGB).transpose(2, 0, 1) + # org_img = cv2.resize(image, [416, 416]) # resize后的原图 (640, 640, 3) + org_img = image.resize((416,416)) + # img = cv2.cvtColor(org_img, cv2.COLOR_BGR2RGB).transpose(2, 0, 1) + img = org_img.convert("RGB") + img = np.array(img).transpose(2, 0, 1) img = img.astype(dtype=np.float32) # onnx模型的类型是type: float32[ , , , ] img /= 255.0 img = np.expand_dims(img, axis=0) # [3, 640, 640]扩展为[1, 3, 640, 640] @@ -216,12 +226,14 @@ def get_distance(self,image,draw=False): if draw: org_img = self.draw(org_img, boxes) # cv2.imshow('result', org_img) - cv2.imwrite('result.png', org_img) + # cv2.imwrite('result.png', org_img) + org_img.save('result.png') # cv2.waitKey(0) return int(boxes[..., :4].astype(np.int32)[0][0]) if __name__ == "__main__": onnx = ONNX() - img_path="background0.png" - img = cv2.imread(img_path) + img_path="../assets/background.png" + # img = cv2.imread(img_path) + img = Image.open(img_path) print(onnx.get_distance(img,True)) \ No newline at end of file